/* Command line parsing with optional Wildcard expansion */ /* identical in function to CRT console startup code */ #include #include #include #define WIN32_LEAN_AND_MEAN #include #include "CmdLine.h" #define _T(x) x /* Globals */ int argc; char **argv; // Use Wildcard expansion on Command Line #define WILDCARD 1 static int mysetargv (char *lpszCmdLine); // ======================================================================= // ParseCommandLine() // // Returns: TRUE if successful, or FALSE if there was an error. // ======================================================================= int ParseCommandLine(LPTSTR cmdline) { if (mysetargv(cmdline)) return 0; // Returning TRUE means the caller should continue doing what they // were doing: we succeeded. return 1; } void FreeCommandLine(void) { } /*** *wild.c - wildcard expander * * Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved. * *Purpose: * expands wildcards in argv * * handles '*' (none or more of any char) and '?' (exactly one char) * *******************************************************************************/ /* ** these are the data structures ** ** __argv ** ------- ------ ** | |---->| |---->"arg0" ** ------- ------ ** | |---->"arg1" ** ------ ** .... ** ------ ** | |---->"argn" ** ------ ** |NULL| ** ------ ** argend ** ------- ** ------- | | ** | | __argc ------- ** ------- | ** | ** arghead V ** ------ --------- ---------- ** | |---->| | |----> .... ---->| |NULL| ** ------ --------- ---------- ** | | ** V V ** "narg0" "nargn" */ /* Prototypes */ struct argnode { char *argptr; struct argnode *nextnode; }; static int mycwild (void); static int mymatch(char *, char *); static int myadd(char *); static void mysort(struct argnode *); static char * myfind (char *pattern); static void myparse_cmdline(char *cmdstart, char **argv, char *args, int *numargs, int *numchars); #define NULCHAR _T('\0') #define SPACECHAR _T(' ') #define TABCHAR _T('\t') #define DQUOTECHAR _T('\"') #define SLASHCHAR _T('\\') #define FWDSLASHCHAR _T('/') #define COLONCHAR _T(':') #define QUOTECHAR _T('\"') #define SLASH _T("\\") #define FWDSLASH _T("/") #define STAR _T("*.*") #define DOT _T(".") #define DOTDOT _T("..") #define WILDSTRING _T("*?") static struct argnode *arghead; static struct argnode *argend; /*** *stdargv.c - standard & wildcard _setargv routine * * Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved. * *Purpose: * processes program command line, with or without wildcard expansion * *******************************************************************************/ /*** *_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. * *******************************************************************************/ static int mysetargv (char *lpszCmdLine) { char *p; char *cmdstart; /* start of command line to parse */ int numargs, numchars; /* if there's no command line at all (won't happen from cmd.exe, but possibly another program), then we use _pgmptr as the command line to parse, so that argv[0] is initialized to the program name */ cmdstart = lpszCmdLine; /* first find out how much space is needed to store args */ myparse_cmdline(cmdstart, NULL, NULL, &numargs, &numchars); /* allocate space for argv[] vector and strings */ p = (char*)malloc(numargs * sizeof(char *) + numchars); if (p == NULL) return(-1); /* store args and argv ptrs in just allocated block */ myparse_cmdline(cmdstart, (char **)p, p + numargs * sizeof(char *), &numargs, &numchars); /* set argv and argc */ argc = numargs - 1; argv = (char **)p; #ifdef WILDCARD /* call _[w]cwild to expand wildcards in arg vector */ if (mycwild()) return(-1); #endif /* WILDCARD */ return(0); } /*** *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 myparse_cmdline ( char *cmdstart, char **argv, char *args, int *numargs, int *numchars ) { char *p; char c; int inquote; /* 1 = inside quotes */ int copychar; /* 1 = copy char to *args */ unsigned numslash; /* num of backslashes seen */ *numchars = 0; *numargs = 1; /* the program name at least */ /* first scan the program name, copy it, and count the bytes */ p = cmdstart; if (argv) *argv++ = args; #ifdef WILDCARD /* To handle later wild card expansion, we prefix each entry by it's first character before quote handling. This is done so _[w]cwild() knows whether to expand an entry or not. */ if (args) *args++ = *p; ++*numchars; #endif /* WILDCARD */ /* A quoted program name is handled here. The handling is much simpler than for other arguments. Basically, whatever lies between the leading double-quote and next one, or a terminal null character is simply accepted. Fancier handling is not required because the program name must be a legal NTFS/HPFS file name. Note that the double-quote characters are not copied, nor do they contribute to numchars. */ if ( *p == DQUOTECHAR ) { /* scan from just past the first double-quote through the next double-quote, or up to a null, whichever comes first */ while ( (*(++p) != DQUOTECHAR) && (*p != NULCHAR) ) { ++*numchars; if ( args ) *args++ = *p; } /* append the terminating null */ ++*numchars; if ( args ) *args++ = NULCHAR; /* if we stopped on a double-quote (usual case), skip over it */ if ( *p == DQUOTECHAR ) p++; } else { /* Not a quoted program name */ do { ++*numchars; if (args) *args++ = *p; c = *p++; } while ( c != SPACECHAR && c != NULCHAR && c != TABCHAR ); if ( c == NULCHAR ) { p--; } else { if (args) *(args-1) = NULCHAR; } } inquote = 0; /* loop on each argument */ for(;;) { if ( *p ) { while (*p == SPACECHAR || *p == TABCHAR) ++p; } if (*p == NULCHAR) break; /* end of args */ /* scan an argument */ if (argv) *argv++ = args; /* store ptr to arg */ ++*numargs; #ifdef WILDCARD /* To handle later wild card expansion, we prefix each entry by it's first character before quote handling. This is done so _[w]cwild() knows whether to expand an entry or not. */ if (args) *args++ = *p; ++*numchars; #endif /* WILDCARD */ /* 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; } /* if at end of arg, break loop */ if (*p == NULCHAR || (!inquote && (*p == SPACECHAR || *p == TABCHAR))) break; /* copy character into argument */ if (copychar) { if (args) *args++ = *p; ++*numchars; } ++p; } /* 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; } /*** *int _cwild() - wildcard expander * *Purpose: * expands wildcard in file specs in argv * * handles '*' (none or more of any char), '?' (exactly one char), and * '[string]' (chars which match string chars or between n1 and n2 * if 'n1-n2' in string inclusive) * *Entry: * *Exit: * returns 0 if successful, -1 if any malloc() calls fail * if problems with malloc, the old argc and argv are not touched * *Exceptions: * *******************************************************************************/ int mycwild (void) { char **newargv = argv; struct argnode *nodeptr; int newargc; char **tmp; char *wchar; arghead = argend = NULL; for (newargv = argv; *newargv; newargv++) /* for each arg... */ if ( *(*newargv)++ == QUOTECHAR ) /* strip leading quote from quoted arg */ { if (myadd(*newargv)) return(-1); } else if (wchar = strpbrk( *newargv, WILDSTRING )) { /* attempt to expand arg with wildcard */ if (mymatch( *newargv, wchar )) return(-1); } else if (myadd( *newargv )) /* normal arg, just add */ return(-1); /* count the args */ for (newargc = 0, nodeptr = arghead; nodeptr; nodeptr = nodeptr->nextnode, newargc++) ; /* try to get new arg vector */ if (!(tmp = (char **)malloc(sizeof(char *)*(newargc+1)))) return(-1); /* the new arg vector... */ argv = tmp; /* the new arg count... */ argc = newargc; /* install the new args */ for (nodeptr = arghead; nodeptr; nodeptr = nodeptr->nextnode) *tmp++ = nodeptr->argptr; /* the terminal NULL */ *tmp = NULL; /* free up local data */ for (nodeptr = arghead; nodeptr; nodeptr = arghead) { arghead = arghead->nextnode; free(nodeptr); } /* return success */ return(0); } /*** *match(arg, ptr) - [STATIC] * *Purpose: * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ static int mymatch (char *arg, char *ptr) { char *cnew; int length = 0; char *all; struct argnode *first; int gotone = 0; while (ptr != arg && *ptr != SLASHCHAR && *ptr != FWDSLASHCHAR && *ptr != COLONCHAR) { /* find first slash or ':' before wildcard */ ptr--; } if (*ptr == COLONCHAR && ptr != arg+1) /* weird name, just add it as is */ return(myadd(arg)); if (*ptr == SLASHCHAR || *ptr == FWDSLASHCHAR || *ptr == COLONCHAR) /* pathname */ length = ptr - arg + 1; /* length of dir prefix */ if (cnew = myfind(arg)) { /* get the first file name */ first = argend; do { /* got a file name */ if (strcmp(cnew, DOT) && strcmp(cnew, DOTDOT)) { if (*ptr != SLASHCHAR && *ptr != COLONCHAR && *ptr != FWDSLASHCHAR ) { /* current directory; don't need path */ #ifdef _DEBUG if (!(arg=(char*)malloc((strlen(cnew)+1))) || myadd(strcpy(arg,cnew))) #else /* _DEBUG */ if (!(arg = strdup(cnew)) || myadd(arg)) #endif /* _DEBUG */ return(-1); } else /* add full pathname */ if (!(all=(char*)malloc((length+strlen(cnew)+1))) || myadd(strcpy(strncpy(all,arg,length)+length,cnew) - length)) return(-1); gotone++; } } while (cnew = myfind(NULL)); /* get following files */ if (gotone) { mysort(first ? first->nextnode : arghead); return(0); } } return(myadd(arg)); /* no match */ } /*** *add(arg) - [STATIC] * *Purpose: * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ static int myadd ( char *arg ) { struct argnode *nodeptr; if (!(nodeptr = (struct argnode *)malloc(sizeof(struct argnode)))) return(-1); nodeptr->argptr = arg; nodeptr->nextnode = NULL; if (arghead) argend->nextnode = nodeptr; else arghead = nodeptr; argend = nodeptr; return(0); } /*** *sort(first) - [STATIC] * *Purpose: * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ static void mysort (struct argnode *first) { struct argnode *nodeptr; char *temp; if (first) /* something to sort */ while (nodeptr = first->nextnode) { do { if (stricmp(nodeptr->argptr, first->argptr) < 0) { temp = first->argptr; first->argptr = nodeptr->argptr; nodeptr->argptr = temp; } } while (nodeptr = nodeptr->nextnode); first = first->nextnode; } } /*** *find(pattern) - find matching filename * *Purpose: * if argument is non-null, do a DOSFINDFIRST on that pattern * otherwise do a DOSFINDNEXT call. Return matching filename * or NULL if no more matches. * *Entry: * pattern = pointer to pattern or NULL * (NULL means find next matching filename) * *Exit: * returns pointer to matching file name * or NULL if no more matches. * *Exceptions: * *******************************************************************************/ static char * myfind (char *pattern) { char *retval; static HANDLE _WildFindHandle; static LPWIN32_FIND_DATA findbuf; if (pattern) { if (findbuf == NULL) findbuf = (LPWIN32_FIND_DATA)malloc(MAX_PATH + sizeof(*findbuf)); if (_WildFindHandle != NULL) { (void)FindClose( _WildFindHandle ); _WildFindHandle = NULL; } _WildFindHandle = FindFirstFile( (LPTSTR)pattern, findbuf ); if (_WildFindHandle == (HANDLE)0xffffffff) return NULL; } else if (!FindNextFile( _WildFindHandle, findbuf )) { (void)FindClose( _WildFindHandle ); _WildFindHandle = NULL; return NULL; } retval = findbuf->cFileName; return retval; }