Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

/*
     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 <stdio.h>
#include <stdlib.h>
#include <tchar.h>

#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
*           <progname><nul><args><nul>
*       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;
}