//function GETARGS.C -- gets command-line arguments (switches-first) //written by E. Nicholas Cupery 27 MAR 95 // This routine gets the raw command-line directly from the PSP, and decodes // it into individual argument tokens. This is superior to the standard C // "argc/argv[]" facility, for several reasons. First, this routine will // separate contiguous switches (for example "/a/b/c") into separate tokens. // Second, this routine will keep double-quoted tokens intact. Third, the // output array of command-line tokens will be sorted, with the program's // full file-specification first, followed by any command-line switches (tokens // that start with a slash) in the order in which they originally appeared, // followed by any non-switch tokens in the order in which they originally // appeared. // This function also returns the bare name of the program, stripped of its // path and extension information for use in error messages, and a string that // holds the error-explanation (if any). (Note that the error-explanation // string does NOT include any trailing carriage-control.) // The return status of this function is an integer, which specifies the // total argument-count including the file-specification, or an error flag. // Errors are possible here. If an error occurs, this function will return // an integer result that is always a negative quantity. These errors are: // // FR_GETARGS_ERR_DVTO == DOS version too old (not at least v3.0) // FR_GETARGS_ERR_TMIT == too many input tokens for my local storage // FR_GETARGS_ERR_EST == "empty" switch token (single '/' character) // FR_GETARGS_ERR_MDQ == misuse of double-quotes // // Note that the actual error number is of little value, since this routine // has already loaded a pointer to a meaningful error-message. In other words, // the caller should just check for a negative returned value, and then issue // an error-message using the program-name and error-message provided herein. #include "stdio.h" //standard I/O header #include "string.h" //standard string header #include "dos.h" //standard header for DOS stuff #include "FARBA.H" //Farba Research standard header #define FR_GETARGS_ERR_DVTO -1 //version too old (not at least v3.0) #define FR_GETARGS_ERR_TMIT -2 //too many input tokens #define FR_GETARGS_ERR_EST -3 //"empty" switch token (single '/') #define FR_GETARGS_ERR_MDQ -4 //misuse of double-quotes //BEGIN FUNCTION-PROTOTYPES // // //END OF FUNCTION-PROTOTYPES //BEGIN FUNCTION // int GETARGS(int *swcount, char *args[], char progname[9], char **errmsg) { //BEGIN GLOBAL VARIABLES USED // extern unsigned char _osmajor; //DOS major version number extern unsigned int _psp; //segment address of my PSP // //END OF GLOBAL VARIABLES USED //BEGIN PERMANENT (STATIC) LOCAL VARIABLES // static char filespec[256]; //near storage for full filespec static char dvtomsg[] = "DOS version too old (need v3.0 minimum)"; static char tmitmsg[] = "too many input tokens"; static char estmsg[] = "empty switch-token (single '/')"; static char mdqmsg[] = "misuse of double-quotes"; static char okaymsg[] = "success"; // //BEGIN PERMANENT (STATIC) LOCAL VARIABLES //BEGIN TEMPORARY LOCAL VARIABLES // S16 index; //local array-index S16 j; //local loop-counter S16 k; //local loop-counter S16 namestart; //starting index of bare name S16 quoting_flag; //"inside a quote-field" (0=no) S16 status; //return status for caller S16 tkncount; //total tokens counter S16 tknchars; //current-token char-count char lastchar; //last character parsed S16 quote_count; //count of quotes in a token char far *comlinptr; //far-pointer to command-line UINT far *envsegadd; //far-pointer to env-seg address char far *envblkptr; //far-pointer to environment UINT envseg; //environment segment-address char septoks[256]; //separated-tokens array char *ptrarray[FR_MAX_TOKENS+1]; //token-pointers array // //END OF TEMPORARY LOCAL VARIABLES //BEGIN EXECUTABLE CODE if (_osmajor < 3) //if this DOS version is too old { for (k=0 ; k < 8 ; k++) progname[k] = '?'; //use question-marks for name progname[8] = 0; //make dummy name ASCIZ *errmsg = dvtomsg; //point to error string status = FR_GETARGS_ERR_DVTO; //"DOS too old" status goto END; //return immediately } //LOAD FAR-POINTERS TO MY PSP AND ENVIRONMENT FP_SEG(comlinptr) = _psp; //segment address of my PSP FP_OFF(comlinptr) = 129; //offset to command-line string FP_SEG(envsegadd) = _psp; //segment address of my PSP FP_OFF(envsegadd) = 0x2C; //now point to env-seg address envseg = *envsegadd; //fetch environment segment addr FP_SEG(envblkptr) = envseg; //environment-block segment FP_OFF(envblkptr) = 0; //environment-block offset //SKIP PAST ALL EVARS while (*envblkptr) //while any E-vars left { while (*envblkptr) envblkptr++; //skip this E-var and... envblkptr++; //...its trailing null } //FETCH THE FULL FILE-SPECIFICATION FROM THE END OF THE ENVIRONMENT-BLOCK envblkptr += 2; //skip following-items count k=0; //start of full file-spec array do { //do all file-spec chars... envblkptr++; //bump the source pointer filespec[k] = *envblkptr; //copy a file-spec char k++; //bump the destination pointer } while (*envblkptr); //...while any char left args[0] = &filespec; //load pointer to full file-spec //LOAD JUST THE BARE PROGRAM-NAME INTO CALLER-SUPPLIED ARRAY namestart = strlen(filespec) - 1; //point at file-spec end while ((filespec[namestart] != FR_CHAR_BS) && //until find first backslash... (namestart != -1)) //...or run out of chars... { //...in Power-Ctrace debugger namestart--; //backup one char } j = 0; //start at beginning of progname for (k=namestart+1 ; k < namestart+9 ; k++) //for all bare filename chars { if (filespec[k] == '.') break; //done if hit the dot if (filespec[k] == ' ') break; //done if hit a space progname[j] = filespec[k]; //copy one filename char j++; //bump the progname pointer } progname[j] = 0; //add trailing ASCIZ null dummysub(); //ZZZ FOR USE IN GETTING POWER CTRACE TO STOP //LOAD INDIVIDUAL TOKENS INTO LOCAL LINEAR STORAGE k = 0; //start of local linear array tkncount = 0; //initialize token-counter NXTARG: //come here to look for next arg if (*comlinptr == FR_CHAR_CR) goto ENDALL; //done if found trailingif (*comlinptr == 0) goto ENDALL; //done if found trailing if ((*comlinptr == ' ') || //if have a space or... (*comlinptr == FR_CHAR_TAB)) //...a tab { comlinptr++; //bump the pointer goto NXTARG; //ignore the whitespace } lastchar = ' '; //set previous char to a space tknchars = 0; //initialize token char-count tkncount++; //count this new token if (tkncount > FR_MAX_TOKENS) //if too many tokens to store { *errmsg = tmitmsg; //pojnt to error string status = FR_GETARGS_ERR_TMIT; //"too many tokens" status goto END; //return immediately } quoting_flag = 0; //not inside a quote-field ptrarray[tkncount] = &septoks[k]; //store new token's address NXTCHAR: //come here if char in same arg if (*comlinptr == FR_CHAR_CR) goto ENDALL; //done if found trailing if (*comlinptr == 0) goto ENDALL; //done if found trailing if ((*comlinptr == ' ') || //if have a space or... (*comlinptr == FR_CHAR_TAB)) //...a tab { if (quoting_flag) //if inside a quote-field { septoks[k] = *comlinptr; //store this new token char lastchar = *comlinptr; //and save as previous char comlinptr++; //bump command-line pointer k++; //bump the token pointer tknchars++; //bump token's char-count goto NXTCHAR; //go look for another char } else //else, not inside quote-field { septoks[k] = 0; //make this token ASCIZ k++; //and bump the token pointer lastchar = *comlinptr; //and save as previous char goto NXTARG; //go handle next token } } if (*comlinptr == '/') //if have a slash { if (quoting_flag) //if inside a quote-field { septoks[k] = *comlinptr; //store this new token char lastchar = *comlinptr; //and save as previous char comlinptr++; //bump command-line pointer k++; //bump the token pointer tknchars++; //bump token's char-count goto NXTCHAR; //go look for another char } else //else, not inside quote-field { if (tknchars == 0) //if / is 1st char in token { septoks[k] = *comlinptr; //store this new token char lastchar = *comlinptr; //and save as previous char comlinptr++; //bump command-line pointer k++; //bump the token pointer tknchars++; //bump token's char-count goto NXTCHAR; //go look for another char } else //else, / ends current token { septoks[k] = 0; //make current token ASCIZ k++; //bump the token pointer goto NXTARG; //go handle as new token } } } if (*comlinptr == '"') //if have a double-quote { if (quoting_flag) //if inside a quote-field { quoting_flag = 0; //end the quoting-field } else //else, not inside quote-field { quoting_flag = 1; //start a quoting-field } if ((quoting_flag != 0) && //if new quote-field AND... (lastchar != FR_CHAR_TAB) && //...previous char not tab... (lastchar != ' ') && //...OR space... (lastchar != '/') && //...OR slash... (lastchar != '=') && //...OR equal-sign... (lastchar != ':') && //...OR colon... (lastchar != ';') && //...OR semicolon... (lastchar != ',') && //...OR comma... (lastchar != '"')) //...OR another double-quote { *errmsg = mdqmsg; //appropriate error string status = FR_GETARGS_ERR_MDQ; //"misuse of double-quote" goto END; //return immediately } septoks[k] = *comlinptr; //store this new token char lastchar = *comlinptr; //and save as previous char comlinptr++; //bump command-line pointer k++; //bump the token pointer tknchars++; //bump token's char-count goto NXTCHAR; //go look for another char } if ((lastchar == '"') && //if plain char following... (quoting_flag == 0)) //...end of a quote-field { *errmsg = mdqmsg; //point to error string status = FR_GETARGS_ERR_MDQ; //"misuse of double-quote" goto END; //return immediately } septoks[k] = *comlinptr; //store this plain token char lastchar = *comlinptr; //and save as previous char comlinptr++; //bump command-line pointer k++; //bump the token pointer tknchars++; //bump token's char-count goto NXTCHAR; //go look for another char ENDALL: //come here to end last arg septoks[k] = 0; //make last token ASCIZ //CHECK ALL TOKENS FOR LEGALITY for (k=1; k <= tkncount ; k++) //for each token { tknchars = strlen(ptrarray[k]); //get the token length if ((tknchars == 1) && //if single-char token AND... (*ptrarray[k] == '/')) //...is a lone slash { *errmsg = estmsg; //appropriate error string status = FR_GETARGS_ERR_EST; //"empty switch token" goto END; //return immediately } quote_count = 0; //initialize quote-counter for (j=0; j < tknchars ; j++) //for each char in this token { if (*(ptrarray[k]+j) == '"') quote_count++; //count double-quote chars } if ((quote_count != 0) && //if quote-count not 0 AND... (quote_count != 2)) //...quote-count not 2 { *errmsg = mdqmsg; //appropriate error string status = FR_GETARGS_ERR_MDQ; //"misuse of double-quote" goto END; //return immediately } } //POUR SWITCH ARGUMENT-POINTERS INTO CALLER-SUPPLIED POINTER-ARRAY index = 1; //initialize callers array index for (k=1 ; k <= tkncount ; k++) //for all tokens found { if (*ptrarray[k] == '/') //if this token is a switch { args[index] = ptrarray[k]; //stick it in caller's array index++; //bump caller's array index swcount++; //count this switch token ptrarray[k] = 0; //mark this one "used up" } } //POUR NON-SWITCH ARGUMENT-POINTERS INTO CALLER-SUPPLIED POINTER-ARRAY for (k=1; k <= tkncount ; k++) //for all tokens found { if (ptrarray[k] != 0) //if this token not "used up" { args[index] = ptrarray[k]; //stick it in caller's array index++; //bump caller's array index } } //END OF FUNCTION // status = tkncount+1; //return arg-count on success *errmsg = okaymsg; //"success" END: return status; //return with status to caller }