//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 trailing
if (*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
}