Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

getargs.c

/*
 * This file has been modified for the cdrkit suite.
 *
 * The behaviour and appearence of the program code below can differ to a major
 * extent from the version distributed by the original author(s).
 *
 * For details, see Changelog file distributed with the cdrkit package. If you
 * received this file from another source then ask the distributing person for
 * a log of modifications.
 *
 */

/* @(#)getargs.c  2.37 04/09/25 Copyright 1985, 1988, 1994-2003 J. Schilling */
#define     NEW
/*
 *    Copyright (c) 1985, 1988, 1994-2003 J. Schilling
 *
 *    1.3.88       Start implementation of release 2
 */
/*
 *    Parse arguments on a command line.
 *    Format string type specifier (appearing directly after flag name):
 *          ''    BOOL
 *          '!'   BOOL with size modifier +++ NEU +++ (XXX nicht fertig)
 *          '*'   string
 *          '?'   char
 *          '#'   number
 *          '&'   call function
 *          '+'   inctype                 +++ NEU +++
 *
 *    The '#' and '+' types may have size modifiers added:
 *          'c'/'C'     char
 *          's'/'S'     short
 *          'i'/'I'     int   (default)
 *          'l'/'L'     long
 *          'll'/'LL' long long
 *
 *    The format string 'f* ' may be used to disallow -ffoo for f*
 *
 *    XXX This is currently only implemented for the '*' format
 */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
/* LINTLIBRARY */
#include <mconfig.h>
#include <standard.h>
#include <utypes.h>
#include <getargs.h>
#include <vadefs.h>
#include <strdefs.h>
#include <schily.h>
#include <ctype.h>

#define     NOARGS              0   /* No more args               */
#define     NOTAFLAG      1   /* Not a flag type argument   */
#define     BADFLAG           (-1)  /* Not a valid flag argument  */
#define     BADFMT            (-2)  /* Error in format string     */
#define     NOTAFILE    (-3)  /* Seems to be a flag type arg      */


      int   _getargs __PR((int *, char *const **, const char *,
                                          BOOL, va_list));
LOCAL int   dofile __PR((int *, char *const **, const char **));
LOCAL int   doflag __PR((int *, char *const **, const char *,
                                    const char *, BOOL, va_list));
LOCAL int   dosflags __PR((const char *, const char *, BOOL, va_list));
LOCAL int   checkfmt __PR((const char *));
LOCAL int   checkeql __PR((const char *));

LOCAL va_list     va_dummy;

LOCAL char  fmtspecs[] = "#?*&+";
/*LOCAL     char  fmtspecs[] = "#?*&+!";*/

#define     isfmtspec(c)            (strchr(fmtspecs, c) != NULL)

/*---------------------------------------------------------------------------
|
|     get flags until a non flag type argument is reached
|
+---------------------------------------------------------------------------*/
/* VARARGS3 */
#ifdef      PROTOTYPES
EXPORT int
getargs(int *pac, char *const **pav, const char *fmt, ...)
#else
EXPORT int
getargs(pac, pav, fmt, va_alist)
      int   *pac;
      char  **pav[];
      char  *fmt;
      va_dcl
#endif
{
      va_list     args;
      int   ret;

#ifdef      PROTOTYPES
      va_start(args, fmt);
#else
      va_start(args);
#endif
      ret = _getargs(pac, pav, fmt, TRUE, args);
      va_end(args);
      return (ret);
}


/*---------------------------------------------------------------------------
|
|     get all flags on the command line, do not stop on files
|
+---------------------------------------------------------------------------*/
/* VARARGS3 */
#ifdef      PROTOTYPES
EXPORT int
getallargs(int *pac, char *const **pav, const char *fmt, ...)
#else
EXPORT int
getallargs(pac, pav, fmt, va_alist)
      int   *pac;
      char  **pav[];
      char  *fmt;
      va_dcl
#endif
{
      va_list     args;
      int   ret;

#ifdef      PROTOTYPES
      va_start(args, fmt);
#else
      va_start(args);
#endif
      for (; ; (*pac)--, (*pav)++) {
            if ((ret = _getargs(pac, pav, fmt, TRUE, args)) != NOTAFLAG)
                  break;
      }
      va_end(args);
      return (ret);
}


/*---------------------------------------------------------------------------
|
|     get next non flag type argument (i.e. a file)
|
+---------------------------------------------------------------------------*/
EXPORT int
getfiles(pac, pav, fmt)
      int         *pac;
      char *const *pav[];
      const char  *fmt;
{
      return (_getargs(pac, pav, fmt, FALSE, va_dummy));
}


/*---------------------------------------------------------------------------
|
|     check args until the next non flag type argmument is reached
|     *pac is decremented, *pav is incremented so that the
|     non flag type argument is at *pav[0]
|
|     return code:
|           NOARGS            no more args
|           NOTAFLAG    not a flag type argument
|           BADFLAG           a non-matching flag type argument
|           BADFMT            bad syntax in format string
|
|
+---------------------------------------------------------------------------*/
/*LOCAL*/ int
_getargs(pac, pav, fmt, setargs, args)
      register int            *pac;
      register char     *const      **pav;
            const char  *fmt;
            BOOL        setargs;
            va_list           args;
{
      const char  *argp;
            int   ret;


      for (; *pac > 0; (*pac)--, (*pav)++) {
            argp = **pav;

            ret = dofile(pac, pav, &argp);

            if (ret != NOTAFILE)
                  return (ret);

            ret = doflag(pac, pav, argp, fmt, setargs, args);

            if (ret != NOTAFLAG)
                  return (ret);
      }
      return (NOARGS);
}


/*---------------------------------------------------------------------------
|
| check if *pargp is a file type argument
|
+---------------------------------------------------------------------------*/
LOCAL int
dofile(pac, pav, pargp)
      register int            *pac;
      register char *const    **pav;
            const char  **pargp;
{
      register const char     *argp = *pargp;


      if (argp[0] == '-') {
            /*
             * "-"      is a special non flag type argument
             *    that usually means take stdin instead of a named file
             */
            if (argp[1] == '\0')
                  return (NOTAFLAG);
            /*
             * "--" is a prefix to take the next argument
             *    as non flag type argument
             * NOTE: Posix requires "--" to indicate the end of the
             *     flags on the command line. But we are currently not
             *     Posix.
             */
            if (argp[1] == '-' && argp[2] == '\0') {
                  if (--(*pac) > 0) {
                        (*pav)++;
                        return (NOTAFLAG);
                  } else {
                        return (NOARGS);
                  }
            }
      }

      /*
       * now check if it may be flag type argument
       * flag type arguments begin with a '-', a '+' or contain a '='
       * i.e. -flag +flag or flag=
       */
      if (argp[0] != '-' && argp[0] != '+' && (!checkeql(argp)))
            return (NOTAFLAG);

      return (NOTAFILE);
}


/*---------------------------------------------------------------------------
|
|     compare argp with the format string
|     if a match is found store the result a la scanf in one of the
|     arguments pointed to in the va_list
|
|     If setargs is FALSE, only check arguments for getfiles()
|     in this case, va_list may be a dummy arg.
|
+---------------------------------------------------------------------------*/
LOCAL int
doflag(pac, pav, argp, fmt, setargs, oargs)
            int         *pac;
            char  *const      **pav;
      register const char     *argp;
      register const char     *fmt;
            BOOL        setargs;
            va_list           oargs;
{
      char  argstr[2];
      long  val;
      Llong llval;
      int   singlecharflag    = 0;
      BOOL  isspec;
      BOOL  hasdash           = FALSE;
      BOOL  doubledash  = FALSE;
      BOOL  haseql            = checkeql(argp);
      const char  *sargp;
      const char  *sfmt = fmt;
      va_list     args;
      char  *const      *spav = *pav;
      int         spac  = *pac;
      void        *curarg     = (void *)0;

      /*
       * flags beginning with '-' don't have to include the '-' in
       * the format string.
       * flags beginning with '+' have to include it in the format string.
       */
      if (argp[0] == '-') {
            argp++;
            hasdash = TRUE;
            /*
             * Implement legacy support for --longopt
             * If we find a double dash, we do not look for combinations
             * of boolean single char flags.
             */
            if (argp[0] == '-') {
                  argp++;
                  doubledash = TRUE;
                  /*
                   * Allow -- only for long options.
                   */
                  if (argp[1] == '\0') {
                        return (BADFLAG);
                  }
            }
      }
      sargp = argp;

      /*
       * Initialize 'args' to the start of the argument list.
       * I don't know any portable way to copy an arbitrary
       * C object so I use a system-specific routine
       * (probably a macro) from stdarg.h.  (Remember that
       * if va_list is an array, 'args' will be a pointer
       * and '&args' won't be what I would need for memcpy.)
       * It is a system requirement for SVr4 compatibility
       * to be able to do this assgignement. If your system
       * defines va_list to be an array but does not define
       * va_copy() you are lost.
       * This is needed to make sure, that 'oargs' will not
       * be clobbered.
       */
      va_copy(args, oargs);

      if (setargs)
            curarg = va_arg(args, void *);

      /*
       * check if the first flag in format string is a singlechar flag
       */
      if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0')
            singlecharflag++;
      /*
       * check the whole format string for a match
       */
      for (;;) {
            for (; *fmt; fmt++, argp++) {
                  if (*fmt == '\\') {
                        /*
                         * Allow "#?*&+" to appear inside a flag.
                         * NOTE: they must be escaped by '\\' only
                         *     inside the the format string.
                         */
                        fmt++;
                        isspec = FALSE;
                  } else {
                        isspec = isfmtspec(*fmt);
                  }
                  /*
                   * If isspec is TRUE, the arg beeing checked starts
                   * like a valid flag. Argp now points to the rest.
                   */
                  if (isspec) {
                        /*
                         * If *argp is '+' and we are on the
                         * beginning of the arg that is currently
                         * checked, this cannot be an inc type flag.
                         */
                        if (*argp == '+' && argp == sargp)
                              continue;
                        /*
                         * skip over to arg of flag
                         */
                        if (*argp == '=') {
                              argp++;
                        } else if (*argp != '\0' && haseql) {
                              /*
                               * Flag and arg are not separated by a
                               * space.
                               * Check here for:
                               * xxxxx=yyyyy    match on '&'
                               * Checked before:
                               * abc=yyyyy      match on 'abc&'
                               *          or     'abc*'
                               *          or     'abc#'
                               * We come here if 'argp' starts with
                               * the same sequence as a valid flag
                               * and contains an equal sign.
                               * We have tested before if the text
                               * before 'argp' matches exactly.
                               * At this point we have no exact match
                               * and we only allow to match
                               * the special pattern '&'.
                               * We need this e.g. for 'make'.
                               * We allow any flag type argument to
                               * match the format string "&" to set
                               * up a function that handles all odd
                               * stuff that getargs will not grok.
                               * In addition, to allow getargs to be
                               * used for CPP type flags we allow to
                               * match -Dabc=xyz on 'D&'. Note that
                               * Dabc=xyz will not match 'D&'.
                               */
                              if ((!hasdash && argp != sargp) || *fmt != '&')
                                    goto nextarg;
                        }

                        /*
                         * The format string 'f* ' may be used
                         * to disallow -ffoo for f*
                         *
                         * XXX This is currently only implemented for
                         * XXX the '*' format
                         */
                        if (!haseql && *argp != '\0' && fmt[0] == '*' && fmt[1] == ' ')
                              goto nextarg;
                        /*
                         * *arpp == '\0' || !haseql
                         * We come here if 'argp' starts with
                         * the same sequence as a valid flag.
                         * This will match on the following args:
                         * -farg    match on 'f*'
                         * -f12           match on 'f#'
                         * +12            match on '+#'
                         * -12            match on '#'
                         * and all args that are separated from
                         * their flags.
                         * In the switch statement below, we check
                         * if the text after 'argp' (if *argp != 0) or
                         * the next arg is a valid arg for this flag.
                         */
                        break;
                  } else if (*fmt == *argp) {
                        if (argp[1] == '\0' &&
                            (fmt[1] == '\0' || fmt[1] == ',')) {

                              if (setargs)
                                    *((int *)curarg) = TRUE;


                              return (checkfmt(fmt)); /* XXX */
                        }
                  } else {
                        /*
                         * skip over to next format identifier
                         * & reset arg pointer
                         */
                  nextarg:
                        while (*fmt != ',' && *fmt != '\0') {
                              /* function has extra arg on stack */
                              if (*fmt == '&' && setargs)
                                    curarg = va_arg(args, void *);
                              fmt++;
                        }
                        argp = sargp;
                        break;
                  }
            }
            switch (*fmt) {

            case '\0':
                  /*
                   * Boolean type has been tested before.
                   */
                  if (singlecharflag && !doubledash &&
                      (val = dosflags(sargp, sfmt, setargs, oargs)) !=
                                                BADFLAG)
                        return (val);


                  return (BADFLAG);

            case ',':
                  fmt++;
                  if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0')
                        singlecharflag++;
                  if (setargs)
                        curarg = va_arg(args, void *);
                  continue;

            case '*':
                  if (*argp == '\0') {
                        if (*pac > 1) {
                              (*pac)--;
                              (*pav)++;
                              argp = **pav;
                        } else {
                              return (BADFLAG);
                        }
                  }
                  if (fmt[1] == ' ')      /* To disallow -ffoo for f* */
                        fmt++;
                  if (setargs)
                        *((const char **)curarg) = argp;


                  return (checkfmt(fmt));

            case '?':
                  /*
                   * If more than one char arg, it
                   * cannot be a character argument.
                   */
                  if (argp[1] != '\0')
                        goto nextchance;
                  if (setargs)
                        *((char *)curarg) = *argp;


                  return (checkfmt(fmt));

            case '+':
                  /*
                   * inc type is similar to boolean,
                   * there is no arg in argp to convert.
                   */
                  if (*argp != '\0')
                        goto nextchance;
                  /*
                   * If *fmt is '+' and we are on the beginning
                   * of the format desciptor that is currently
                   * checked, this cannot be an inc type flag.
                   */
                  if (fmt == sfmt || fmt[-1] == ',')
                        goto nextchance;

                  if (fmt[1] == 'l' || fmt[1] == 'L') {
                        if (fmt[2] == 'l' || fmt[2] == 'L') {
                              if (setargs)
                                    *((Llong *)curarg) += 1;
                              fmt += 2;
                        } else {
                              if (setargs)
                                    *((long *)curarg) += 1;
                              fmt++;
                        }
                  } else if (fmt[1] == 's' || fmt[1] == 'S') {
                        if (setargs)
                              *((short *)curarg) += 1;
                        fmt++;
                  } else if (fmt[1] == 'c' || fmt[1] == 'C') {
                        if (setargs)
                              *((char *)curarg) += 1;
                        fmt++;
                  } else {
                        if (fmt[1] == 'i' || fmt[1] == 'I')
                              fmt++;
                        if (setargs)
                              *((int *)curarg) += 1;
                  }

                  argstr[0] = *fmt;
                  argstr[1] = '\0';

                  return (checkfmt(fmt));

            case '#':
                  if (*argp == '\0') {
                        if (*pac > 1) {
                              (*pac)--;
                              (*pav)++;
                              argp = **pav;
                        } else {
                              return (BADFLAG);
                        }
                  }
                  if (*astoll(argp, &llval) != '\0') {
                        /*
                         * arg is not a valid number!
                         * go to next format in the format string
                         * and check if arg matches any other type
                         * in the format specs.
                         */
                  nextchance:
                        while (*fmt != ',' && *fmt != '\0') {
                              if (*fmt == '&' && setargs)
                                    curarg = va_arg(args, void *);
                              fmt++;
                        }
                        argp = sargp;
                        *pac = spac;
                        *pav = spav;
                        continue;
                  }
                  val = (long)llval;
                  if (fmt[1] == 'l' || fmt[1] == 'L') {
                        if (fmt[2] == 'l' || fmt[2] == 'L') {
                              if (setargs)
                                    *((Llong *)curarg) = llval;
                              fmt += 2;
                        } else {
                              if (setargs)
                                    *((long *)curarg) = val;
                              fmt++;
                        }
                  } else if (fmt[1] == 's' || fmt[1] == 'S') {
                        if (setargs)
                              *((short *)curarg) = (short)val;
                        fmt++;
                  } else if (fmt[1] == 'c' || fmt[1] == 'C') {
                        if (setargs)
                              *((char *)curarg) = (char)val;
                        fmt++;
                  } else {
                        if (fmt[1] == 'i' || fmt[1] == 'I')
                              fmt++;
                        if (setargs)
                              *((int *)curarg) = (int)val;
                  }
                  argstr[0] = *fmt;
                  argstr[1] = '\0';

                  return (checkfmt(fmt));

            case '&':
                  if (*argp == '\0') {
                        if (*pac > 1) {
                              (*pac)--;
                              (*pav)++;
                              argp = **pav;
                        } else {
                              return (BADFLAG);
                        }
                  }

                  if ((val = checkfmt(fmt)) != NOTAFLAG)
                        return (val);

                  if (setargs) {
                        int   ret;
                        void  *funarg = va_arg(args, void *);

                        ret = ((*(getargfun)curarg) (argp, funarg));
                        if (ret != NOTAFILE)
                              return (ret);
                        fmt++;
                  } else {
                        return (val);
                  }
                  /*
                   * Called function returns NOTAFILE: try next format.
                   */
            }
      }
}


/*---------------------------------------------------------------------------
|
|     parse args for combined single char flags
|
+---------------------------------------------------------------------------*/
typedef struct {
      void  *curarg;    /* The pointer to the arg to modify  */
      short count;            /* The number of times a sc flag appears */
      char  c;          /* The single char flag character    */
      char  type;       /* The type of the single char flag  */
} sflags;

LOCAL int
dosflags(argp, fmt, setargs, oargs)
      register const char     *argp;
      register const char     *fmt;
            BOOL        setargs;
            va_list           oargs;
{
#define     MAXSF 32
            sflags      sf[MAXSF];
            va_list args;
      register sflags   *rsf  = sf;
      register int      nsf   = 0;
      register const char *p  = argp;
      register int      i;
      register void     *curarg = (void *)0;
            char  type;

      /*
       * Initialize 'args' to the start of the argument list.
       * I don't know any portable way to copy an arbitrary
       * C object so I use a system-specific routine
       * (probably a macro) from stdarg.h.  (Remember that
       * if va_list is an array, 'args' will be a pointer
       * and '&args' won't be what I would need for memcpy.)
       * It is a system requirement for SVr4 compatibility
       * to be able to do this assgignement. If your system
       * defines va_list to be an array but does not define
       * va_copy() you are lost.
       * This is needed to make sure, that 'oargs' will not
       * be clobbered.
       */
      va_copy(args, oargs);

      if (setargs)
            curarg = va_arg(args, void *);

      while (*p) {
            for (i = 0; i < nsf; i++) {
                  if (rsf[i].c == *p)
                        break;
            }
            if (i >= MAXSF)
                  return (BADFLAG);
            if (i == nsf) {
                  rsf[i].curarg = (void *)0;
                  rsf[i].count = 0;
                  rsf[i].c = *p;
                  rsf[i].type = (char)-1;
                  nsf++;
            }
            rsf[i].count++;
            p++;
      }

      while (*fmt) {
            if (!isfmtspec(*fmt) &&
                (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0') &&
                strchr(argp, *fmt)) {
                  for (i = 0; i < nsf; i++) {
                        if (rsf[i].c == *fmt) {
                              if (fmt[1] == '+') {
                                    fmt++;
                                    if (fmt[1] == ',' ||
                                        fmt[1] == '\0') {
                                          rsf[i].type = 'i';
                                    } else if ((fmt[1] == 'l' ||
                                              fmt[1] == 'L') &&
                                              (fmt[2] == 'l' ||
                                              fmt[2] == 'L')) {
                                          /*
                                           * Type 'Q'uad (ll)
                                           */
                                          rsf[i].type = 'Q';
                                          fmt++;
                                    } else {
                                          /*
                                           * Type 'l','i','s','c'
                                           */
                                          rsf[i].type = fmt[1];
                                    }
                              } else {
                                    /*
                                     * ',' or '\0' for BOOL
                                     */
                                    rsf[i].type = fmt[1];
                              }
                              rsf[i].curarg = curarg;
                              break;
                        }
                  }
            }
            while (*fmt != ',' && *fmt != '\0') {
                  /* function has extra arg on stack */
                  if (*fmt == '&' && setargs)
                        curarg = va_arg(args, void *);
                  fmt++;
            }
            if (*fmt != '\0')
                  fmt++;

            if (setargs)
                  curarg = va_arg(args, void *);
      }
      for (i = 0; i < nsf; i++) {
            type = rsf[i].type;
            if (type == (char)-1) {
                  return (BADFLAG);
            }
            if (rsf[i].curarg) {
                  if (type == ',' || type == '\0') {
                        *((int *)rsf[i].curarg) = TRUE;
                  } else if (type == 'i' || type == 'I') {
                        *((int *)rsf[i].curarg) += rsf[i].count;
                  } else if (type == 'l' || type == 'L') {
                        *((long *)rsf[i].curarg) += rsf[i].count;
                  } else if (type == 'Q') {
                        *((Llong *)rsf[i].curarg) += rsf[i].count;
                  } else if (type == 's' || type == 'S') {
                        *((short *)rsf[i].curarg) += rsf[i].count;
                  } else if (type == 'c' || type == 'C') {
                        *((char *)rsf[i].curarg) += rsf[i].count;
                  } else {
                        return (BADFLAG);
                  }
            }
      }
      return (NOTAFLAG);
}

/*---------------------------------------------------------------------------
|
|     If the next format character is a comma or the string delimiter,
|     there are no invalid format specifiers. Return success.
|     Otherwise raise the getarg_bad_format condition.
|
+---------------------------------------------------------------------------*/
LOCAL int
checkfmt(fmt)
      const char  *fmt;
{
      char  c;

      c = *(++fmt);     /* non constant expression */


      if (c == ',' || c == '\0') {
            return (NOTAFLAG);
      } else {
            raisecond("getarg_bad_format", (long)fmt);
            return (BADFMT);
      }
}

/*---------------------------------------------------------------------------
|
|     Parse the string as long as valid characters can be found.
|     Valid flag identifiers are chosen from the set of
|     alphanumeric characters, '-' and '_'.
|     If the next character is an equal sign the string
|     contains a valid flag identifier.
|
+---------------------------------------------------------------------------*/
LOCAL int
checkeql(str)
      register const char *str;
{
      register unsigned char c;

      for (c = (unsigned char)*str;
                        isalnum(c) || c == '_' || c == '-'; c = *str++)
            /* LINTED */
            ;
      return (c == '=');
}

Generated by  Doxygen 1.6.0   Back to index