Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

/* -*- c: tabs: 4 -*- ******************************************************
* Module name   : getopt
* Module type   : Core Services
* Compiler(s)   : ANSI C
* Environment(s): n/a
*
* Description:
*                 
    Command line option parsing
*
* Provides user functions
*
    getopt
    getoptl
*
* Contants:
*
    n/a
*
* Version   Who      Date        Description 
  1.0       APY      02/06/97    Long argument version.
  1.1       APY      07/05/98    Core Services
*
* $Name:  $
* $Source: /cvsroot/device/DEVL/UTILS/CMDFILE/getopt.c,v $            
* $Revision: 1.2 $ $Date: 2004/05/10 03:47:05 $ $State: Exp $    
* $Author: ayoung $ $Locker:  $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cmdfile.h"
#include "getopt.h"

#ifndef index
#define index               strchr
#define rindex              strrchr
#endif
            

/*
 * get option letter from argument vector
 */
#ifndef sun
int     opterr = 1,         /* if error message should be printed */
        optind = 1;         /* index into parent argv vector */
#else
extern  int opterr;
extern  int optind;
#endif
int     optopt,             /* character checked for validity */
        optlidx = -1;       /* long index */
char    *optarg;            /* argument associated with option */

#define BADCH               (int)'?'
#define EMSG                ""




/*----------------------------------------------------------------------------
    Synopsis:
        #include <getopt.h>
        int getopt
            (
                int nargc,
                char *const *nargv,
                const char *ostr
            )  
            
        extern int  optarg; 
        extern int  optind; 
        extern int  opterr;
       
        int CsGetoptl
            (
                int nargc,
                char *const *nargv,
                const char *ostr,
                const struct option *lopt,
                int long_only
            )
            
        extern int  optlidx;
 
    MT-Level:
        Unsafe                
        
    Purpose:
        The CsGetopt function returns the next option letter
        in 'argv' that matches a letter in 'ostr'.  It supports
        all the rules of the command syntax standard (see below).
        This function is the functional replacement of the
        standard UNIX getopt() function.
                 
        The CsGetoptl functions extends the command syntax 
        including support for long options.
                                        
        'ostr' must contain the option letters the command using
        CsGetopt()/CsGetoptl() will recognize; if a letter is
        followed by a colon,  the option is expected to have
        an argument,  or group of arguments,  which may be
        separated from it by white space. 'optarg' is set to
        point to the start of the option argument on return.
                                         
        'optind' represents the 'argv' index of the next
        argument to be processed.  'optind' is external and
        is initialised to 1 before the first call.  When all
        options have been processed (that is, up to the
        first non-option argument), EOF is returned.  The 
        special option "--" (two hyphens) may be used to delimit
        the end of the options;  when it is encountered, EOF is
        returned and  "--"'  is skipped.   This is useful in 
        delimiting non-option arguments that begin with
        ``-'' (hyphen). 
                                                  
        The additional parameters to CsGetoptl() enable long 
        argument handling. 'lopt' defines the argument names
        and 'long_only' is used to specify whether long
        options are only matched when preceded with "--" or
        or long and short arguments can be mixed.
             
        'optlidx' represents the 'lopt' index of the matched 
        long argument, initialised to -1 upon each call.
              
        
    Parameters:
        
        nargc,          Number of command line arguments.
                       
        nargv,          The NULL terinated argument list.

        ostr,           Argument syntax specification
        
        lopt,           Specifies the pointer to the long argument list

                        struct option
                        {
                          char *name;   Option name 
                          int has_arg;  0=none, 1=required, 2=optional
                          int *flag;    Value buffer                 
                          int val;      value return on detection
                        };
                                         
                        The 'name' fields contains the address of the
                        NUL terminated string command label.  'has_arg' 
                        speicfies the whether an associated argument
                        isn't excepted (0), is required (1), or is 
                        optional (2).
                       
                        The following manifest constants are defined for
                        use within the 'has_arg' field:
           
                        no_argument         No argument expected
                        required_argument   An argument must be supplied
                        optional_argument   An optional argument
                                                                      
                        'flag' is the optional address of the buffer
                        assigned with the value of argument. 'val' states
                        the value to be returned upon the given option
                        being encountered.
                         
        long_only,      When specified as non-zero, long arguments are
                        only matched when preceded with "--".
  
    Command Syntax Standard:    
    
        The following command syntax should be obeyed. The 
        CsGetopt function supports Rules 3-10 below. Rules
        10b-10c are only active during long argument processing.
        The enforcement of the other rules must be done by
        the command itself.

        1.  Command names (name above) must be between two and
            nine characters long.

        2.  Command names must include only lower-case letters
            and digits.

        3.  Option names (option above) must be one character
            long.

        4.  All options must be preceded by "-".

        5.  Options with no arguments may be grouped after a
            single "-".

        6.  The first option-argument (optarg above) following
            an option must be preceded by a tab or space character.
            
        7.  Option-arguments cannot be optional.

        8.  Groups of option-arguments following an option
            must either be separated by commas or separated by
            tab or space character and quoted (-o xxx,z,yy or
            -o "xxx z yy").                       
               
        9.  All options must precede operands (cmdarg above)
            on the command line.

        10. "--" may be used to indicate the end of the options.
            
        10b "--" may precede a long option.
                           
        10c If a command element has the form "-f", where f is a
            valid short option, don't consider it an abbreviated
            form of a long option that starts with f.  Otherwise
            there would be no way to give the -f short option.
        
            On the other hand, if there's a long option "fubar"
            and the command element is "-fu",  do consider that an
            abbreviation of the long option, just like "--fu", 
            and not "-f" with arg "u".
       
        11. The order of the options relative to one another
            should not matter.                      
            
        12. The relative order of the operands (cmdarg  above)
            may affect their significance in ways determined
            by the command with which they appear.

        13. "-" preceded and followed by a space character
            should only be used to mean standard input.
                        
    Returns:    int - 
    
        The CsGetopt functions return the character matched, otherwise
        EOF at the end of input stream, or '?' upon a parer error.

        The parser prints an error message on the standard error
        and returns a "?" (question mark) when it encounters an option
        letter not included in 'ostr' or 'lopt' or no argument is located 
        after an option that expects one.  Error messages may be
        disabled by setting 'opterr' to 0.  The value of the character
        that caused the error is in 'optopt'.
        
        In addition CsGetoptl() generates messages when ambiguous
        arguments between the short and long selections are presented.
                     
    Examples:
    
        The following code fragment shows how one might process  the
        arguments for a command that can take the mutually exclusive
        options a and b, and the option o, which requires an
        argument:                  
        
        #include <stdlib.h>
        #include <stdio.h>
        #include <csgetopt.h>
                 
        int main(int argc, char **argv)
        {          
            int  aflg=0, bflg=0, errflg=0, c;
            char *ofile = NULL;

            while ((c = CsGetopt(argc, argv, "abo:")) != EOF)
                switch (c) {
                case 'a':
                    if (bflg)
                         errflg++;
                    else aflg++;
                    break;
                case 'b':
                    if (aflg)
                         errflg++;
                    else bflg++;
                    break;
                case 'o':
                    ofile = optarg;
                    void)printf("ofile = %s\n", ofile);
                    break;
                case '?':
                    errflg++;
                }
            if (errflg) {
                (void)fprintf(stderr, 
                    "usage: cmd [-a|-b] [-o <filename>] files...\n");
                exit (2);
            }
            while(optind < argc)
                printf("%s\n", argv[optind++]);
        }       
               
                
        The following code fragment shows how one might process the
        arguments for a command that can take the options 'a' and 'b',
        plus 'verbose' and 'debug' which requires an argument:                              
                
        #include <stdlib.h>
        #include <stdio.h>
        #include <csgetopt.h>         
                
        static struct option longopts[] =
        {
            {"verbose", 0,  NULL,   'v'},
            {"debug",   1,  NULL,   'd'},  
        };                          
                               
        int main(int argc, char **argv)
        {          
            int  aflg=0, bflg=0, debug=0, verbose=0, c;

            while ((c = CsGetoptl(argc, argv, "ab", longopts, 1)) != EOF)
                switch (c) {
                case 'a':
                    aflg++;
                    break;
                case 'b':
                    bflg++;
                    break;         
                case 'd':
                    debug = atoi(optarg);
                    break;
                case 'v':
                    verbose++;
                    break;
                case '?':
                    (void)fprintf(stderr, 
                        "usage: cmd [-ab] [-debug <lvl>] [-verbose] ...\n");
                    exit (2);                                           
                }
            : 
        }                  
        
----------------------------------------------------------------------------*/
int
getopt(
    int nargc, char *const *nargv, const char *ostr)
{
    return getoptl(nargc, nargv, ostr, NULL, 0);
}   


int
getopt_long(
    int nargc, char *const *nargv, const char *ostr, 
    const struct option *lopts, int *longind)
{
    int ret = getoptl(nargc, nargv, ostr, lopts, 0);
    if (longind)
        *longind = optlidx;
    return (ret);
}


int
getopt_long_only(
    int nargc, char *const *nargv, const char *ostr, 
    const struct option *lopts, int *longind)
{
    int ret = getoptl(nargc, nargv, ostr, lopts, 1);
    if (longind)
        *longind = optlidx;
    return (ret);
}


int 
getoptl(
    int nargc, char *const *nargv, const char *ostr, 
    const struct option *lopt, int long_only)
{
    static   char   *place = EMSG;      /* option letter processing */
    register char   *oli;               /* option letter list index */
    char     *pProg;

    if ((pProg = rindex(*nargv, '/')) == NULL)
        pProg = *nargv;
    else pProg++;
    optlidx = -1;

    if (!*place)
    {
    /* Update scanning pointer */
        if (optind >= nargc || *(place = nargv[optind]) != '-')
        {
            place = EMSG;
            return (EOF);
        }
        if (place[1] && *++place == '-')
        {
            if (!*++place)
            {
            /* Found "--" , meaning premature end of options */
                optind++;
                place = EMSG;
                return (EOF);
            }
        }

    /* Reference GCC 'getopt':
        * If ARGV-element has the form "-f", where f is a valid short
        * option, don't consider it an abbreviated form of a long
        * option that starts with f.  Otherwise there would be no way
        * to give the -f short option.
        * 
        * On the other hand, if there's a long option "fubar" and
        * the ARGV-element is "-fu",  do consider that an abbreviation
        * of the long option, just like "--fu", and not "-f" with
        * arg "u".
        * 
        * This distinction seems to be the most useful approach.
        */
        if (lopt != NULL && 
             (nargv[optind][1] == '-' || 
              (!long_only && (nargv[optind][2] || 
                    !index(ostr?ostr:"", nargv[optind][1])))))
        {
            const   struct option *pOpt;
            #define EXTRACT     1
            #define NONEXTRACT  2 
            #define AMBIGUOUS   4
            int     flags = 0;
            char    *pEnd;
            int     i;

        /* Test all long options for either exact match 
            *   or abbreviated matches.
            */
            for (pEnd = place; *pEnd && *pEnd != '='; pEnd++)
                ;
            for (pOpt=lopt, i=0; pOpt->name; pOpt++, i++)
                if (!strncmp(pOpt->name, place, pEnd-place))
                {
                    if ((int)(pEnd - place) == (int)strlen(pOpt->name))
                    {
                        flags &= ~AMBIGUOUS;
                        flags |= EXTRACT;       /* Exact match */
                        optlidx = i;
                        break;
                    }
                    if (flags & NONEXTRACT)
                        flags |= AMBIGUOUS;     /* Multiple match(s) */
                    else
                    {
                        flags |= NONEXTRACT;    /* Part match */
                        optlidx = i;
                    }
                }
            if (flags & AMBIGUOUS)
            {
                if (opterr)
                    (void) fprintf(stderr, 
                            "%s: option `%s' is ambiguous\n",
                            pProg, place);
                optind++;
                place = EMSG;
                return BADCH;
            }

        /* Long option match */
            if (flags & (EXTRACT|NONEXTRACT))
            {
                pOpt = lopt+optlidx;            
                optind++;
            
            /* Parse argument request */
                if (*pEnd)
                {
                    if (pOpt->has_arg)
                        optarg = pEnd+1;
                    else
                    {
                        if (opterr)
                        {
                            if (nargv[optind-1][1] == '-') 
                            {
                            /* Long format */
                                (void) fprintf(stderr,
                                    "%s: option `--%s' doesn't allow an argument\n",
                                    pProg, pOpt->name);
                            } else {
                            /* Short */
                                (void) fprintf(stderr,
                                    "%s: option `%c%s' doesn't allow an argument\n",
                                    pProg, nargv[optind-1][0], pOpt->name);
                            }
                        }
                        place = EMSG;
                        return BADCH;
                    }
                }
                else if (pOpt->has_arg)
                {                               
                    if (optind < nargc && 
                            strcmp(nargv[optind], "--") != 0)
                    {
                    /* Argument available (non-terminator) */
                        optarg = nargv[optind++];
                    }
                    else if (pOpt->has_arg != optional_argument)
                    {   
                    /* No argument */
                        if (opterr)
                            (void) fprintf(stderr,
                                "%s: option `%s' requires an argument\n",
                                   pProg, nargv[optind - 1]);
                        place = EMSG;
                        return (ostr && ostr[0] == ':' ? ':' : BADCH);
                    }                   
                    else
                    {                    
                        optarg = NULL;    /* Optional argument */
                    }                     
               }                     
                
            /* Return completion value */
                place = EMSG;
                if (pOpt->flag)
                {
                    *(pOpt->flag) = pOpt->val;
                    return 0;
                }
                return pOpt->val;
            }

        /* Can't find it as a long option.  If this is not 
            *   'long_only', or the option starts with '--' or 
            *   is not a valid short option, then it's an error.
            *
            *   Otherwise interpret it as a short option.  
            */
            if (!long_only || nargv[optind][1] == '-' || 
                    index(ostr ? ostr : "", *place) == NULL)
            {
                if (opterr)
                {
                    if (nargv[optind][1] == '-') {
                    /* Long format */
                        (void) fprintf(stderr, 
                                "%s: unrecognized option `--%s'\n",
                                pProg, place);
                    } else {
                    /* Short */
                        (void) fprintf(stderr, 
                                "%s: unrecognized option `%c%s'\n",
                                pProg, nargv[optind][0], place);
                    }
                }
                optind++;
                place = EMSG;
                return BADCH;
            }
        }
    }

/* Option letter okay? */
    if ((optopt = (int) *place++) == (int) ':' ||
            (oli = index(ostr, optopt)) == NULL)
    {
    /* If the user didn't specify '-' as an option, assume it means EOF */
        if (optopt == (int) '-')
            return (EOF);
        if (!*place)
            ++optind;
        if (opterr)
            (void) fprintf(stderr, 
                    "%s: illegal option -- %c\n", 
                    pProg, optopt);
        return (BADCH);
    }

/*LINTED*/
    if (*++oli != ':')
    { 
    /* Don't need argument */
        optarg = NULL;
        if (!*place)
            ++optind;
    } 
    else
    { 
    /* Need an argument */
        if (*place)
        { 
        /* No white space */
            optarg = place;
        } 
        else if (nargc <= ++optind || strcmp(nargv[optind], "--") == 0)
        {                                         
        /* No argument, or terminator encountered  */
            if (opterr)
                (void) fprintf(stderr, 
                    "%s: option requires an argument -- %c\n", 
                        pProg, optopt);
            place = EMSG;
            return (BADCH);
        } 
        else
        { 
        /* White space -- return next argument */
            optarg = nargv[optind];
        }
        place = EMSG;
        ++optind;
    }
    return (optopt);        /* Dump back option letter */
}