/* * BATCH.C - batch file processor for COMMAND.COM. * * * * Comments: * * ??/??/?? (Evan Jeffrey) ------------------------------------------------- * started. * * 07/15/95 (Tim Norman) --------------------------------------------------- * modes and bugfixes. * * 08/08/95 (Matt Rains) --------------------------------------------------- * i have cleaned up the source code. changes now bring this source into * guidelines for recommended programming practice. * * i have added some constants to help making changes easier. * * 01/29/96 (Steffan Kaiser) ----------------------------------------------- * made a few cosmetic changes * * 02/05/96 (Tim Norman) --------------------------------------------------- * changed to comply with new first/rest calling scheme * */ #include #include #include #include #include #include "command.h" #define D_LABELERR "ERROR: label not found!" #define D_SYNTAXERR "ERROR: syntax error!" #define D_PAUSEMSG "[press any key to continue]" #define D_BEEP "beep" #define D_CALL "call" #define D_DOWN "down" #define D_ECHO "echo" #define D_ERRORLEVEL "errorlevel" #define D_EXISTS "exists" #define D_GOTO "goto" #define D_IF "if" #define D_NOT "not" #define D_ON "on" #define D_OFF "off" #define D_PAUSE "pause" #define D_REM "rem" #define D_SHIFT "shift" /* function decls */ void split (char *, char **); char *parse_firstarg (char *); void printprompt (void); /* * process a batch file * * */ int batch(char *firstword, char *restofline) { FILE *bfile; int argc; /* argc for call to this batch file */ char *argv[128]; /* argv not including name of batch file */ char *first, *rest; /* first and rest of read-in line */ int len; /* length of command-line string */ int realnum; /* the actual number of the parameter we want */ char *insertstr; /* string to replace with env. var. specifier */ char *nextpercent; /* place where the second % sign is when parsing */ /* %envvar% type environment variable includes */ char varname[128]; /* name of the env. var. we are extracting */ int length; /* length of env. var. name */ char *var; /* pointer to env. var. value */ char *tmp; /* temporary string pointer */ static int nestlevel = 0; static int called; static int echo = 1; int count; int tokens; int shiftlevel = 0; /* number of times we've shifted the parameters */ int offset = 0; int charnum; char textline[128]; char cmdline[128]; char *p[128]; /* varibles found within code */ int notval = 0; int found = 0; char filestring[256]; nestlevel++; /* keep track of how many batch files are running */ /* open the batch file */ if(!(bfile = fopen(firstword, "rt"))) { return(1); } /* split our command-line params */ split (restofline, argv); for (argc = 0; argv[argc]; argc++) ; /* set the called flag to 1. This indicates that a batch file has been */ /* run. Used for determining what to do when a batch file is called */ /* from within another batch file */ called = 1; /* cycle through each line of the batch file */ while(fgets(textline, sizeof (textline), bfile) != NULL) { len = strlen (textline); for (count = 0, offset = 0; textline[count]; count++) { if (textline[count] == '%') { /* command-line specifier (e.g. %0, %1) */ if (isdigit (textline[count+1])) { /* find the real arg number */ realnum = shiftlevel + textline[count+1] - '0'; /* if it's a valid argument number */ if (realnum >= 0 && realnum <= argc) { if (realnum == 0) insertstr = first; else insertstr = argv[realnum - 1]; /* adjust the length of the command line */ len += strlen (insertstr) - 2; /* check if command line is too long */ if (len > sizeof (cmdline) - 1) { /* spit out a nasty message and quit... there should be */ /* a better way of handling this */ fprintf (stderr, "Command line too long.\n"); fclose (bfile); nestlevel --; return 1; } /* copy expansion into string */ memcpy (&cmdline[offset], insertstr, strlen (insertstr)); offset += strlen (insertstr); count ++; } } else if (textline[count] == '%') { cmdline[offset++] = '%'; count++; } else if ((nextpercent = strchr (&textline[count+1], '%')) != NULL) { /* calculate length of env. var. name */ length = nextpercent - &textline[count+1]; /* copy the env. var. name into varname */ memcpy (varname, &textline[count+1], length); varname[length] = 0; /* env. var. must be in uppercase */ strupr (varname); /* get the value of the env. var. */ var = getenv (varname); /* if the env. var. exists */ if (var) { /* make sure this doesn't make the length too long */ len += strlen (var) - length; if (len > sizeof (cmdline) - 1) { fprintf (stderr, "Command line too long.\n"); fclose (bfile); nestlevel --; return 1; } /* copy into the new string */ memcpy (&cmdline[offset], var, strlen (var)); offset += strlen (var); } } } else cmdline[offset++] = textline[count]; } /* NUL terminate the string */ cmdline[offset] = 0; /* do we want to expand aliases in batch files??? */ /* make a copy that we can use to pass to parsecommandline if nothing */ /* else matches */ strcpy (textline, cmdline); /* parse the beginning of the command */ first = cmdline; while (isspace (*first)) first++; /* check for non-echo character */ if (*first == '@') { first++; while (isspace (*first)) first++; } else if (echo) { printprompt (); puts (cmdline); } /* find the rest of the line and delimit the first word */ rest = parse_firstarg (first); /* POST condition: rest may be NULL */ /* check for blank line */ if(first[0] == 0) { ; } /* check for SHIFT instruction */ else if(strcmpi (first, D_SHIFT) == 0) { /* check if we are shifting down rather than up */ if(rest && strncmpi(rest, D_DOWN, sizeof (D_DOWN)) == 0) /* only shift down if shiftlevel != 0 */ shiftlevel = shiftlevel ? shiftlevel - 1 : 0; else /* shift up */ shiftlevel++; } /* check for PAUSE instruction */ else if(strcmpi(first, D_PAUSE) == 0) { puts(D_PAUSEMSG); _getch(); } /* check for ECHO instruction */ else if(strcmpi(first, D_ECHO) == 0) { if (rest) { if(strncmpi(rest, D_OFF, sizeof (D_OFF)) == 0) echo = 0; else if(strncmpi(rest, D_ON, sizeof (D_ON)) == 0) echo = 1; else puts(rest); } } /* check for GOTO instruction */ else if(strcmpi(first, D_GOTO) == 0) { if (!rest) { fprintf (stderr, "No label specified for GOTO\n"); fclose (bfile); nestlevel --; return 1; } /* extract the label that we're going to */ parse_firstarg (rest); /* now search for the label */ rewind(bfile); found = 0; while (!found && fgets (textline, sizeof (textline), bfile) != NULL) { tmp = textline; while (isspace (*tmp)) tmp++; if (*tmp == ':') { tmp++; parse_firstarg (tmp); if (strcmp (tmp, rest) == 0) found = 1; } } if(!found) { puts(D_LABELERR); fclose (bfile); nestlevel --; return(1); } } /* check for IF command */ else if(strcmpi(first, D_IF) == 0) { puts ("IF not implemented yet"); #if 0 if(strcmpi(p[1], D_NOT) == 0) { notval++; } if(!strcmpi(p[1 + notval], D_ERRORLEVEL)) { ; } else if(!strcmpi(p[1 + notval], D_EXISTS)) { ; } else { ; } #endif } /* check for BEEP command */ else if(strcmpi(first, D_BEEP) == 0) { printf("\a"); } /* check for CALL command */ else if(strcmpi(first, D_CALL) == 0) { parsecommandline(rest); } /* ignore labels */ else if(first[0] == ':') { ; } else { /* clear the call flag */ called = 0; parsecommandline(textline); /* if a batch file was called, then return */ if(called) { fclose (bfile); nestlevel --; return(0); } /* set the call flag back */ called = 1; } } /* close the file and decrease the nesting level */ fclose(bfile); nestlevel--; if(nestlevel == 0) { /* reset the echo when the main calling batch file quits */ echo = 1; } return(0); } /* * comment this out if you want to use MickeySoft C * * */ int _getch() { return(getch()); }