Farba Research x86 C Language Example

//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
}

Farba Products WORK