/* ; File : $Workfile: BATCH.C$ ; ; Description : ; ; Original Author : DIGITAL RESEARCH ; ; Last Edited By : $CALDERA$ ; ;-----------------------------------------------------------------------; ; Copyright Work of Caldera, Inc. All Rights Reserved. ; ; THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL, ; PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC. ; ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES ; WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF ; THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO ; HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE ; AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE ; AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED, ; COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED, ; CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST, ; TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF ; CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT ; AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND ; CIVIL LIABILITY. ;-----------------------------------------------------------------------; ; ; *** Current Edit History *** ; *** End of Current Edit History *** ; ; $Log$ ; ENDLOG */ /* * 27 Oct 87 Improve GOTO command to ignore trailing white space, only * match on the first 8 characters and only allow valid * filename characters. * 28 Oct 87 Correct duplicate prompt display when a batch file label * is read and the echo flag is ON. * 9 Nov 87 Change Batch file termination so that Output Redirection is * correctly handled. * 13 Jan 88 If prompt display is aborted because of a critical error * the following prompt will be forced to "$n$g". * 25 Jan 88 Support redirection on the FOR command correctly * 24 Feb 88 Generate batch file parameter %0 correctly as WS200 install * requires that the drive specifier be present. Garry Silvey * 5 May 88 Batch paramater %0 is now a copied from the invoking command * line. * 18 May 88 Support the ESC_CHAR in the command line. * 20 May 88 Disable MULTI_CHAR support in DOSPLUS when the user enters the * first command. * 26 May 88 Added STACK switch and support $q in prompt. * 27 May 88 Added string undefs. * 6 Jun 88 Call resident portion to do readline (SideKick+ problem) * 23 Jun 88 Support CR only delimited batch files and if errorlevel == * syntax used in installation files. * 6 Jul 88 Support the FOR ... CALL syntax correctly. (IMS) * 17 Aug 88 Jump to labels followed by comments (Ashton Tate By-Line) * 21 Sep 88 Use KEYIN_FLG to allow the default ECHO state to be ON. * 16 Nov 88 Disable BACK_CHAR in Concurrent DOS. * 21 Dec 88 Allow leading whitespace before labels * 5 Jan 89 Support Quoted strings in Batch files. * 18 Apr 89 Support Quoted strings in IF command * 19 Apr 89 Quotes in IF command: "x == "x" parses, "x=x" == "x.. doesn't * 24 Apr 89 Increase MAX_LINE to 128 for pctools ver 5 * 19 May 89 Take out support for quoted strings in IF command * 05 Jun 89 Do not echo command or display the prompt during FOR command. * Restore support for batch files with long lines. * 02 Nov 89 batch_line bodge which substitutes 0xFF for 0x00 changed - we * now substitute '\r\n', then throw away rest of the line. * 01 Dec 89 batch_line - trailing % at end of line is discarded * 15 Dec 89 "if errorlevel -1" allowed * 30 Jan 90 Added int2e_start and int2e_finish to save and restore * important batch file variables to allow novell menu program * to use int 2e to invoke batch files. * 30 Jan 90 Forced batch data structures to appear on segment boundaries; * Added dummy memory descriptor before batch structure; * Put segment address of batch structure in batch_seg_ptr; * All so novell can find and patch the drive letter of * autoexec.bat during a remote boot. * 6-Mar-90 Watcom C v 7.0 * 7-Mar-90 allow ESC_CHAR through unless followed by MULTI/BACK_CHAR * 14-Mar-90 Reduce batch_buf to 32 bytes like wot dos is * 20-Mar-90 Batch structures allocated by mem_alloc (ie. MS_M_ALLOC) * rather than on heap. Batch file nesting no longer heap limited. * 27-Mar-90 Allow "=", "==", "===" etc in "if errorlevel==n" * 10-Apr-90 Make errorlevel 999 same as errorlevel 231 (mod 256) * 8-May-90 Don't echo getcmd unless batch file (eg. "dir|more" shouldn't * echo "C:>more") * 23-May-90 batch_read no longer repeatedly deblanks line (which leads * to the buffer happily wandering up memory). * 30-May-90 "if ab de==ef" form doesn't generate syntax error * 13-Jun-90 batch_line rejects unmatched "|" as syntax error * 20-Sep-90 is_filechar() and is_pathchar() now take pointer instead of byte * Changed batch_char() to return pointer instead of byte, * renamed to batch_ptr(). * Amended make_label(), batch_start() and cmd_for() to check for * DBCS lead bytes. * 24-Sep-90 Add $m and $u option to PROMPT to display status of mail and user name respectively * 27-Sep-90 Add IF USERID COMMAND IF LOGINNAME COMMAND IF GROUPNAME COMMAND IF ASK ["string"] COMMAND .. AND .. OR .. to IF processing INPUT ["string"] INPUTC ["string"] * 10-Oct-90 inherit batch files from TMP when TSR auto-loads CDOS.COM (a bodge for Stellar) * 25-Oct-90 inherit echoflg from TMP when TSR auto-loads CDOS.COM * 31-Oct-90 change IF ASK command to IF KEY command DRDOS BUXTON ------------ * 25-Apr-91 '!' is now ignored during COMMAND /C processing. * 28-May-91 leading white space is now ignored before labels. * 09-Jun-91 Added GOSUB, RETURN and SWITCH commands. * 24-Jul-91 if batch_seg_ptr is poked to zero all batch processing is terminated. * 26-Jul-91 batch files are now read into a far buffer. This avoids reading directly to seg FFFF if we happen to be there and so solves some NOVELL problems. * 18-Jun-92 Support ? in batch files as in config.sys. * 23-Jun-92 $u in prompt causes LOGINNAME in environment to be displayed. * 07-Jul-92 IF EXIST now finds hidden files. */ #include "defines.h" #include #if defined(MWC) && defined(strlen) #undef strcmp /* These are defined as macros in string.h */ #undef strcpy /* which are expanded in line under */ #undef strlen /* Metaware C. These undefs avoid this. */ #endif #include #if !defined(DOSPLUS) #include #endif #include "command.h" #include "toupper.h" #include "support.h" #include "dosif.h" #include "global.h" #include "dos.h" #include /*RG-01*/ #if defined(CDOSTMP) || defined(CDOS) #include #define PATH_LEN 65 /* max path length (null terminated) */ EXTERN PD FAR * CDECL pd; /* Far pointer to Current PD */ EXTERN VOID CDECL cmd_set(BYTE *); /* COMINT.C */ #if !defined (NOSECURITY) #include "security.h" #include "login.h" #endif #endif /*RG-01-end*/ EXTERN VOID CDECL cmd_pause(); EXTERN BOOLEAN parse(BYTE *); EXTERN UWORD boot_key_scan_code; /* in COM.C */ /*RG-03*/ BOOLEAN if_context=FALSE; BOOLEAN ifcond=FALSE; /*RG-03-end*/ EXTERN jmp_buf break_env; #define MAX_LINE 128 /* Maximum No of Chars in input line */ #if defined(CPM) EXTERN UWORD user; /* USER Number variable for CPM.EXE */ #endif EXTERN BYTE msg_prmeq[]; /* Static Environ String "PROMPT=" */ #if 0 #define FCONTROL struct fcc MLOCAL FCONTROL { BOOLEAN sflg; /* FOR File Search Flag */ DTA search; /* FOR Search structure */ BYTE *files; /* FOR File list */ BYTE *cmd; /* FOR Command Line */ BYTE forvar; /* FOR variable char */ }; #endif MLOCAL FCONTROL *forptr; #if 0 #define BCONTROL struct bcc GLOBAL BCONTROL { BCONTROL FAR *bcontrol; /* Previous Batch Control Structure */ BOOLEAN eof; /* End of File Flag */ LONG offset; /* Offset in BATCH file */ LONG ret_offset[4]; /* return offset from gosub */ BYTE *batcmd; /* Batch File Input parameters */ UWORD batshift; /* Shift Offset */ BYTE batfile[MAX_PATHLEN]; /* Batch File Name */ UWORD stream; /* Stream for this Batch File */ FCONTROL *fcontrol; /* Pointer to previous FOR command */ BYTE *heap_start; /* Heap pointer before extra bytes */ WORD heap_size; /* are added to shift to segment */ BYTE save_area[1]; /* boundary. - EJH */ } FAR *batch, FAR *batch_save; /* Master Batch Control Stucture */ #endif /* Handle 255 is closed */ #define CLOSED 0xff /* Keyboard Variables */ GLOBAL BYTE kbdbuf[MAX_LINE+2]= {0};/* Keyboard Input Buffer */ GLOBAL BYTE *kbdptr = kbdbuf+2; /* Keyboard Buffer Pointer */ MLOCAL BOOLEAN keyin_flg = FALSE; /* This flag is set to TRUE when the */ /* initial command line buffer setup */ /* by INIT() has been exhausted. */ MLOCAL WORD batchflg_save; /* Used during INT 2E handling. */ MLOCAL WORD echoflg_save; /* ditto above */ GLOBAL WORD echoflg_save2; /* saves echo state when batch file */ /* execed. */ /* * Batch file buffering control structures. */ MLOCAL LONG batch_off; /* Offset of buffered data in File */ MLOCAL WORD batch_cnt = 0; /* Number of bytes in buffer */ MLOCAL BYTE batch_buf[32]; MLOCAL BYTE batch_eof[] = "\x1a"; /* End of file string */ MLOCAL BYTE batch_sep[] = "\t ;,="; /* Batch Command line option delimiters */ EXTERN VOID CDECL cmd_ver(BYTE *); /* COMINT.C Display Version */ EXTERN VOID docmd(BYTE *, BOOLEAN); /* COM.C */ EXTERN VOID CDECL int_break(VOID); /* COM.C */ GLOBAL BOOLEAN getcmd(BYTE *); GLOBAL VOID for_end(); GLOBAL VOID batch_start(BYTE *, BYTE *, BYTE *); GLOBAL VOID batch_end(VOID); GLOBAL VOID batch_close(VOID); MLOCAL VOID for_in(BYTE *); MLOCAL WORD batch_open(VOID); MLOCAL VOID batch_read(BYTE *, BOOLEAN); MLOCAL VOID batch_line(BYTE *, BOOLEAN); MLOCAL BYTE *batch_ptr(VOID); MLOCAL VOID prompt(VOID); MLOCAL BOOLEAN novell_extension(BYTE *, BYTE *); #if !defined(CDOSTMP) EXTERN UWORD FAR *batch_seg_ptr; /* For novell remote boot. see CSTART.ASM */ #endif #if defined(CDOS) || defined(CDOSTMP) #define TmpPspEchoFlgPtr 0x58 /* Magic location to keep echo flag */ #define TmpPspDataSeg 0x5a /* Magic location to keep Data Seg */ #define TmpPspBatchSeg 0x5c /* Magic location to keep Batch Ptr */ #define TmpPspMpad 0x5e /* Magic location to keep unlinked MPAD */ #endif #if defined(CDOS) typedef struct _mpad { UWORD link; /* address of next MPAD */ UWORD start; /* seg of allocation unit */ UWORD length; /* length in paras */ UWORD res0x06; /* reserved */ UWORD xios; /* Process id */ } MPAD; GLOBAL VOID inherit_TMP_state(VOID); #endif /*.pa*/ GLOBAL BOOLEAN getcmd(line) /* read command line */ BYTE *line; { BYTE *s; BOOLEAN quote = FALSE; BOOLEAN cancel_prompt = FALSE; #if defined(DOSPLUS) WORD i; BYTE cmd_name[16]; #endif back_flag = FALSE; /* Disable BackGround Processing*/ *line = '\0'; FOREVER { if(for_flag) { /* If Processing a FOR command */ for_in(line); /* then call then use the FOR_IN*/ return NO; /* routine to fill the keyboard */ } /* buffer and then return */ if(!batchflg) /* If no batch processing then */ break; /* skip further tests */ batch_restart: #if defined(DOSPLUS) if (!*batch_seg_ptr) { /* if batch_seg_ptr has been set to */ batch_endall(); /* zero terminate all batch files */ return NO; } #endif if(batchflg && batch->eof) { /* Close the batch file if at */ batch_end(); /* the end of the file. */ if(batchflg == 0) /* BREAK if batch processing */ return NO; /* is complete. */ continue; } if(!batch_open()) /* Open the file and read a line*/ return YES; /* from the file */ batch_read(line, NO); /* Read Line */ if(batch->eof) /* If the end of the batch file */ batch_close(); /* has been detected then close */ /* the file. */ if (*line == '?' || boot_key_scan_code == 0x4200) { optional_line(line); } if(*line == '@') { /* If first character in the */ strcpy(line, line+1); /* command line is '@' donot */ return NO; /* echo the command and move the*/ } /* string down 1 character. */ if (!cancel_prompt) { if(crlfflg && echoflg) crlf(); prompt(); } return echoflg; } if(!*kbdptr) { /* Set the Keyboard Input flag */ keyin_flg = TRUE; /* after a initial command line */ /* buffer has been exhausted */ #if 1 if (c_option) { /* insert an EXIT command if we */ kbdptr = &kbdbuf[2]; /* are processing a /C command */ strcpy(&kbdbuf[2],"exit"); } #endif } if (!*kbdptr) { /* Check for existing line */ /* NEIL */ if(crlfflg && echoflg) crlf(); /* NEIL end */ prompt(); /* issue command line prompt */ allow_pexec = FALSE; /* $x in prompt string may cause batchflg to be set. */ /* If so we must jump to batch processing code. */ if (batchflg) { cancel_prompt = TRUE; goto batch_restart; } kbdptr = ""; /* Force KBDPTR to point to '\0'*/ /* in case we get ABORTED and */ /* drop through here again. */ kbdbuf[0] = MAX_LINE; /* set max. input length */ kbdbuf[kbdbuf[1]+2] = '\r'; /* Terminate current line. */ #if defined(CDOSTMP) system(C_READSTR, kbdbuf); /* read a line */ #else readline(kbdbuf); #endif crlf(); kbdbuf[kbdbuf[1] + 2]='\0'; /* terminate input */ kbdptr = kbdbuf + 2; } s = kbdptr; while(*s) { if(*s == '"') /* Check for a " character and */ quote = !quote; /* update the flag correctly */ #if !defined(DOSPLUS) if(*s == ESC_CHAR && /* If the Escape character has */ !quote && /* been specified then do not */ ((*(s+1) == MULTI_CHAR) || (*(s+1) == BACK_CHAR))) { *line++ = *++s; /* process the following char. */ s++; continue; } #endif #if defined(DOSPLUS) /* Disable MULTI_CHAR support */ if(!(keyin_flg||c_option||k_option)) /* after the init command line */ #endif /* has been exhausted. */ if(*s == MULTI_CHAR && /* If a Multiple command char */ !quote) { /* and the QUOTE flag is FALSE */ s++; /* then break the command here */ break; /* and save the rest of the line*/ } /* for next time. */ #if FALSE /* defined(CDOSTMP) */ if(*s == BACK_CHAR && /* If a Back Ground processing */ !quote) { /* and the QUOTE flag is FALSE */ s++; /* then treat as for MULTI_CHAR */ back_flag = TRUE; /* except that the current */ break; /* command is executed in the */ } /* background. */ #endif if(*s == PIPE_CHAR && /* If a Pipe enable character */ !quote) { /* and the QUOTE flag is FALSE */ s++; /* then break the command here */ pipe_out = YES; /* and save the rest of the line*/ break; /* for next time. */ } copy_char(&line, &s); /* Just save the character */ } *line = '\0'; /* Terminate the Buffer */ kbdptr = deblank(s); /* Copy the possibly null length*/ return NO; /* string to KBDBUF for next */ /* next invocation and save CCP */ } MLOCAL VOID for_in(line) /* A FOR command is currently */ BYTE *line; /* executing so build the line */ { /* from the internal FOR data */ BYTE *s,*t; /* initialized by CMD_FOR */ BYTE *bp1, *fp; WORD i; FOREVER { fp = forptr->files; /* Get the next string and stop */ if(strlen(fp) == 0) { /* if its the zero length string */ *line = '\0'; /* which marks the end of the FOR */ crlfflg = YES; /* search list. */ for_end(); return; } if(!iswild(fp)) { /* If not an ambiguous file */ forptr->sflg = NO; /* then update the FOR */ forptr->files += strlen(fp)+1; /* pointer to the next file */ break; /* in the search list. */ } if(forptr->sflg) /* Search for the next file */ i = ms_x_next(&forptr->search); /* file on the disk if */ else /* FOR_SFLG otherwise get first */ i = ms_x_first(fp, ATTR_RO, &forptr->search); if(i < 0) { /* If the search failed */ forptr->sflg = NO; /* then update the FOR */ forptr->files += strlen(fp)+1; /* pointer to the next file */ continue; /* in the search list. */ } /* and get the next entry */ fp = (BYTE *) heap(); strip_path(forptr->files, fp); /* Extract the Path */ strcat(fp, forptr->search.fname); /* and then add the matching */ forptr->sflg = YES; /* filename and update the */ strupr(fp); /* variables. */ break; /* Force name to uppercase */ } s = forptr->cmd; t = line; while (*s && (t - line) < MAX_LINE) { /* Copy the command */ if(*s == '%' && *(s+1) == forptr->forvar) { /* line looking for */ s += 2; /* the imbedded %c and insert*/ bp1 = fp; /* the current substition */ while(*bp1 && (t - line) < MAX_LINE) /* string pointed at by FP */ copy_char(&t, &bp1); continue; } copy_char(&t, &s); } *t = '\0'; /* and terminate the string */ } /*.pa*/ /* * BATCH FILE CONTROL ROUTINES * =========================== * * The following routines provide the interface from COMMAND.COM to * a batch file. BATCH_START sets up all the local variables to enable * batch processing while BATCH_END terminates batch processing. BATCH_READ * reads a line of data from the batch file and expands it to contain the * command line variables and variables from the environment. */ GLOBAL VOID batch_start(argv0, path, tail) BYTE *argv0; /* Invoking Command */ BYTE *path; /* Complete filename */ BYTE *tail; /* Command Line Options */ { BYTE *s2; BYTE dirbuf[MAX_PATHLEN]; WORD i; if(batchflg) /* If a batch file is currently */ batch_close(); /* close it. So minimum number */ /* of handles are used. */ s2 = path; /* Save the original Path */ if((path = d_check(path)) == NULL) /* Check that the file */ return; /* exists. */ batch_new(); /* new incarnation of batch */ forptr = (FCONTROL *) NULL; /* Disable current FOR control */ for_flag = NO; /* and Global FOR flag */ /* * Generate the full path specification for the batch file * and store in the batch control information. If the user * has specified the full path use it otherwise determine the * full path using ms_x_curdir. */ if (ddrive != -1 && *path != *pathchar) { ms_x_curdir(ddrive+1, dirbuf); sprintf(heap(), "%c:%s%s%s%s", ddrive + 'A', pathchar, dirbuf, (*dirbuf ? pathchar : ""), path); } else if (ddrive != -1) sprintf(heap(), "%c:%s", ddrive + 'A', path); else ms_x_expand(heap(), path); for (i=0; ibatfile[i] = heap()[i]; batch->batfile[MAX_PATHLEN-1] = 0; /* * Copy the invoking command and the individual elements * of the command line into a buffer ready for processing */ batch->batcmd = (BYTE *)heap(); /* Initialize Parameter Buffer */ strcpy(heap(), argv0); /* Copy the invoking command */ heap_get(strlen(heap())+1); /* and protect the buffer */ while(*tail) { /* While there are command line */ s2 = (BYTE *)heap(); /* parameters copy them */ while(*tail && strchr(batch_sep, *tail)) tail = skip_char(tail); while(*tail && !strchr(batch_sep, *tail)) copy_char(&s2, &tail); *s2++ = '\0'; heap_get(strlen(heap()) + 1); } *(WORD *)heap_get(2) = 0; /* Double NULL is a terminator */ /* for command line params */ if(in_flag & REDIR_ACTIVE) /* If Input redirection has been */ in_flag |= REDIR_BATCH; /* enabled for this command force*/ /* it on for the complete command*/ if(out_flag & REDIR_ACTIVE) /* If Output redirection has been*/ out_flag |= REDIR_BATCH; /* enabled for this command force*/ /* it on for the complete command*/ batchflg++; /* increment batch flag */ crlfflg = YES; /* print CR/LF after this */ } GLOBAL VOID batch_endall() /* This terminates BATCH */ { /* processing by closing ALL */ while(batchflg) { /* active batch files */ batch_end(); } } GLOBAL VOID batch_end() /* This function is called for */ { /* both NORMAL and ABNORMAL */ if(batchflg == 0) /* termination of batch file */ return; /* processing */ boot_key_scan_code = 0; batch_close(); /* Close the Batch file */ for_end(); /* Terminate Any FOR command */ batch_old(); /* Restore the previous batch */ /* control structures to heap */ if(--batchflg == 0) { *batch_seg_ptr = 0; echoflg = echoflg_save2; /* Restore the original ECHO */ crlfflg = YES; /* flag and set CR/LF flag when */ } /* returning to the keyboard. */ } MLOCAL BOOLEAN batch_open() { WORD h, i; BYTE *name; if(batch->eof) { /* If the End of the batch file */ batch_end(); /* was discovered last time then*/ return FALSE; /* End Batch file input and exit*/ } if(batch->stream != CLOSED) return batch->stream; name = heap(); for (i=0; ibatfile[i]; while ((h = ms_x_open(name, OPEN_READ)) < 0) { err_flag = TRUE; eprintf(MSG_BATMISS, name); /* prompt for batch file */ heap_get(strlen(name)+1); cmd_pause(""); heap_set(name); } err_flag = FALSE; batch->stream = h; return TRUE; } GLOBAL VOID batch_close() { if(batchflg != 0 && batch->stream != CLOSED) { /* Check if the batch file */ batch_cnt = 0; /* currently open if YES */ ms_x_close(batch->stream); /* then flush the internal */ batch->stream = CLOSED; /* buffer and close file. */ } } #if defined(DOSPLUS) GLOBAL VOID inherit_batch_file(bc) BCONTROL FAR *bc; { WORD i; BYTE FAR *p_heap; BYTE *l_heap; /* inherit any parent batch file first */ if (bc->bcontrol) inherit_batch_file(bc->bcontrol); /* create a new batch structure */ batch_new(); batch->offset = bc->offset; /* continue at same offset */ for (i=0;i<4;i++) batch->ret_offset[i] = bc->ret_offset[i]; batch->batshift = bc->batshift; for (i=0; ibatfile[i] = bc->batfile[i]; batch->batfile[MAX_PATHLEN-1] = 0; /* get command line */ p_heap = MK_FP(*parent_psp+16,bc->batcmd); l_heap = heap(); while (1) { while(*p_heap) *l_heap++ = *p_heap++; *l_heap++ = *p_heap++; if (*p_heap == 0) { *l_heap = 0; break; } } heap_get(l_heap-heap()); batchflg++; } GLOBAL VOID inherit_parent_state() { UWORD FAR *p; BCONTROL FAR *bc; UWORD root_psp; root_psp = *parent_psp; while(1) { p = MK_FP(root_psp-1,8); if (p[0] == 0x4F43 && p[1] == 0x4D4D && p[2] == 0x4E41 && p[3] == 0x0044) break; p = MK_FP(root_psp,0x16); root_psp = *p; } p = MK_FP(root_psp+16,batch_seg_ptr); #if 0 printf("batch_seg_ptr = %04X:%04X\n",p); printf("parent batch_seg_ptr = %04X\n",*p); #endif if (*p == 0 || *p == 0xFFFF) return; bc = MK_FP(*p,0); inherit_batch_file(bc); *p = 0; p = MK_FP(root_psp+16,&echoflg); echoflg = *p; } #endif GLOBAL VOID batch_new() /* save current batch file heap contexts to high memory */ { BYTE *hp_start; WORD hp_size; UWORD i; BCONTROL FAR *bc; #if defined(CDOSTMP) UWORD FAR *ptr; #endif if (batchflg != 0) hp_start = batch->heap_start; else hp_start = heap(); hp_size = heap() - hp_start; i = (sizeof(BCONTROL) + hp_size + 15)/16; mem_alloc(&bc, &i, i, i); /* allocate new batch structure */ if (i == 0) { /* if we can't allocate one */ longjmp(break_env, IA_HEAP);/* then pretend heap has run out*/ } /* to force termination. */ bc->bcontrol = batch; /* Link to Previous Structure */ batch = bc; /* make this current batch struc*/ batch->eof = NO; /* Have not found the EOF yet */ batch->offset = 0L; /* start at beginning of File */ for (i=0;i<4;i++) batch->ret_offset[i] = 0L; batch->batshift = 0; /* No Shift Factor */ batch->stream = CLOSED; /* Batch file is not open */ batch->fcontrol = forptr; /* Save current FOR control */ batch->heap_start = hp_start; /* Save original heap */ batch->heap_size = hp_size; for (i=0; i < hp_size; i++) { batch->save_area[i] = hp_start[i]; } heap_set(hp_start); /* free up heap used by old batch */ #if defined(CDOSTMP) ptr = MK_FP(pd->P_PSP, TmpPspEchoFlgPtr); *ptr = (UWORD)&echoflg; ptr = (UWORD FAR *) &ptr; /* coerce a FAR * to local data */ i = FP_SEG(ptr); /* so we can get local data segment */ ptr = MK_FP(pd->P_PSP, TmpPspDataSeg); *ptr = i; /* save local data segment */ ptr = MK_FP(pd->P_PSP, TmpPspBatchSeg); *ptr = (UWORD)(((ULONG)batch) >> 16); #else /* Get segment address of batch and put it where novell */ /* can find it. */ *batch_seg_ptr = (UWORD)(((ULONG)batch) >> 16); #endif } MLOCAL VOID batch_old() /* restore current batch file heap contents from high memory */ { BCONTROL FAR *bc; UWORD i; #if defined(CDOSTMP) UWORD FAR *ptr; #endif heap_set(batch->heap_start+batch->heap_size); for (i=0; iheap_size; i++) { batch->heap_start[i] = batch->save_area[i]; } bc = batch; forptr = batch->fcontrol; /* Restore the previous for */ for_flag = (BOOLEAN) forptr; /* control structures */ batch = batch->bcontrol; /* restore ptr to previous batch */ mem_free(&bc); /* free up batch memory */ #if defined(CDOSTMP) ptr = MK_FP(pd->P_PSP, TmpPspBatchSeg); *ptr = (UWORD)(((ULONG)batch) >> 16); #endif } /* * Read lines repeatedly from the batch file until a line in * the correct format is read from the batch file or the EOF * has been reached. */ MLOCAL VOID batch_read(line, goto_flg) BYTE *line; /* Command Line Buffer */ BOOLEAN goto_flg; /* Goto Command Flag */ { BYTE *l; /* we need to deblank line */ do { batch_line(line, goto_flg); /* Read the next line from */ l = deblank(line); /* the batch file and return*/ if(*l != ';') { if(goto_flg && *l == ':') /* a line in the correct */ return; /* format. */ if(!goto_flg && *l != ':') return; } } while(!batch->eof); } /* * Read one line from the batch file and place the expanded data into * the buffer LINE. */ MLOCAL VOID batch_line(line, goto_flg) BYTE *line; /* Command Line Buffer */ BOOLEAN goto_flg; /* Goto Command Flag */ { REG WORD i; REG BYTE *s; WORD n, env_start; BYTE c, *bp; BYTE env_str[128]; BOOLEAN quote = FALSE; LONG old_offset; int j; env_start = NULL; /* Copy the environment into a */ #if 0 /* 'eject any line starting with 'rem' */ old_offset = batch->offset; i=0; do{ switch(c=*batch_ptr()){ case 0x1a: batch->eof = YES; /* We have come to the end */ c = '\r'; /* of the batch file so set */ break; /* flag and mark end of line*/ default: if (i < MAX_LINE) line[i++] = c; if (dbcs_lead(c)) { if ((c = *batch_ptr()) >= ' ' && i < MAX_LINE) line[i++] = c; } }/*switch*/ }while(c!='\r'); if (*batch_ptr() != '\n') batch->offset--; line[i]=0; j=0; while(line[j]){ if (line[j] != ' ') break; j++; } if (( strlwr(line[j]) == 'r') && ( strlwr(line[j+1]) == 'e') && ( strlwr(line[j+2]) == 'm') && ( strlwr(line[j+3]) == ' ')){ if (echoflg) printf("%s\n",line); line[0]='\0'; return; } batch->offset = old_offset; batch->eof = NO; #endif /*rbf-end*/ /* process line */ i = 0; do { switch(c = *batch_ptr()) { case '\0': /* In OS/2 install.bat file */ #if 0 if (i < MAX_LINE) /* "ECHO \0" displays blank */ line[i++] = '\r'; /* line - so we insert a CR */ while (c != '\r') { /* then swallow rest of line */ c = *batch_ptr(); /* read next character - if */ if (c == 0x1a) /* it's an EOF mark then use */ goto end_of_file; /* end-of-file code else end */ } /* up falling into '\r' code */ #else c = *batch_ptr(); if ((c == '\r') && (i < MAX_LINE)) line[i++] = '\r'; #endif case '\r': /* carriage return */ if(*batch_ptr() != '\n') /* skip line feed */ batch->offset--; /* if present */ break; case '"': /* Support Quoted strings */ quote = !quote; /* in batch files. */ goto save_it; case PIPE_CHAR: /* Handle Piped Output */ if(goto_flg || quote) /* Ignore this character if */ goto save_it; /* we are searching for a */ /* Label or Quote. */ line[i] = '\0'; c = *deblank(line); /* normal case we just */ if ((c !='\0') && (c != ':') && following_command()) { c = '\r'; /* simulate a CR and set */ pipe_out = YES; /* Pipe Output flag. */ } else if (c == ':') { /* if it's a label */ for(;(c != '\r') && (c != 0x1A); c = *batch_ptr()) if (c == 0x1A) /* eat rest of the line */ batch->eof = YES; if(*batch_ptr() != '\n')/* skip line feed */ batch->offset--; /* if present */ c = '\r'; } else { /* if it's a syntax error */ swallow_line(line); /* discard the rest of line */ i = 0; /* start again with new line */ } break; case '%': /* The following code checks to see if the */ /* string starting at line[env_start-1] is */ /* define in the environment if it is then */ /* its definition replaces it in the input */ /* line. Otherwise no change is made. */ if(env_start) { env_start--; line[i] = '\0'; /* Terminate Input */ strcpy(env_str, line+env_start);/* Copy the String */ strupr(env_str); /* and force string */ bp = (BYTE *)heap(); /* into Uppercase */ i = env_start; env_start = NULL; strcat(env_str,"="); if (env_scan(env_str,bp)) { if (novell_extension(env_str,bp)) break; } while(*bp && i < MAX_LINE-1) line[i++] = *bp++; break; } c = *batch_ptr(); if (c == '\r') { batch->offset--; /* rewind to point to '\r' */ break; /* then break to normal code */ } if (c < '0' || c > '9') { /* if not a parameter */ if(c != '%') /* or a '%' character */ env_start = i+1; /* save its start address in */ goto save_it; /* the string and wait for */ } /* the terminating '%' */ n = c - '0' + batch->batshift; /* get parameter # 0-9 and */ /* add in SHIFT offset */ s = batch->batcmd; while(n-- && strlen(s)) /* skip all other parameters */ s += strlen(s)+1; /* before the one we want */ if((strlen(s) + i) >= MAX_LINE) /* Break if Greater than MAX_LINE*/ break; strcpy (line + i, s); /* get the substitution */ i += strlen (s); /* add in its size */ break; case 0x1a: end_of_file: batch->eof = YES; /* We have come to the end */ c = '\r'; /* of the batch file so set */ break; /* flag and mark end of line*/ default: save_it: if (i < MAX_LINE) line[i++] = c; if (dbcs_lead(c)) { if ((c = *batch_ptr()) >= ' ' && i < MAX_LINE) line[i++] = c; } } } while (c != '\r'); /* repeat until CR */ line[i] = '\0'; /* Terminate the line and */ if(batch->eof) return; #if 0 /* not DOS compatible */ if(*batch_ptr() == 0x1A) /* Check if the next this */ batch->eof = YES; /* the end of the file if */ else /* YES then set the flag */ batch->offset--; /* force the character to */ #endif /* be re-read next time */ return; /* return to the caller */ } MLOCAL BOOLEAN following_command() /* return true if we have a possible command on the rest of the line */ { LONG old_offset; BOOLEAN res = FALSE; BYTE *s; old_offset = batch->offset; /* save batch offset */ while (TRUE) { s = batch_ptr(); /* look ahead at batch file */ if (*s == '\r' || *s == 0x1a || (!dbcs_lead(*s) && *s == PIPE_CHAR)) break; if (!is_blank(s)) { res = TRUE; /* possible command if we */ break; /* hit non whitespace char */ } if (dbcs_lead(*s)) { s = batch_ptr(); if (*s == '\r' || *s == 0x1a) break; } } batch->offset = old_offset; /* restore batch offset */ return res; } MLOCAL VOID swallow_line(s) BYTE *s; /* there is a syntax error on this line - swallow it and say so */ { BYTE c; prompt(); /* possibly echo the prompt */ if (echoflg) /* echo to screen if wanted */ printf("%s%c",s,PIPE_CHAR); do { c = *batch_ptr(); if (c == 0x1a) { c = '\r'; /* pretend to be end of line*/ batch->eof = YES; /* We have come to the end */ break; /* flag and mark end of line*/ } if (echoflg) /* echo to screen if wanted */ putc(c); } while (c != '\r'); if (echoflg) putc('\n'); if (*batch_ptr() != '\n') /* skip line feed */ batch->offset--; /* if present */ eprintf(MSG_SYNTAX); /* report syntax error */ } /* * In order to improve performance of the batch file processing * the Batch file is read in blocks of BATCH_BUF characters. * and the routine BATCH_CHAR then returns a pointer to a character * from the buffer (filling the buffer if required). */ MLOCAL BYTE *batch_ptr() { BYTE FAR *buf; UWORD bufsize; UWORD i; if(batch->eof) return(batch_eof); if (batch->offset < batch_off || batch->offset >= (batch_off + (LONG) (batch_cnt - 1))) { batch_off = batch->offset; ms_x_lseek (batch->stream, batch->offset, 0); batch_cnt = far_read(batch->stream, gp_far_buff, sizeof(batch_buf)); if(batch_cnt <= 0) { batch->eof = YES; return(batch_eof); } for (i=0; ioffset++ - batch_off)]); } /*.pa*/ /* * BATCH FILE COMMANDS * =================== * * The following commands are used almost entirely in BATCH files and * have little or no meaning outside that environment. */ GLOBAL VOID CDECL cmd_shift () { batch->batshift++; /* Increment the Shift Offset */ } MLOCAL WORD label_ignore_char(s) BYTE *s; { if (*s == '=') return(1); if (*s == ';') return(1); if (*s == ',') return(1); if (*s == ' ') return(1); return(0); } /* * Extract the first a valid characters from the label and then * zero terminate the resulting string. */ MLOCAL BYTE * make_label(label) BYTE *label; { REG BYTE *bp; UWORD i; label = deblank(label); /* remove leading white space */ while (label_ignore_char(label)) label = skip_char(label); bp = label; while (is_filechar(bp)) /* skip over valid chars */ bp = skip_char(bp); *bp = '\0'; /* make label zero terminated */ return label; } GLOBAL VOID CDECL cmd_goto (label) /* goto label in batch file */ REG BYTE *label; { UWORD i; BYTE *bp, s[MAX_LINE+2]; /* Allocate buffer for Batch Input */ if (!batchflg) /* if not in batch mode */ return; /* this command is ignored */ if(*label == ':') /* Ignore any leading ':' */ label++; label = make_label(label); /* Convert to Label Format */ batch->offset = 0L; /* rewind the batch file */ batch->eof = NO; /* So it cannot be EOF */ if(!batch_open()) /* Check the Batch file is open */ return; /* and stop if the function fails. */ while(!batch->eof) { /* while not end of file read next */ batch_read(s, YES); /* line and return the next command */ bp = deblank(s); if((*bp == ':') && !strnicmp(make_label(bp+1),label, 8)) return; } batch_end(); /* Stop any further batch file */ crlfflg = YES; /* processing and print the error */ eprintf(MSG_LABEL, label); } GLOBAL VOID CDECL cmd_gosub (label) /* gosub label in batch file */ REG BYTE *label; { UWORD i; BYTE *bp, s[MAX_LINE+2]; /* Allocate buffer for Batch Input */ if (!batchflg) /* if not in batch mode */ return; /* this command is ignored */ if (batch->ret_offset[3] != 0L) { batch_end(); crlfflg = YES; eprintf(MSG_GOSUB); return; } if(*label == ':') /* Ignore any leading ':' */ label++; label = make_label(label); /* Convert to Label Format */ i = 0; while (batch->ret_offset[i] != 0L) i++; batch->ret_offset[i] = batch->offset; batch->offset = 0L; /* rewind the batch file */ batch->eof = NO; /* So it cannot be EOF */ if(!batch_open()) /* Check the Batch file is open */ return; /* and stop if the function fails. */ while(!batch->eof) { /* while not end of file read next */ batch_read(s, YES); /* line and return the next command */ bp = deblank(s); if((*bp == ':') && !strnicmp(make_label(bp+1),label, 8)) return; } batch_end(); /* Stop any further batch file */ crlfflg = YES; /* processing and print the error */ eprintf(MSG_LABEL, label); } GLOBAL VOID CDECL cmd_return() { UWORD i; if (!batchflg) return; if (batch->ret_offset[0] == 0L) { batch_end(); crlfflg = YES; eprintf(MSG_RETURN); return; } i = 0; while ((batch->ret_offset[i] != 0L)&&(i<4)) i++; batch->offset = batch->ret_offset[i-1]; batch->ret_offset[i-1] = 0L; } #if SWITCH_ENABLED GLOBAL VOID CDECL cmd_switch(list) REG BYTE *list; { BYTE *list_start; BYTE *label; WORD i,j; BYTE c; if (!batchflg) return; list_start = list; switch_retry: list = list_start; i = psp_poke(STDIN,1); #if defined(CDOSTMP) c =(BYTE) bdos(C_RAWIO, 0xFD); /* Get a character from console */ if ((c==0) ||(dbcs_lead(c))) bdos(C_RAWIO, 0xFD); /* skip second byte in DBCS pair */ #else c = (BYTE) msdos(MS_C_RAWIN, 0);/* Get a character from console */ if ((c==0) || (dbcs_lead(c))) msdos(MS_C_RAWIN, 0); /* skip second byte in DBCS pair */ #endif psp_poke(STDIN,i); if (c==0x03) int_break(); /* check for CTRL-C */ if (c==0x0d) c = '1'; /* return gives default of 1 */ i = (WORD) (c - '1'); if (i<0 || i>8) goto switch_retry; /* ignore invalid keys */ j = 0; while (j') return(OP_NE); if (op[1] == '=') return(OP_LE); return(OP_LT); } if (op[0] == '>') { if (op[1] == '=') return(OP_GE); return(OP_GT); } return(-1); } MLOCAL LONG get_decimal(s) BYTE *s; { LONG total = 0; if (*s == '#') s++; while (*s>='0' && *s<='9') { total *= 10; total += (LONG) (*s-'0'); s++; } return(total); } MLOCAL BOOLEAN CDECL test_cond(cptr) BYTE **cptr; { BYTE *cmd,*str1, *str2, *ptr; DTA search; BOOLEAN not, cond, neg,is_user; BYTE level; BYTE c[]=" \n"; WORD attr; UWORD userid; LONG val1,val2; cmd=*cptr; not = cond = NO; /* Initialise the Flags */ if(!strnicmp(cmd = deblank(cmd), "not", 3)) { not = YES; cmd = deblank(cmd+3); } switch(if_index(&cmd)) { /* * EXIST Option extract the possibly ambiguous filename * and check if it exists. */ case 0: cmd = deblank(get_filename(heap(), cmd, YES)); cond = !ms_x_first(heap(), ATTR_STD|ATTR_HID, &search); break; /* * DIREXIST Option checks if the given directory exists */ case 1: cmd = deblank(get_filename(heap(), cmd, YES)); attr = ms_x_chmod(heap(), ATTR_ALL, 0); if (attr < 0) cond = FALSE; else cond = (attr & 0x10); break; /* * ERRORLEVEL Option extract the decimal number from the * command line. */ case 2: level = 0; neg = FALSE; if(*cmd =='-') { neg = TRUE; cmd++; } if(!isdigit(*cmd)) { /* SYNTAX error if the */ syntax(); /* first character is not a */ return FALSE; /* digit. */ } while(isdigit(*cmd)) level = level * 10 + (*cmd++ - '0'); level = level & 0x00FF; if (neg) level = -level; cond = (level<=(err_ret & 0x00FF)); break; /*RG-02*/ #if !defined(NOXBATCH) #if (defined(CDOSTMP) || defined(CDOS)) /* * KEY ["string"] [==] [""] Search for the string "string" and * display it if it exists, then read a key and echo it. * However, if the string to match is null "" then do a keyboard * status check and return TRUE if there is no key there */ case 3: cmd = deblank(cmd); ptr=cmd; if (display_string(&ptr)!=0) return FALSE; cmd = deblank(ptr); while(*cmd == '=') /* Remove any "=" string */ cmd++; cmd = deblank(cmd); if ((*cmd==0)||(*(cmd+1)!=' ')) { /* check for condition */ syntax(); return FALSE; } /* read a character from the keyboard */ c[0]=bdos(C_RAWIO, 0xFD); /* Get a character from console */ /* echo the char typed */ if (echoflg) /* echo to screen if wanted */ putc(c[0]); crlf(); /* print cr/lf */ /* check if it matches */ if((tolower(c[0])==*cmd)||(toupper(c[0])==*cmd)) cond=TRUE; else cond=FALSE; /* skip the condition */ while (*cmd!=' ') cmd++; /* skip the char */ break; #endif #if !defined (NOSECURITY) && (defined(CDOSTMP) || defined(CDOS)) /* * USERID Option extract the 4 digit hex user id * and check if it is us. */ case 4: if (!login_enabled()) return FALSE; cmd = deblank(cmd); if(aschextobin(cmd)==get_user_on_station()) cond=TRUE; else cond=FALSE; do { /* skip past the user id */ if ((*cmd>='0' && *cmd<='9') ||(tolower(*cmd)>='a' && tolower(*cmd)<='f')) cmd++; /* skip the hex digit */ else { syntax(); return FALSE; } } while (*cmd!=' '); break; /* * LOGINNAME Option extract the loginname and check if it is us. */ case 5: if (!login_enabled()) return FALSE; is_user=TRUE; /* * GROUPNAME Option extract the loginname and check if it is us. */ case 6: if (!login_enabled()) return FALSE; cmd = deblank(cmd); if(aschextobin(user_info.userid)!=get_user_on_station()) { if(get_user_info(get_user_on_station())!=0) { syntax(); is_user=cond=FALSE; return FALSE; } } if((is_user==TRUE)&&(strncmp(strlwr(cmd),strlwr(user_info.loginname),strlen(user_info.loginname))==0)) { cond=TRUE; cmd+=strlen(user_info.loginname); } else if((is_user!=TRUE)&&(strncmp(strlwr(cmd),strlwr(user_info.groupname),strlen(user_info.groupname))==0)) { cond=TRUE; cmd+=strlen(user_info.groupname); } else { cond=FALSE; while (*cmd!=' ') cmd++; /* skip the name */ } if (*cmd!=' ') { is_user=cond=FALSE; } is_user=FALSE; break; #endif #endif /*NOXBATCH*/ /*RG-02-end*/ /* * String comparison option. */ default: str1 = cmd; /* Extract String 1 */ while ((!is_blank(cmd)) && (*cmd != '=') && ((*cmd != '!') || (cmd[1]!= '=')) && (*cmd != '<') && (*cmd != '>')) { cmd = skip_char(cmd); } str2 = cmd; cmd = deblank(cmd); attr = get_operator(cmd++); if (attr == -1) { syntax(); return(FALSE); } *str2 = 0; if (*cmd == '=' || *cmd == '>') cmd++; cmd = deblank(cmd); str2 = cmd; while (!is_blank(cmd)) cmd = skip_char(cmd); *cmd++ = 0; if (*str1 == '#') { val1 = get_decimal(str1); val2 = get_decimal(str2); switch(attr) { case OP_EQ: cond = (val1==val2); break; case OP_NE: cond = (val1!=val2); break; case OP_LT: cond = (val1val2); break; case OP_GE: cond = (val1>=val2); break; } } else switch(attr) { case OP_EQ: cond = (strcmp(str1,str2) == 0); break; case OP_NE: cond = (strcmp(str1,str2) != 0); break; case OP_LT: cond = (strcmp(str1,str2) < 0); break; case OP_LE: cond = (strcmp(str1,str2) <= 0); break; case OP_GT: cond = (strcmp(str1,str2) > 0); break; case OP_GE: cond = (strcmp(str1,str2) >= 0); break; } break; } if(not) /* if negated condition */ cond = !cond; *cptr=cmd; return cond; /* write result back */ } #if !defined(NOXBATCH) BOOLEAN is_it_or(BYTE *cmd) { cmd--; if ((*cmd != 0) && (*cmd != '\t') && (*cmd != ' ')) return FALSE; cmd++; if (strnicmp(cmd, "or", 2) != 0) return FALSE; cmd+=2; if ((*cmd != '\t') && (*cmd != ' ')) return FALSE; return TRUE; } #endif GLOBAL VOID CDECL cmd_if(cmd) BYTE *cmd; { BOOLEAN cond; ifcond=cond=test_cond(&cmd); if(!*deblank(cmd)) { /* and return a SYNTAX error*/ syntax(); /* if it is empty. */ return; } if(!cond) { #if !defined(NOXBATCH) while (!is_it_or(cmd) && (*cmd != 0)) { if (strnicmp(cmd,"ECHO",4)==0) return; cmd++; } if (*cmd==0) return; /* no OR's so quit now */ if_context = TRUE; docmd(deblank(cmd), YES); /* New command starts at "or" */ #endif } else { cmd = deblank(cmd); if (strnicmp(cmd,"AND",3)) { if (parse(cmd)) return; /* IF won't have been 'parsed' for */ /* > or < redirectors so do it now */ } if_context = TRUE; /* Execute command if the */ docmd(cmd, YES); /* condition flag is TRUE */ } if_context=FALSE; } /*RG-03*/ #if !defined(NOXBATCH) GLOBAL VOID CDECL cmd_or(cmd) BYTE *cmd; { BOOLEAN cond; BYTE *org_cmd; cond=test_cond(&cmd); if(!*deblank(cmd)) { /* and return a SYNTAX error*/ syntax(); /* if it is empty. */ return; } if(!cond) { org_cmd = cmd; /* now look for "OR" */ while (!is_it_or(cmd) && (*cmd != 0)) { if (strnicmp(cmd,"ECHO",4)==0) while(*cmd) cmd++; else cmd++; } if (*cmd==0) { /* oh dear, no ORs */ if (ifcond) /* but so far so good, so do command anyway */ docmd(deblank(org_cmd), YES); return; } docmd(deblank(cmd), YES); /* New command starts at "or" */ return; } else { cmd = deblank(cmd); if (strnicmp(cmd,"AND",3)) { if (parse(cmd)) return; /* IF won't have been 'parsed' for */ /* > or < redirectors so do it now */ } ifcond=cond; /* Execute command if the */ docmd(cmd, YES); /* condition flag is TRUE */ } } #endif /*NOXBATCH*/ /*RG-03-end*/ GLOBAL VOID CDECL cmd_for(s) BYTE *s; { FCONTROL *fc; BYTE *bp1; fc = (FCONTROL *) heap_get(sizeof(FCONTROL)); /* Allocate Control Struct */ if(forptr) /* and prevent nesting of */ goto for_error; /* FOR Command. */ s = deblank(s); /* Remove leading blanks */ if ((*s++ != '%') || /* Get the FOR variable */ (fc->forvar = *s++) < ' ') /* character and save */ goto for_error; if(strnicmp(s = deblank(s), "in", 2)) /* Check for the correct */ goto for_error; /* command syntax. */ s = deblank(s+2); if (*s++ != '(') goto for_error; fc->files = (BYTE *)heap(); /* Allocate FOR parameter */ while(*s && *s != ')') { /* buffer and scan the */ bp1 = (BYTE *)heap(); /* command line generating */ /* zero terminated strings */ while(strchr(batch_sep, *s)) /* Skip any separators */ s = skip_char(s); while( *s != ')' && /* then copy all valid */ !strchr(batch_sep, *s)) /* characters into buffer */ /* then zero terminate */ copy_char(&bp1, &s); *bp1++ = '\0'; heap_get(strlen(heap()) + 1); /* Preserve String */ } *(BYTE *)heap_get(1) = '\0'; /* Final String is zero */ /* bytes in length */ s = deblank(s); if(*s++ != ')') goto for_error; if(strnicmp(s = deblank(s), "do", 2)) goto for_error; if(in_flag & REDIR_ACTIVE) /* If Input redirection has been */ in_flag |= REDIR_FOR; /* enabled for this command force*/ /* it on for the complete command*/ if(out_flag & REDIR_ACTIVE) /* If Output redirection has been*/ out_flag |= REDIR_FOR; /* enabled for this command force*/ /* it on for the complete command*/ fc->cmd = (BYTE *)heap_get(strlen(s = deblank(s+2)) +1); strcpy(fc->cmd, s); fc->sflg = NO; /* File matching inactive */ for_flag = YES; /* Turn FOR processing ON */ forptr = fc; /* Save control Structure */ return; for_error: /* When a Syntax error occurs */ heap_set((BYTE *) fc); /* restore the heap and print */ syntax(); /* an error message. */ return; } GLOBAL VOID for_end() { if(for_flag) { heap_set((BYTE *) forptr); /* Terminate FOR processing */ forptr = (FCONTROL *) NULL; /* restore the HEAP and reset */ for_flag = NO; /* control flags. */ } } /*.pa*/ /* * This command generates the displayed prompt based on the contents * of the string PROMPT= in the environment. Otherwise the default * prompt string DEFAULT_PROMPT is used. */ MLOCAL BOOLEAN prompt_flg = FALSE; /* Prompt Flag */ MLOCAL VOID prompt() /* display command line prompt */ { REG BYTE *cp; BYTE buf[MAX_PATHLEN]; BYTE c; #if !STACK BYTE cpbuf[MAX_ENVLEN]; #endif /*rbf*/ #if 1 BYTE prmptcpbuf[MAX_ENVLEN]; REG BYTE *prmptcp = prmptcpbuf; #endif if(!echoflg) /* Return if No Echo */ return; #if defined(CPM) cp = heap(); strcpy(cp, "[CPM] $u$p$g"); #else if(env_scan(msg_prmeq, cp = (BYTE *)heap())) strcpy(cp, DEFAULT_PROMPT); #endif if(prompt_flg) /* If the previous Prompt display */ strcpy(cp, "$n$g"); /* terminated due to a Critical */ /* then just display the default */ prompt_flg = TRUE; /* drive. */ #if STACK cp = stack(strlen(cp) + 1); #else cp = &cpbuf[0]; #endif strcpy(cp, heap()); while((c = *cp++) != 0) { /* get next character */ if (c != '$') /* if not '$', print as is */ putc (c); else { c = *cp++; switch(tolower(c)) { /* else get next character */ case '\0': /* Treat "$\0" as an invalid */ cp--; /* prompt command sequence */ break; case 't': /* print current time */ disp_systime(); break; case 'd': /* print current date */ disp_sysdate(); break; case 'p': /* print current path */ if (ms_x_curdir(drive+1, buf) < 0) printf(MSG_DRV_INVALID); else printf("%c:%s%s", drive+'A', pathchar, buf); break; case 'v': /* print version number */ cmd_ver(""); break; case 'n': /* print default drive */ putc((BYTE) drive+'A'); break; case 'g': /* print ">" */ putc ('>'); break; case 'l': /* print "<" */ putc ('<'); break; case 'b': /* print "|" */ putc ('|'); break; case 'q': /* print "=" */ putc ('='); break; case '_': /* print CR,LF */ crlf(); break; case 'h': /* print backspace, space, backspace */ printf("\b \b"); break; case 'e': /* print ESC character */ putc ('\33'); break; /*RG-01 */ #if !defined(NOXBATCH) #if !defined (NOSECURITY) #if defined(CDOSTMP) || defined(CDOS) case 'm': /* print mail status */ if (login_enabled()) { if(chk_mail()) { /* we have mail */ if(env_scan("MAIL=", heap())) printf(MSG_UHAVEMAIL); else printf("%s",heap()); } } break; case 'u': /* Display the User Name */ if (login_enabled()) { if((aschextobin(user_info.userid)!=get_user_on_station())||(strlen(user_info.userid)==0)) { if(get_user_info(get_user_on_station())!=0) printf("#%04X",get_user_on_station()); else printf("%s",user_info.loginname); } else printf("%s",user_info.loginname); } break; #endif #endif #endif /*NOXBATCH*/ /*RG-01-end*/ #if defined(CPM) case 'u': /* Display the User Number */ printf("%d", user); break; #endif #if defined(DOSPLUS) case 'u': if (!env_scan("LOGINNAME=",heap())) printf("%s",heap()); break; #endif case '$': putc ('$'); /* print single '$' */ break; case 'x': if ((allow_pexec)&&(!batchflg)) prompt_exec(); break; default: /* Otherwise the character */ break; } } } prompt_flg = FALSE; /* Prompt display completed OK */ } #if !defined(CDOSTMP) /* The following functions are called by the int2e_handler function in * COM.C. If a program is run from a batch file and calls INT 2E to * execute a new batch file the original batch file must NOT be terminated. * Therefore batch and batchflg must be saved, set to zero, and then * restored when INT 2E returns. - EJH */ GLOBAL VOID int2e_start() { batchflg_save = batchflg; batchflg = 0; batch_save = batch; echoflg_save = echoflg; echoflg = ECHO_ON; } GLOBAL VOID int2e_finish() { batchflg = batchflg_save; batch = batch_save; echoflg = echoflg_save; } #endif #if defined(CDOS) MLOCAL VOID map_user_page(UWORD window_page, UWORD physical_page) { UWORD pblk[3]; pblk[0] = window_page; pblk[1] = physical_page; pblk[2] = 0; bdos(181, pblk); } MLOCAL BYTE FAR * map_pd_mem(UWORD ppd, UWORD wp, UWORD seg, UWORD offset) { /* Map memory for another process into our memory space so we examine it */ UWORD base_seg; UWORD page; PD FAR * pdptr; UWORD sysdat; UWORD mptbl; UWORD FAR * ptr; UWORD mp_off; UWORD i; MPAD FAR * mp; sysdat = FP_SEG(pd); ptr = MK_FP(sysdat, 0xc8); /* DEBUG */ if (*ptr == 0) /* for DEBUG under XM assume non-banked system */ return(MK_FP(seg, offset)); /* DEBUG */ ptr = MK_FP(sysdat, *ptr + 8); mptbl = *ptr; /* get segment of 4k page to map */ base_seg = (seg + (offset+15)/16) & 0xff00; /* now find physical page to map */ pdptr = (PD FAR *) MK_FP(sysdat, ppd); for (mp_off = pdptr->P_MPAR; mp_off !=0;) { mp = MK_FP(sysdat, mp_off); mp_off = mp->link; if ((base_seg >= mp->start) && (base_seg < (mp->start + mp->length))) { page = mp->xios; for (i = 0; i < ((base_seg - mp->start)/1024); i++){ ptr = MK_FP(mptbl, page*2); page = *ptr; } page = 4*page + (((base_seg - mp->start)/0x0100) & 3); map_user_page(wp, page); return(MK_FP(wp*0x0100,(seg*16+offset) & 0x0fff)); } } /* not found in mpad's - must be non-banked address */ return(MK_FP(seg, offset)); } MLOCAL VOID ip_poke(UWORD ppd, UWORD wp, UWORD seg, UWORD offset, UBYTE val) { UBYTE FAR *ptr; ptr = map_pd_mem(ppd, wp, seg, offset); *ptr = val; } MLOCAL UBYTE ip_peek(UWORD ppd, UWORD wp, UWORD seg, UWORD offset) { UBYTE FAR *ptr; ptr = map_pd_mem(ppd, wp, seg, offset); return(*ptr); } MLOCAL UWORD ip_peek_word(UWORD ppd, UWORD wp, UWORD seg, UWORD offset) { return (ip_peek(ppd, wp, seg, offset) + 256*ip_peek(ppd, wp, seg, offset+1)); } GLOBAL VOID inherit_TMP_state(VOID) /* inherit batch files */ /* NB. This is bodged - we don't inherit FOR state, or redirection */ { PD FAR *parent; UWORD batch_seg; UWORD tmp_mpad; UWORD mp_off; MPAD FAR * mp; VOID FAR *window; UWORD win_page; UWORD i; parent = (PD FAR *) MK_FP(FP_SEG(pd), pd->P_PARENT); /* verify our parent is a Tmp, forget if it isn't */ if ((parent->P_NAME[0] != 'T') || (parent->P_NAME[1] != 'm') || (parent->P_NAME[2] != 'p')) return; /* allocate a 4k aligned window to bank data into */ i = 2*4096/16; mem_alloc(&window, &i, i, i); if (i==0) return; win_page = (FP_SEG(window) + 0x0100) / 0x100; batch_seg = ip_peek_word(pd->P_PARENT, win_page, parent->P_PSP, TmpPspBatchSeg); /* do we have any batch files to inherit */ if (batch_seg) { echoflg = (BOOLEAN)ip_peek_word(pd->P_PARENT, win_page, ip_peek_word(pd->P_PARENT, win_page, parent->P_PSP, TmpPspDataSeg), ip_peek_word(pd->P_PARENT, win_page, parent->P_PSP, TmpPspEchoFlgPtr)); /* recover Tmp's hidden mpads */ tmp_mpad = ip_peek_word(pd->P_PARENT, win_page, parent->P_PSP, TmpPspMpad); for (mp_off = parent->P_MPAR; mp_off != 0;) { mp = MK_FP(FP_SEG(parent), parent->P_MPAR); mp_off = mp->link; } mp->link = tmp_mpad; inherit_batch_file(pd->P_PARENT, win_page, batch_seg); mp->link = 0; /* unlink again */ } map_user_page(0, 0); /* force user page to be unmapped */ bdos(141,0); /* dispatch to re-map TPA memory */ mem_free(&window); /* free the window */ } MLOCAL VOID inherit_batch_file(UWORD ppd, UWORD win_page, UWORD batch_seg) { UBYTE FAR * dst; BCONTROL FAR *save0; FCONTROL *save1; BYTE *save2; WORD save3; UWORD i; UWORD data_seg; PD FAR *parent; /* if we are in a nested batch file, inherit older batch file first */ i = ip_peek_word(ppd, win_page, batch_seg, 2); if (i != 0) inherit_batch_file( ppd, win_page, i); batch_new(); /* new incarnation of batch */ save0 = batch->bcontrol; save1 = batch->fcontrol; save2 = batch->heap_start; save3 = batch->heap_size; dst = (BYTE FAR *) batch; /* copy batch structure */ for (i=0; ieof - (UWORD) &batch->bcontrol, (UBYTE) TRUE); /* * Copy the invoking command and the individual elements * of the command line into a buffer ready for processing */ parent = (PD FAR *) MK_FP(FP_SEG(pd), pd->P_PARENT); data_seg = ip_peek_word(pd->P_PARENT, win_page, parent->P_PSP, TmpPspDataSeg); i = (UWORD) batch->batcmd; batch->batcmd = (BYTE *)heap(); /* Initialize Parameter Buffer */ while (ip_peek_word(ppd, win_page, data_seg, i) != 0) *(BYTE *)heap_get(1) = ip_peek(ppd, win_page, data_seg, i++); *(WORD *)heap_get(2) = 0; /* Double NULL is a terminator */ /* for command line params */ batch->bcontrol = save0; batch->fcontrol = save1; batch->heap_start = save2; batch->heap_size = save3; batchflg++; /* increment batch flag */ crlfflg = YES; /* print CR/LF after this */ } #endif EXTERN N_CMD novell_ext_list[]; EXTERN BYTE FAR * CDECL farptr(BYTE *); EXTERN BYTE FAR * CDECL cgroupptr(BYTE *); EXTERN BOOLEAN CDECL call_novell(BYTE *, BYTE *, WORD); EXTERN BOOLEAN CDECL nov_station(WORD *); EXTERN WORD CDECL nov_connection(); MLOCAL BOOLEAN novell_extension(src,dst) BYTE *src; BYTE *dst; /* * Check if src string is a novell string to be expanded. eg login_name. * if so, put expansion in dst. */ { N_CMD FAR *n_cmd_p; BYTE FAR *cpf; WORD i; for (i=0;src[i] && src[i]!='=';i++); src[i] = 0; n_cmd_p = (N_CMD FAR *)farptr((BYTE *)&novell_ext_list[0]); while(n_cmd_p->string) { cpf = cgroupptr(n_cmd_p->string); for(i=0; (cpf[i]==src[i]) && src[i]; i++); if(cpf[i]==src[i]) { (*n_cmd_p->func)(dst); return(0); } n_cmd_p++; } return(1); } GLOBAL VOID CDECL get_login_name(dst) BYTE *dst; { struct s_nov_e346_in { WORD len; BYTE code; } nov_e346_in; struct s_nov_e346_out { WORD len; BYTE level; LONG id; } nov_e346_out; struct s_nov_e336_in { WORD len; BYTE code; LONG id; } nov_e336_in; struct s_nov_e336_out { WORD len; LONG id; WORD type; BYTE name[48]; } nov_e336_out; nov_e346_in.len = 1; nov_e346_in.code = 0x46; nov_e346_out.len = 5; nov_e346_out.id = -1L; call_novell((BYTE *)&nov_e346_in, (BYTE *)&nov_e346_out, 0xE3); if (nov_e346_out.id == -1L) { *dst = 0; return; } nov_e336_in.len = 5; nov_e336_in.code = 0x36; nov_e336_in.id = nov_e346_out.id; nov_e336_out.len = 54; call_novell((BYTE *)&nov_e336_in, (BYTE *)&nov_e336_out, 0xE3); strcpy(dst,nov_e336_out.name); } GLOBAL VOID CDECL get_pstation(dst) BYTE *dst; { WORD sn[3]; if (nov_station(sn)) { *dst = 0; return; } sprintf(dst,"%04X%04X%04X",sn[0],sn[1],sn[2]); } GLOBAL VOID CDECL get_full_name(dst) BYTE *dst; { /* scan bindery */ struct s_nov_e337_in { WORD len; BYTE code; LONG last_object_id; WORD object_type; BYTE object_name_len; BYTE object_name[48]; } nov_e337_in; struct s_nov_e337_out { WORD len; LONG object_id; WORD object_type; BYTE object_name[48]; BYTE object_flag; BYTE object_security; BYTE object_has_properties; } nov_e337_out; /* scan property */ struct s_nov_e33c_in { WORD len; BYTE code; WORD object_type; BYTE object_name_len; BYTE object_name[48]; LONG sequence_num; BYTE property_name_len; BYTE property_name[16]; } nov_e33c_in; struct s_nov_e33c_out { WORD len; BYTE property_name[16]; BYTE property_flags; BYTE property_security; LONG sequence_num; BYTE property_has_value; BYTE more_properties; } nov_e33c_out; /* read property */ struct s_nov_e33d_in { WORD len; BYTE code; WORD object_type; BYTE object_name_len; BYTE object_name[48]; BYTE segment_num; BYTE property_name_len; BYTE property_name[16]; } nov_e33d_in; struct s_nov_e33d_out { WORD len; BYTE property_value[128]; BYTE more_segments; BYTE property_flags; } nov_e33d_out; int res; get_login_name(nov_e337_in.object_name); nov_e337_in.object_name_len = strlen(nov_e337_in.object_name); if (!nov_e337_in.object_name_len){ *dst = 0; return; } nov_e337_in.code = 0x37; nov_e337_in.len = sizeof(struct s_nov_e337_in) - 2; nov_e337_out.len = sizeof(struct s_nov_e337_out) -2 ; nov_e337_in.last_object_id = -1L; nov_e337_in.object_type = 0x0100; /*user*/ for(;;){ res = call_novell((BYTE *)&nov_e337_in, (BYTE *)&nov_e337_out, 0xe3 ); if ( res == 0xfc ) break; else if ( res ){ *dst= 0; return; } if ( nov_e337_out.object_has_properties ){ nov_e33c_in.code = 0x3c; nov_e33c_in.len = sizeof(struct s_nov_e33c_in) - 2; nov_e33c_out.len = sizeof(struct s_nov_e33c_out) - 2; nov_e33c_in.object_type = nov_e337_out.object_type; nov_e33c_in.object_name_len = sizeof( nov_e33c_in.object_name ); strcpy( nov_e33c_in.object_name, nov_e337_out.object_name ); nov_e33c_in.sequence_num = -1L; nov_e33c_in.property_name_len = 1; nov_e33c_in.property_name[0] = '*'; for(;;){ res = call_novell( (BYTE *)&nov_e33c_in, (BYTE *)&nov_e33c_out, 0xe3 ); if ( res == 0xfb ) break; else if ( res ){ *dst = 0; return; } if ( nov_e33c_out.property_has_value ){ nov_e33d_in.code = 0x3d; nov_e33d_in.len = sizeof (struct s_nov_e33d_in) - 2; nov_e33d_out.len = sizeof (struct s_nov_e33d_out) - 2; nov_e33d_in.object_type = nov_e337_out.object_type; nov_e33d_in.object_name_len = sizeof( nov_e33d_in.object_name ); strcpy( nov_e33d_in.object_name, nov_e337_out.object_name ); nov_e33d_in.segment_num = 1; nov_e33d_in.property_name_len = strlen( nov_e33c_out.property_name ); strcpy(nov_e33d_in.property_name, nov_e33c_out.property_name ); for(;;){ res = call_novell( (BYTE *)&nov_e33d_in, (BYTE *)&nov_e33d_out, 0xe3); if ( res == 0xec ) break; else if ( res ){ *dst = 0; return; } if (!strcmp(nov_e33c_out.property_name,"IDENTIFICATION")){ strcpy(dst,nov_e33d_out.property_value); return; } nov_e33d_in.segment_num++; }/*for*/ }/*if*/ nov_e33c_in.sequence_num = nov_e33c_out.sequence_num; }/*for*/ nov_e337_in.last_object_id = nov_e337_out.object_id; }/*if*/ }/*for*/ } GLOBAL VOID CDECL get_hour(dst) BYTE *dst; { SYSTIME time; ms_gettime(&time); if (time.hour > 12) time.hour -= 12; if (time.hour == 0) time.hour = 12; sprintf(dst,"%d",time.hour); } GLOBAL VOID CDECL get_hour24(dst) BYTE *dst; { SYSTIME time; ms_gettime(&time); sprintf(dst,"%02d",time.hour); } GLOBAL VOID CDECL get_minute(dst) BYTE *dst; { SYSTIME time; ms_gettime(&time); sprintf(dst,"%02d",time.min); } GLOBAL VOID CDECL get_second(dst) BYTE *dst; { SYSTIME time; ms_gettime(&time); sprintf(dst,"%02d",time.sec); } GLOBAL VOID CDECL get_am_pm(dst) BYTE *dst; { SYSTIME time; BYTE FAR *p; ms_gettime(&time); if (time.hour >= 12) p = cgroupptr(PM_TIME); else p = cgroupptr(AM_TIME); while (*p) *dst++ = *p++; *dst = 0; } GLOBAL VOID CDECL get_greeting(dst) BYTE *dst; { SYSTIME time; BYTE FAR *p; ms_gettime(&time); if (time.hour < 12) p = cgroupptr(GREETING_MORNING); else if (time.hour < 17) p = cgroupptr(GREETING_AFTERNOON); else p = cgroupptr(GREETING_EVENING); while (*p) *dst++ = *p++; *dst = 0; } GLOBAL VOID CDECL get_year(dst) BYTE *dst; { SYSDATE date; ms_getdate(&date); sprintf(dst,"%d",date.year); } GLOBAL VOID CDECL get_short_year(dst) BYTE *dst; { SYSDATE date; ms_getdate(&date); sprintf(dst,"%d",date.year%100); } GLOBAL VOID CDECL get_month(dst) BYTE *dst; { SYSDATE date; ms_getdate(&date); sprintf(dst,"%d",date.month); } GLOBAL VOID CDECL get_month_name(dst) BYTE *dst; { SYSDATE date; BYTE *m; ms_getdate(&date); switch(date.month) { case 1: m = JAN_M; break; case 2: m = FEB_M; break; case 3: m = MAR_M; break; case 4: m = APR_M; break; case 5: m = MAY_M; break; case 6: m = JUN_M; break; case 7: m = JUL_M; break; case 8: m = AUG_M; break; case 9: m = SEP_M; break; case 10: m = OCT_M; break; case 11: m = NOV_M; break; case 12: m = DEC_M; break; } sprintf(dst,"%s",m); } GLOBAL VOID CDECL get_day(dst) BYTE *dst; { SYSDATE date; ms_getdate(&date); sprintf(dst,"%d",date.day); } GLOBAL VOID CDECL get_nday_of_week(dst) BYTE *dst; { SYSDATE date; ms_getdate(&date); sprintf(dst,"%d",date.dow+1); } GLOBAL VOID CDECL get_day_of_week(dst) BYTE *dst; { SYSDATE date; ms_getdate(&date); sprintf(dst,"%s",day_names(date.dow)); } GLOBAL VOID CDECL get_os_version(dst) BYTE *dst; { env_scan("VER=",dst); } GLOBAL VOID CDECL get_connection(dst) BYTE *dst; { int i; i = nov_connection(); if (i==-1) { *dst = 0; return; } sprintf(dst,"%d",i); }