/* Emulation of DOS startup code, using the following rules when interpreting arguments given on the operating system command line: Arguments are delimited by white space, which is either a space or a tab. A string surrounded by double quotation marks is interpreted as a single argument, regardless of white space contained within. A quoted string can be embedded in an argument. Note that the caret (^) is not recognized as an escape character or delimiter. A double quotation mark preceded by a backslash, \", is interpreted as a literal double quotation mark ("). Backslashes are interpreted literally, unless they immediately precede a double quotation mark. If an even number of backslashes is followed by a double quotation mark, then one backslash (\) is placed in the argv array for every pair of backslashes (\\), and the double quotation mark (") is interpreted as a string delimiter. If an odd number of backslashes is followed by a double quotation mark, then one backslash (\) is placed in the argv array for every pair of backslashes (\\) and the double quotation mark is interpreted as an escape sequence by the remaining backslash, causing a literal double quotation mark (") to be placed in argv. This list illustrates the rules above by showing the interpreted result passed to argv for several examples of command-line arguments. The output listed in the second, third, and fourth columns is from the ARGS.C program that follows the list. Command-Line Input argv[1] argv[2] argv[3] "a b c" d e a b c d e "ab\"c" "\\" d ab"c \ d a\\\b d"e f"g h a\\\b de fg h a\\\"b c d a\"b c d a\\\\"b c" d e a\\b c d e */ #include #include #include #define NULCHAR '\0' #define NLCHAR '\n' #define CRCHAR '\r' #define SPACECHAR ' ' #define TABCHAR '\t' #define DQUOTECHAR '\"' #define SLASHCHAR '\\' static void parse(const char *, const char *, char **, char *, int *, int *, int); /*** *_setargv, __setargv - set up "argc" and "argv" for C programs * *Purpose: * Read the command line and create the argv array for C * programs. * *Entry: * Arguments are retrieved from the program command line, * pointed to by _acmdln. * *Exit: * "argv" points to a null-terminated list of pointers to ASCIZ * strings, each of which is an argument from the command line. * "argc" is the number of arguments. The strings are copied from * the environment segment into space allocated on the heap/stack. * The list of pointers is also located on the heap or stack. * _pgmptr points to the program name. * *Exceptions: * Terminates with out of memory error if no memory to allocate. * *******************************************************************************/ int parse_cmdbuffer( const char *arg0, const char *cmd, char ***__argv, int *__argc ) { char *p; int numargs, numchars; int nls; /* 1 = contained line feeds */ /* Are there line feeds, of so dont use SPACES or TABS as argument delimitors */ nls = strchr(cmd, NLCHAR) ? 1 : 0; /* first find out how much space is needed to store args */ parse(arg0, cmd, NULL, NULL, &numargs, &numchars, nls); /* allocate space for argv[] vector and strings */ p = malloc(numargs * sizeof(char *) + numchars * sizeof(char)); if (p == NULL) return -1; /* error */ /* store args and argv ptrs in just allocated block */ parse(arg0, cmd, (char **)p, p + numargs * sizeof(char *), &numargs, &numchars, nls); /* set argv and argc */ *__argc = numargs - 1; *__argv = (char **)p; return *__argc; } /*** *static void parse_cmdline(cmdstart, argv, args, numargs, numchars) * *Purpose: * Parses the command line and sets up the argv[] array. * On entry, cmdstart should point to the command line, * argv should point to memory for the argv array, args * points to memory to place the text of the arguments. * If these are NULL, then no storing (only coujting) * is done. On exit, *numargs has the number of * arguments (plus one for a final NULL argument), * and *numchars has the number of bytes used in the buffer * pointed to by args. * *Entry: * char *cmdstart - pointer to command line of the form * * char **argv - where to build argv array; NULL means don't * build array * char *args - where to place argument text; NULL means don't * store text * *Exit: * no return value * int *numargs - returns number of argv entries created * int *numchars - number of characters used in args buffer * *Exceptions: * *******************************************************************************/ static void parse( const char *arg0, const char *cmdstart, char **argv, char *args, int *numargs, int *numchars, int linefeeds ) { const char *p; int inquote = 0; /* 1 = inside quotes */ char quote_char; int copychar = 0; /* 1 = copy char to *args */ unsigned numslash; /* num of backslashes seen */ /* Handle arg0 */ if (argv) *argv++ = (char *)arg0; *numargs = 1; /* the program name at least */ *numchars = strlen(arg0)+1; /* Loop on each argument */ for(p = cmdstart;;) { /* Eat (leading) white space */ if ( *p ) { while (*p == SPACECHAR || *p == TABCHAR || *p == NLCHAR || *p == CRCHAR) ++p; } if (*p == NULCHAR) break; /* end of args */ /* scan an argument */ if (argv) *argv++ = args; /* store ptr to arg */ ++*numargs; /* loop through scanning one argument */ for (;;) { copychar = 1; /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote 2N+1 backslashes + " ==> N backslashes + literal " N backslashes ==> N backslashes */ numslash = 0; while (*p == SLASHCHAR) { /* count number of backslashes for use below */ ++p; ++numslash; } if (*p == DQUOTECHAR) { /* if 2N backslashes before, start/end quote, otherwise copy literally */ if (numslash % 2 == 0) { if (inquote) { if (p[1] == DQUOTECHAR) p++; /* Double quote inside quoted string */ else /* skip first quote char and copy second */ copychar = 0; } else copychar = 0; /* don't copy quote */ inquote = !inquote; } numslash /= 2; /* divide numslash by two */ } /* copy slashes */ while (numslash--) { if (args) *args++ = SLASHCHAR; ++*numchars; quote_char = SLASHCHAR; } /* if at end of arg, break loop */ if (*p == NULCHAR || *p == NLCHAR || *p == CRCHAR || (!inquote && !linefeeds && (*p == SPACECHAR || *p == TABCHAR))) break; /* copy character into argument */ if (copychar) { if (args) *args++ = *p; ++*numchars; } ++p; } /* Warnings */ if (inquote) { fprintf(stderr, "%s: Unmatched quote argument (@%u).\n", arg0, (unsigned)(p - cmdstart)+1); } inquote = 0; /* null-terminate the argument */ if (args) *args++ = NULCHAR; /* terminate string */ ++*numchars; } /* We put one last argument in -- a null ptr */ if (argv) *argv++ = NULL; ++*numargs; }