[comp.sources.misc] v17i050: parseargs - functions to parse command line arguments, Part05/12

brad@hcx1.ssd.csd.harris.com (Brad Appleton) (03/18/91)

Submitted-by: Brad Appleton <brad@hcx1.ssd.csd.harris.com>
Posting-number: Volume 17, Issue 50
Archive-name: parseargs/part05

This is part 5 of parseargs

#!/bin/sh
# this is Part.05 (part 5 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file parseargs/ibm_args.c continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 5; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
	echo 'x - still skipping parseargs/ibm_args.c'
else
echo 'x - continuing file parseargs/ibm_args.c'
sed 's/^X//' << 'SHAR_EOF' >> 'parseargs/ibm_args.c' &&
**    an option switch on the command-line.
**
**    KwdPrefix contains the single character prefix used to precede
**    a keyword switch on the command-line.
***^^**********************************************************************/
static  char  OptPrefix='/';
static  char  KwdPrefix='/';
X
#define  isUNIXISH  ( OptPrefix == '-' )
X
X
/***************************************************************************
** ^GLOBAL-VARIABLE: Usage_Requested
**
** ^VISIBILITY:
**    static-global (visible to all functions in this file).
**
** ^DESCRIPTION:
**    Indicates whether a usage message was requested by the user
**    (as opposed to triggerred by a syntax error).  If the message
**    is requested by the user then it is always printed in verbose
**    mode and does not return an error-status-code.
***^^**********************************************************************/
static  BOOL  Usage_Requested = FALSE;
X
X
X   /* macros to detect an option/keyword -- watch out for side effects!! */
#define isOPT(s)  \
X   ( !BTEST(cmd_flags(cmd), pa_KWDSONLY)  && \
X     !BTEST(cmd_state(cmd), ps_NOFLAGS)  && \
X     *s == OptPrefix  &&  *(s+1) \
X   )
X
#define isKWD(s)  \
X   ( !BTEST(cmd_flags(cmd), pa_OPTSONLY)  && \
X     !BTEST(cmd_state(cmd), ps_NOFLAGS)  && \
X     *s == KwdPrefix  &&  *(s+1) \
X   )
X
X
/***************************************************************************
** ^FUNCTION: get_prefixes - determine short and long keyword prefixes
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static VOID get_prefixes()
#endif
/*  
** ^PARAMETERS:
**    None.
**
** ^DESCRIPTION:
**    Get_prefixes will determine the prefixes to used to denote option
**    switches and keyword switches on the command-line.  The prefixes
**    are determined by the $SWITCHAR environment varaible. The first
**    character of the variable is the option-switch prefix and the second
**    character is the keyword-switch prefix.
**
**    If The option-switch prefix is '-' then Unix-style command-line parsing
**    is performed, otherwise MS-DOS style command-line parsing is used.
**
** ^REQUIREMENTS:
**    None.
**
** ^SIDE-EFECTS:
**    Sets the global variables "OptPrefix" and "KwdPrefix'.
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**    - If $SWITCHAR is NULL or empty
**      - use the defaults ('/' and '/').
**    - Else
**      - set the OptPrefix to the first character in SWITCHAR
**      End-if
**
**    - If there is a second character in SWITCHAR
**      - assign it to KwdPrefix
**    - Else if OptPrefix is '-'
**      - then use '+' as the default KwdPrefix
**    - Else
**      - use '/' as the default KwdPrefix
**      End-if
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static VOID  get_prefixes( void )
#endif
{
X   char *prefixes = getenv( "SWITCHAR" );
X
X   if ( prefixes &&  *prefixes ) {
X      OptPrefix = *prefixes;
X      KwdPrefix = *(prefixes + 1);
X      if ( !KwdPrefix )  KwdPrefix = (( OptPrefix == '-' ) ? '+' : '/');
X   }
X   else {
X      OptPrefix = '/';
X      KwdPrefix = '/';
X   }
}
X
X
/***************************************************************************
** ^FUNCTION: ibm_parse - parse MS-DOS and OS/2 arg-vectors
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   int ibm_parse( argv, argd )
/*
** ^PARAMETERS:
*/
X   char *argv[];
/*    -- the vector of string arguments from the command-line
*/
X   ARGDESC argd[];
/*    -- the programmer description of the command and its args
*/
#endif  /* !__ANSI_C__ */
X  
/* ^DESCRIPTION:
**    Ibm_parse will parse the arguments in the given vector of strings,
**    assign the corresponding values to the command-line arguments specified
**    in argd, and check the syntax of the command-line.
**
** ^REQUIREMENTS:
**    The final element in argv must be a NULL pointer.
**
** ^SIDE-EFECTS:
**    argd is modified according to the command-line description and parameters
**
** ^RETURN-VALUE:
**    pe_SUCCESS (0) if no errors are encountered
**    pe_SYSTEM (-1) if a system error is encountered
**    pe_SYNTAX if a syntax error is encountered
**
** ^ALGORITHM:
**    - get the active option and keyword prefixes
**    - determine whether to use Unix style or not (based on the prefixes)
**    - for each command-line argument
**       - attempt to match the argument as a keyword
**       - if it is a keyword argument
**          - record and convert its value (if any)
**         else attempt to match the argument as an option
**         if it is an option
**          - record and convert its value (if any)
**         else it is a positional parameter
**          - record and convert its value (if any)
**         else there are too many arguments
**          - return pe_SYNTAX
**         end-if
**       end-for
***^^**********************************************************************/
#ifdef __ANSI_C__
X   int ibm_parse( char *argv[], ARGDESC argd[] )
#endif
{
X   register ARGDESC *ad, *args, *cmd;
X   register char **av = argv;
X   register char *p;
X   argName_t  name;
X   argMask_t  flags;
X   int  parse_error = pe_SUCCESS;
X   BOOL  ad_okay, is_match = FALSE;
X
X   if ( !argd )  return  parse_error;
X
X      /* initialize command-structure */
X   if ( !CMD_isINIT(argd) )  init_args( argd );
X   cmd = argd;
X
X   get_prefixes();
X
X   while ( av  &&  (p = *av++) ) {
X      if ( isKWD(p) &&
X          ( (OptPrefix != KwdPrefix) || *(p+2) && !strchr(s_ARG_SEP, *(p+2)) )
X         ) {
X         char *s, c = '\0';
X
X         /* check for `++' to end flags */
X         if ( *(p+1) == KwdPrefix  &&  !*(p+2) ) {
X            BSET( cmd_state(cmd), ps_NOFLAGS );
X            cmd_list(cmd) = ARGDESCNULL;
X            continue;
X         }
X
X            /* get past prefix and look for possible argument */
X         s = strpbrk(++p, s_ARG_SEP);
X         if(s) {
X            c = *s;
X            *s++ = '\0';
X         }
X
X         is_match = FALSE;
X         for ( args = argd ; args  &&  !is_match ; args = cmd_defargs(args) ) {
X            for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
X               if ( arg_type(ad) == argDummy )  continue;
X
X               if ( !ARG_isPOSONLY(ad)  &&  match(p, arg_sname(ad)) == 0 ) {
X                  is_match = TRUE;
X                  break;
X               }/*if*/
X            }
X         }
X
X         if ( c )  *(s-1) = c;  /* restore the equal sign */
X
X         if ( !is_match ) {
X            if ( OptPrefix == KwdPrefix ) {
X               goto  MATCHOPT;  /* maybe its an option (and NOT a keyword) */
X            }
X            usrerr("%c%s switch unknown", KwdPrefix, p);
X            parse_error = pe_SYNTAX;
X            cmd_list(cmd) = ARGDESCNULL;
X            continue;
X         }
X
X         flags = arg_flags(ad);
X         if ( ARG_isGIVEN(ad) )
X            BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD );
X
X         BSET( arg_flags(ad), ARGKEYWORD );
X
X         if( ARG_isMULTIVAL(ad) ) {
X            cmd_list(cmd) = ad;
X         }
X         else {
X            cmd_list(cmd) = ARGDESCNULL;
X         }
X
X            /* if usage - just print usage and exit */
X         if ( arg_type(ad) == argUsage ) {
X            Usage_Requested = TRUE;
X            usage(argd);
X            exit(1);
X         }
X
X            /* ARGNOVALs are special, having no value */
X         if ( ! ARG_isVALTAKEN(ad) ) {
X            ad_okay = HANDLE(ad, s, cmd_flags(cmd));
X            if ( !ad_okay ) {
X               arg_flags(ad) = flags;
X               parse_error = pe_SYNTAX;
X            }
X            else {
X               BSET( arg_flags(ad), ARGGIVEN );
X               ad = ARGDESCNULL;
X            }
X            continue;
X         }/*if ARGNOVAL*/
X
X            /* now get the real value */
X         if (!s) {
X            if ( isUNIXISH )  s = *av++;
X            if ( !isUNIXISH  ||  !s  ||  isOPT(s)  ||  isKWD(s) ) {
X               if ( ARG_isVALOPTIONAL(ad) ) {
X                  BSET( arg_flags(ad), ARGGIVEN );
X               }
X               else {
X                  (VOID) get_keyword( arg_sname(ad), name );
X                  usrerr("%c%s switch requires an argument", KwdPrefix, name);
X                  arg_flags(ad) = flags;
X                  parse_error = pe_SYNTAX;
X               }
X
X               if ( isUNIXISH )  av--;
X               continue;
X            }/*if arg*/
X            if ( isUNIXISH )  BSET( arg_flags(ad), ARGVALSEP );
X         }/*if empty*/
X
X            /* try to convert the type */
X         ad_okay = HANDLE(ad, s, cmd_flags(cmd));
X         if ( !ad_okay ) {
X            arg_flags(ad) = flags;
X            parse_error = pe_SYNTAX;
X         }
X         else {
X            BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
X         }
X
X         continue;
X      }/*if keyword*/
X      else if ( isOPT(p) ) {
X         p++;  /* skip over option prefix */
X
MATCHOPT:
X            /* check for `--' to end flags */
X         if ( *p == OptPrefix  &&  !*(p+1) ) {
X            BSET( cmd_state(cmd), ps_NOFLAGS );
X            cmd_list(cmd) = ARGDESCNULL;
X            continue;
X         }
X
X            /* flag argument */
X         while (*p) {
X
X               /* find the flag in the list */
X            is_match = FALSE;
X            for (args = argd; args  &&  !is_match ; args = cmd_defargs(args)) {
X               for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
X                  register char c1 = arg_cname(ad);
X                  register char c2 = *p;
X
X                  if ( arg_type(ad) == argDummy )   continue;
X                  if ( ARG_isPOSONLY(ad) )   continue;
X
X                  if ( BTEST(cmd_flags(cmd), pa_ANYCASE) ) {
X                     c1 = TOUPPER( c1 );
X                     c2 = TOUPPER( c2 );
X                  }/*if*/
X
X                  if ( c1 == c2 ) {
X                     is_match = TRUE;
X                     break;
X                  }/*if*/
X               }
X            }
X            if ( !is_match ) {
X                  usrerr("%c%c switch unknown", OptPrefix, *p++);
X                  parse_error = pe_SYNTAX;
X                  cmd_list(cmd) = ARGDESCNULL;
X                  if ( !isUNIXISH  &&  *p == *s_ARG_SEP )  p += strlen(p);
X                  if ( !isUNIXISH  &&  *p == OptPrefix )  ++p;
X                  continue;
X            }/* if unknown-option */
X
X            flags = arg_flags(ad);
X            if ( ARG_isGIVEN(ad) )
X               BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD );
X
X            if ( ARG_isMULTIVAL(ad) ) {
X               cmd_list(cmd) = ad;
X            }
X            else {
X               cmd_list(cmd) = ARGDESCNULL;
X            }
X
X               /* move p up to point to the (possible) value */
X            p++;
X            if ( !isUNIXISH  &&  *p  &&  strchr(s_ARG_SEP, *p) )  ++p;
X
X            /* if usage - just print usage and exit */
X            if (arg_type(ad) == argUsage) {
X               Usage_Requested = TRUE;
X               usage(argd);
X               exit(1);
X            }
X
X               /* ARGNOVALs are special, having no value */
X            if (! ARG_isVALTAKEN(ad)) {
X               ad_okay = HANDLE(ad, p, cmd_flags(cmd));
X
X               if ( !ad_okay ) {
X                  arg_flags(ad) = flags;
X                  parse_error = pe_SYNTAX;
X               }/*if*/
X               else {
X                  BSET( arg_flags(ad), ARGGIVEN );
X                  ad = ARGDESCNULL;
X                  if ( ad_okay < 0 )  p -= ad_okay;
X               }/*else*/
X
X               if ( !isUNIXISH  &&  *p == OptPrefix )  ++p;
X               continue;
X            }/*if*/
X
X               /* now get the real value */
X            if ( !(*p) ) {
X               if ( isUNIXISH )  p = *av++;
X               if ( !isUNIXISH  ||  !p  ||  isOPT(p)  ||  isKWD(p) ) {
X                  if ( ARG_isVALOPTIONAL(ad) ) {
X                     BSET( arg_flags(ad), ARGGIVEN );
X                  }
X                  else {
X                     (VOID) get_name(arg_sname(ad), name);
X                     usrerr( "%s required for %c%c flag",
X                             name, OptPrefix, arg_cname(ad) );
X                     arg_flags(ad) = flags;
X                     parse_error = pe_SYNTAX;
X                  }/*else*/
X
X                  if ( isUNIXISH )  av--;
X                  break;
X               }/*if arg*/
X               if ( isUNIXISH )  BSET( arg_flags(ad), ARGVALSEP );
X            }/*if empty*/
X
X               /* try to convert the type */
X            ad_okay = HANDLE(ad, p, cmd_flags(cmd));
X            if ( !ad_okay ) {
X               arg_flags(ad) = flags;
X               parse_error = pe_SYNTAX;
X               p += strlen(p);
X            }/*if*/
X            else {
X               BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
X               if ( isUNIXISH  &&  ad_okay < 0  &&  !ARG_isVALSEPARATE(ad) ) {
X                  p -= ad_okay;
X               }
X               else {
X                  p += strlen(p);
X               }
X            }/*else*/
X
X            if ( !isUNIXISH  &&  *p == OptPrefix )  ++p;
X         }/*while*/
X      }/*elif option*/
X      else {
X            /* parsing a list of arguments */
X         if( cmd_list(cmd) ) {
X            ad = cmd_list(cmd);
X            flags = arg_flags(ad);
X            if ( ARG_isGIVEN(ad) )
X               BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD );
X
X            BSET( arg_flags(ad), ARGVALSEP );
X
X            ad_okay = HANDLE(ad, p, cmd_flags(cmd));
X            if ( !ad_okay ) {
X               arg_flags(ad) = flags;
X               parse_error = pe_SYNTAX;
X            }
X
X            continue;
X         }
X            /* positional argument */
X         is_match = FALSE;
X         for (args = argd; args  &&  !is_match ; args = cmd_defargs(args)) {
X            for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
X               if (arg_type(ad) == argDummy)  continue;
X
X               if ( ARG_isPOSITIONAL(ad)  &&
X                    (!ARG_isGIVEN(ad) ||  ARG_isMULTIVAL(ad)) ) {
X                  is_match = TRUE;
X                  break;
X               }/*if*/
X            }
X         }
X
X         if ( !is_match ) {
X            usrerr("too any arguments");
X            parse_error = pe_SYNTAX;
X            continue;
X         }
X
X         flags = arg_flags(ad);
X         if ( ARG_isGIVEN(ad) )
X            BCLEAR( arg_flags(ad), ARGVALSEP | ARGVALGIVEN | ARGKEYWORD );
X
X         if ( ARG_isMULTIVAL(ad) ) {
X            cmd_list(cmd) = ad;
X         }
X
X         if ( BTEST(cmd_flags(cmd), pa_FLAGS1ST) ) {
X            BSET( cmd_state(cmd), ps_NOFLAGS );
X         }
X
X         BSET( arg_flags(ad), ARGVALSEP );
X
X            /* try to convert */
X         ad_okay = HANDLE(ad, p, cmd_flags(cmd));
X         if ( !ad_okay ) {
X            arg_flags(ad) = flags;
X            parse_error = pe_SYNTAX;
X         }
X         else {
X            BSET( arg_flags(ad), ARGGIVEN | ARGVALGIVEN );
X         }
X      }/*else*/
X   }/*while*/
X
X   return  parse_error;
}
X
X
/***************************************************************************
** ^FUNCTION: fmtarg - format command-argument syntax
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static int fmtarg( ad, buf, usgflags )
/*
** ^PARAMETERS:
*/
X   ARGDESC *ad;
/*    -- pointer to the argument to format
*/
X   char *buf;
/*    -- character buffer to hold the formatted result
*/
X   argMask_t usgflags;
/*    -- set of bitmasks corresponding to the value of the user's USAGECNTL
**       environment variable
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Fmtarg will determine the proper command-line syntax for the
**    given argument and write the result to the given buffer.
**
** ^REQUIREMENTS:
**    buf must be large enough to hold the formatted result (100 characters
**    should do the trick).
**
** ^SIDE-EFECTS:
**    buf is overwritten.
**
** ^RETURN-VALUE:
**    The number of printable characters in the argument-syntax-string
**
** ^ALGORITHM:
**    Print argument usage based on whether or not the argument is
**    positional, hidden, multi-valued (list or vector), etc ....
**    Optional arguments and values are enclosed in square braces.
**
**    Any syntax biases reflected in usgflags will be used.
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static int fmtarg( const ARGDESC *ad, char *buf, argMask_t usgflags )
#endif
{
X   /* buf must already be large enough */
X   char *pos;
X   argName_t   name, keyword;
X
X   (VOID) get_name( arg_sname(ad), name );
X
X   if (ARG_isPOSITIONAL(ad)) {
X      sprintf( buf, "<%s>", name );
X   }
X   else {
X      (VOID) get_keyword( arg_sname(ad), keyword );
X
X      if ( isupper(arg_cname(ad))  &&  toupper(*keyword) == arg_cname(ad) ) {
X         *keyword = toupper(*keyword);
X      }
X
X      if ( !(usgflags & usg_LONGOPTS) ) {
X         sprintf( buf, "%c%c", OptPrefix, arg_cname(ad) );
X      }
X      else if ( !(usgflags & usg_OPTS) ) {
X         sprintf( buf, "%c%s", KwdPrefix, keyword );
X      }
X      else  {  /* use both */
X         if ( OptPrefix == KwdPrefix  &&  *keyword == arg_cname(ad) ) {
X            if ( !*(keyword+1) )
X               sprintf( buf, "%c%c", OptPrefix, arg_cname(ad) );
X            else
X               sprintf( buf, "%c%c[%s]", OptPrefix, arg_cname(ad), keyword+1 );
X         }
X         else {
X            sprintf( buf, "%c%c|%c%s", OptPrefix, arg_cname(ad),
X                                       KwdPrefix, keyword );
X         }
X      }
X
X      pos = buf + strlen(buf);
X
X      if ( ARG_isVALTAKEN(ad)  &&  !ARG_isBOOLEAN(ad)  &&  !ARG_isPSEUDOARG(ad) ) {
X         if ( isUNIXISH )  *(pos++) = ' ';
X         if ( ARG_isVALOPTIONAL(ad) )  *(pos++) = '[';
X         if ( !isUNIXISH )  *(pos++) = *s_ARG_SEP;
X         sprintf( pos, "<%s>", name );
X         if ( ARG_isVALOPTIONAL(ad) )  strcat(pos, "]");
X      }/*if*/
X   }/*else*/
X
X   return  strlen(buf);
}
X
X
/***************************************************************************
** ^FUNCTION: ibm_usage - print a usage message
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   VOID ibm_usage( argd, usage_flags )
/*
** ^PARAMETERS:
*/
X   ARGDESC *argd;
/*    -- the command-descriptor array
*/
X   argMask_t usage_flags;
/*    -- flags set by $USAGECNTL
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Ibm_usage will print the Unix command-line usage of the given
**    command on standard diagnostic output (stderr). The content of the
**    usage message is controlled by the bitmasks in usage_flags which
**    correspond to the settings in the user's USAGECNTL variable.
**
** ^REQUIREMENTS:
**    argd should be a non-null command-line argument-descriptor array
**
** ^SIDE-EFECTS:
**    Prints on stderr.
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**    - if no usage is desired then exit
**    - if paging is requested print to the pager instead of stderr
**    - print the command-line syntax
**    - if the description is requested print it
**    - if verbose mode is requested, print the description of each argument
***^^**********************************************************************/
#ifdef __ANSI_C__
X   void ibm_usage( const ARGDESC *argd, argMask_t usage_flags )
#endif
{
X   register CONST ARGDESC  *ad, *args, *cmd;
X   int  max_cols = 80, max_lines  = 24;
X   int  ll, margin, options, longest, positionals;
X   BOOL first = TRUE;
X   FILE *fp;
X
X   if ( !argd )  return;
X
X      /* initialize command-structure */
X   if ( !CMD_isINIT(argd) )  init_args( (ARGDESC *)argd );
X   cmd = argd;
X
X      /* force verbose-mode if requested */
X   if ( Usage_Requested )   BSET( usage_flags, usg_VERBOSE );
X
X   if ( BTEST(usage_flags, usg_NONE) )  return;
X
X   fp = ( BTEST(usage_flags, usg_PAGED) )
X      ? pgopen( stderr, getenv("USAGE_PAGER") )
X      : stderr;
X
X      /* get screen size */
X   get_winsize( fileno(fp), &max_lines, &max_cols );
X
X   fprintf(fp, "Usage: %s", ProgName);
X
X   ll = strlen( ProgName ) + 7;
X   margin = ll + 1;
X   longest = 0;
X
X      /* print Synopsis */
X   for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
X      for ( args = argd ; args ; args = cmd_defargs(args) ) {
X         for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
X            argName_t  buf;
X            int pl;
X
X               /* don't display hidden arguments */
X            if ( ARG_isHIDDEN(ad) )  continue;
X            if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
X            if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
X
X               /* figure out how wide this parameter is (for printing) */
X            pl = fmtarg(ad, buf, usage_flags);
X
X            if ( pl > longest)  longest = pl;
X
X            if  ( ARG_isMULTIVAL(ad) ) {
X               strcat( buf, "..." );
X               pl += 3;
X            }
X            if ( !ARG_isREQUIRED(ad) ) {
X               pl += 2;
X            }
X
X            /* see if this will fit */
X            if ( (ll + pl + 1) > (max_cols - first) ) {
X                  /* no... start a new line */
X               fprintf(fp, "\n%*s", margin, "");
X               ll = margin;
X            }
X            else {
X                  /* yes... just throw in a space */
X               fputc(' ', fp);
X               ++ll;
X            }
X            ll += pl;
X
X               /* show the argument */
X            if ( !ARG_isREQUIRED(ad) )  fputc('[', fp);
X            fprintf(fp, buf);
X            if ( !ARG_isREQUIRED(ad) )  fputc(']', fp);
X
X            first = FALSE;  /* not first line anymore */
X         }/*for each ad */
X      }/* for each argd */
X   }/* for each parm-type */
X
X   fputc('\n', fp);
X
X   if ( BTEST(usage_flags, usg_DESCRIPTION) ) {
X      CONST char *description = cmd_description(cmd);
X
X      if ( description  &&  *description ) {
X         fprintf( fp, "Description:\n" );
X         indent_para(fp, max_cols, 8, "", 0, description);
X         fputc( '\n', fp );
X      }
X   }/*if*/
X
X   if ( !BTEST(usage_flags, usg_VERBOSE) )  {
X      if ( pgactive(fp) )  (VOID) pgclose( fp );
X      return;
X   }
X
X   options = 0;
X
X      /* print Argument descriptions */
X   for ( positionals = 0 ; positionals < 2 ; positionals++ ) {
X      for ( args = argd ; args ; args = cmd_defargs(args) ) {
X         for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
X            argName_t  buf;
X
X               /* don't display hidden arguments */
X            if ( ARG_isHIDDEN(ad) )  continue;
X            if ( !positionals  &&  ARG_isPOSITIONAL(ad) )  continue;
X            if ( positionals  &&  !ARG_isPOSITIONAL(ad) )  continue;
X
X            if ( !options++ )   fprintf(fp, "Options/Arguments:\n");
X            fmtarg(ad, buf, usage_flags);
X            indent_para( fp, max_cols, 8, buf, longest+2, arg_description(ad) );
X         }/*for each ad */
X      }/* for each argd */
X   }/* for each parm-type */
X
X   if ( pgactive(fp) )  (VOID) pgclose( fp );
}
X
SHAR_EOF
echo 'File parseargs/ibm_args.c is complete' &&
chmod 0664 parseargs/ibm_args.c ||
echo 'restore of parseargs/ibm_args.c failed'
Wc_c="`wc -c < 'parseargs/ibm_args.c'`"
test 23858 -eq "$Wc_c" ||
	echo 'parseargs/ibm_args.c: original size 23858, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= parseargs/parseargs.awk ==============
if test -f 'parseargs/parseargs.awk' -a X"$1" != X"-c"; then
	echo 'x - skipping parseargs/parseargs.awk (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting parseargs/parseargs.awk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'parseargs/parseargs.awk' &&
#!/usr/bin/awk -f
X
##########################################################################
## ^FILE: parseargs.awk - parseargs for awk programs
##
## ^DESCRIPTION:
##    This file defines an awk function named parseargs to parse
##    command-line arguments for awk scripts. It also contains a
##    bare-bones template of what such an awk-script might contain.
##
## ^HISTORY:
##    02/21/91	Brad Appleton	<brad@ssd.csd.harris.com>	Created
###^^#####################################################################
X
X
#########
## ^FUNCTION: parseargs - parse command-line argument vectors
##
## ^SYNOPSIS:
##    parseargs( argc, argv, argd, arr )
##
## ^PARAMETERS:
##    argc -- the number of elements in argv (usually ARGC-1).
##    argv -- the vector of command-line arguments (usually ARGV).
##    argd -- the argument-description string
##    arr  -- the associative array to assign command-line values from
##
## ^DESCRIPTION:
##    Parseargs will invoke parseargs(1) to parse the command-line given
##    in <argv> for the command defined by <argd>.  The resulting values
##    will be assigned to elements of the associative array given by <arr>.
##    Values are assigned using using the syntax: arr [ "argname" ] = value;
##    The exception to this is that if the <argname> is "ARGV" then the global
##    array ARGV is reset to the given array (using tab separated fields).
##
## ^REQUIREMENTS:
##    Any desired initial values for items in <arr> should be assigned BEFORE
##    calling this function (using the syntax: arr[ "argname" ] = initial-val).
##
##    The following global variables may be assigned before calling parseargs:
##
##       PROGNAME -- name of the current awk script (default= ARGV[0])
##       PARSEOPTS -- any extra options to pass toi parseargs() (default="-ul")
##       PARSEINPUT -- input file for parseargs(1) (default=unique-name)
##       PARSEOUTPUT -- output file for parseargs(1) (default=unique-name)
##
## ^SIDE-EFFECTS:
##    The files PARSEINPUT and PARSEOUTPUT are created and then deleted.
##
##    The return value from parseargs(1) will be stored in the global-variable
##    named PARSESTATUS.
##
##    The global variable PARSEARGS will contain the command-line used to
##    invoke parseargs(1).
##
##    ARGV and ARGC may be reset, all other values are (re)set in <arr>.
##
## ^RETURN-VALUE:
##    The exit code returned by parseargs(1).
##
## ^BUGS:
##    Due to the limited ability of awk, scripts using parseargs(1) cannot
##    use short-options (with a  dash '-') because awk will attempt to interpret
##    any such arguments as options to awk and remove them from ARGV (regardless
##    of whether or not they are valid awk-options). Keyword options (with a
##    plus sign '+') may still be used without this difficulty. Dash-options
##    may be successfully processed if they did not first appear on the command
##    to the awk-script, so the full syntax of unix-style options could be 
##    provided in an array other than ARGV.
##
## ^ALGORITHM:
##    - set defaults for PROGNAME, PARSEOPTS, PARSEINPUT, and PARSEOUTPUT.
##    - build the parseargs command (dont forget to quote arguments).
##    - redirect input and output of the parseargs command.
##    - run parseargs(1)
##    - assign the exit-code from parseargs(1) to PARSESTATUS
##    - remove PARSEINPUT
##    - if PARSESTATUS != 0
##      - save RS and FS and reset RS = "" and FS = "\n"
##      - for each record in PARSEOUTPUT
##        - $1 is the argname and $2 is the value
##        - if $1 is "ARGV" reset ARGV and ARGC ($2 is a tab separated array)
##        - else assign arr[ $1 ] = $2
##      end-for
##      - restore RS and FS to previous values
##      - remove PARSEOUTPUT
##      - return PARSESTATUS
###^^####
X
function parseargs(argc, argv, argd, arr) {
X      ## set defaults -- use $$ to get a unique suffix string
X   if ( ! PROGNAME )     PROGNAME = ARGV[0];
X   if ( ! PARSEOPTS )    PARSEOPTS = "-u -l";
X
X   "echo  ${TMP:-/tmp}/parseargs.${$}_"  |  getline TMPFILE;
X   if ( ! PARSEINPUT )   PARSEINPUT = TMPFILE "in";
X   if ( ! PARSEOUTPUT )  PARSEOUTPUT = TMPFILE "out";
X
X      ## build the options and required arguments for parseargs(1)
X   PARSEARGS = sprintf( "parseargs -s awk %s -- '%s'", PARSEOPTS, PROGNAME );
X
X      ## quote each elemnt in argv and append it to the parseargs-command
X   for ( i = 1 ; i <= argc ; i++ ) {
X      arg = argv[i];
X      gsub( /'/, "'\\''", arg );
X      PARSEARGS = PARSEARGS " '" arg "'";
X   }
X
X      ## set up i/o redirection
X   PARSEARGS = PARSEARGS  " <" PARSEINPUT  " >" PARSEOUTPUT;
X   print  argd > PARSEINPUT;
X
X      ## invoke parseargs(1) and save the status
X   PARSESTATUS = system( PARSEARGS );
X   system( "/bin/rm -f " PARSEINPUT );  ## dont need input anymore
X
X      ## if successful status, read the result
X   if ( PARSESTATUS == 0 ) {
X      save_RS = RS; save_FS = FS;
X      RS = ""; FS = "\n";
X      while ( getline  < PARSEOUTPUT  > 0 ) {
X         gsub( /\034/, "\n" );
X         if ( $1 == "ARGV" ) {
X            ARGC = 1 + split( $2, ARGV, "\t" );
X            ARGV[0] = PROGNAME;
X         }
X         else  arr[ $1 ] = $2;
X      }
X      RS = save_RS; FS = save_FS;
X   }
X   system( "/bin/rm -f " PARSEOUTPUT );
X
X   return  PARSESTATUS;
}
X
X
BEGIN {
X  PROGNAME = "test.awk";
X  ARGD = sprintf( "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
X    "'?', ARGHIDDEN, argUsage, NULL,    'Help : print usage and exit'" ,
X    "'S', ARGVALOPT, argStr,   string,  'STRing : optional string arg'" ,
X    "'g', ARGLIST,   argStr,   groups,  'newsGROUPS : groups to test'" ,
X    "'r', ARGOPT,    argInt,   count,   'REPcount : group repeat count'" ,
X    "'d', ARGOPT,    argStr,   dirname, 'DIRectory : working directory'" ,
X    "'x', ARGOPT,    argBool,  xflag,   'Xflag : turn on X-mode'" ,
X    "'y', ARGOPT,    argUBool, yflag,   'Yflag : turn off Y-mode'" ,
X    "'s', ARGOPT,    argChar,  sepch,   'SEPchar : field separator'" ,
X    "'f', ARGLIST,   argStr,   files,   'files : files to process'" ,
X    "' ', ARGREQ,    argStr,   name,    'name : name to use'" ,
X    "' ', ARGLIST,   argStr,   argv,    'argv : any remaining arguments'" ,
X    "ENDOFARGS" );
X
X  Args[ "count" ] = 1;
X  Args[ "dirname" ] = ".";
X  Args[ "sepch" ] = ",";
X  Args[ "yflag" ] = "TRUE";
X
X  rc = parseargs( ARGC-1, ARGV, ARGD, Args );
X  if ( rc != 0 )  exit( rc );
X
X  ## print  the parsed arguments (use defaults if not defined)
X  print "ARGUMENTS:";
X  print "==========";
X
X  for ( i in Args )
X    printf( "Args[\"%s\"] = \"%s\"\n", i, Args[i] );
X
X  argc = split( Args[ "argv" ], argv, "\t" );
X  for ( i = 1 ; i <= argc ; i++ )
X    printf( "argv[%d] = \"%s\"\n", i, argv[i] );
X    
}
SHAR_EOF
chmod 0664 parseargs/parseargs.awk ||
echo 'restore of parseargs/parseargs.awk failed'
Wc_c="`wc -c < 'parseargs/parseargs.awk'`"
test 6648 -eq "$Wc_c" ||
	echo 'parseargs/parseargs.awk: original size 6648, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= parseargs/parseargs.c ==============
if test -f 'parseargs/parseargs.c' -a X"$1" != X"-c"; then
	echo 'x - skipping parseargs/parseargs.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting parseargs/parseargs.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'parseargs/parseargs.c' &&
/*************************************************************************
** ^FILE: parseargs.c - command line interface to parseargs()
**
** ^DESCRIPTION:
**    This file implements the command-line interface to the parseargs
**    library. Under Unix, the user may use parseargs(1) (this program)
**    to parse command-line arguments for shell scripts.  At the present,
**    time, VMS/DCL is not yet supported (nor are MS-DOS Batch-files).
**
**    Given a command name, a vector of string-valued arguments such as that
**    passed to a shell script, and a specification string describing the
**    possible arguments, parseargs matches actual arguments to possible
**    arguments, converts values to the desired type, and diagnoses problems
**    such as missing arguments, extra arguments, and argument values that
**    are syntactically incorrect.  Other behavior such as prompting the
**    user for missing arguments and ignoring as command-line syntax may be
**    specified on the command-line through the use of various options, or
**    through the use of the "PARSECNTL" environment variable.
**
**    Given the command name and the argument specification string,
**       parsearg -U
**    prints a reasonably friendly version of the usage of the
**    calling program on standard diagnostic output. The "verbosity" of
**    the usage message may be controlled through the use of the
**    "USAGECNTL" environment variable.
**
**    Given the command name and the argument specification string,
**       parsearg -M
**    prints a template of the command-syntax on standard output that is
**    suitable for input to nroff or troff using the -man macro package.
**
** ^SEE_ALSO:
**    argtype(3), parseargs(1), parseargs(3), parsecntl(3),
**    parseargs.pl, parseargs.awk
**    test.sh, test.csh, test.ksh, test.rc, test.awk, test.pl
**
** ^BUGS:
**    It does not make sense to use any arguments of type argTBool since
**    parseargs currently has no way of knowing what the initial value of
**    the variable is. For this reason, argTBool is not recognized as a
**    valid argument type (even though it is used by parseargs(3)). By the
**    same token, since the user cannot create their own arguments types on
**    the fly from a shell-script, ARGNOVAL is not recognized as a valid
**    argument flag.
**
**    Commas will not be interpreted properly if any field in the argument
**    specification string contains double quotes that are nested inside of
**    double quotes, or single quotes that are nested inside of single quotes.
**
**    Inside the argument specification string, any repeated string of
**    commas that does not appear inside of double or single quotes will
**    be treated as a single comma.
**
**    Text descriptions for argument entries are automatically formatted in
**    usage messages. Any attempt by the user to include tabs and/or newlines
**    in the description will cause it to be formatted improperly.
**
**    Parseargs cannot properly preserve any newlines in shell variables if
**    the eval command is used to read its output (this is a shortcoming of
**    the eval command, not of parseargs). If the user is concerned about
**    this particular case, then the user should redirect the output from
**    parseargs to a temporary file and use the source command in csh or the
**    dot command (`.') in sh and ksh, to interpret the results; otherwise,
**    newlines will be translated into spaces, or characters following a
**    newline may be lost, in any variables that are set by parseargs.
**
**    Parseargs(1) is subject to the same caveats as parseargs(3).
**    Refer to the CAVEATS section of the parseargs(3) for more information.
**
** ^HISTORY:
**    07/18/90 	Brad Appleton 	<brad@ssd.csd.harris.com> 	Created
***^^**********************************************************************/
X
#include <fcntl.h>
#include <stdio.h>
#include <useful.h>
#include "strfuncs.h"
X
#define PARSEARGS_PRIVATE   /* include private definitions */
#include "parseargs.h"
X
/*************************************************************************
** ^SECTION: RETURN-CODES
**
** Parseargs may return any of the following status-codes:
*/
#define  e_SYSTEM   -1
/*    -- A system error occurred
*/
#define  e_SUCCESS   0
/*    -- No errors, success!!
*/
#define  e_USAGE     1
/*    -- No errors were encountered. A usage-message (or manual-page-template)
**       was explicitly requested (and printed) by the user.
*/
#define  e_SYNTAX    2
/*    -- A syntax error was encountered on the command-line. The error may
**       be in the argument(s) intended for parseargs(1) or in the argument(s)
**       for the invoking shell-script.
*/
#define  e_NOENV     3
/*    -- The user specified that the argument description was to be found in
**       an environment variable, however the environment variable in question
**       is unset or empty.
*/
#define  e_ARGD      4
/*    -- An error was encountered in the string that describes the arguments
**       for the given command.
*/
/**^^**********************************************************************/
X
X   /* default shell variable values for a boolean argument */
static CONST char  Default_StrTrue[]  = "TRUE";
static CONST char  Default_StrFalse[] = "";
X
X      /* define character sets */
static CONST char  WhiteSpace[] = " \t\n\v\r\f\"'";
static CONST char  ArgTableDelims[] = ",";
static CONST char  ArgFlagsDelims[] = "|+ \t\n\v\r\f";
X
X   /* macros to improve readability of string tests */
#define strEQ(s1,s2)     !strcmp(s1, s2)
#define strnEQ(s1,s2,n)  !strncmp(s1, s2, n)
X
#define  BUFFER_SIZE  1024       /* start off with 1k buffer & resize it */
#define  ESCAPED_COMMA  '\001'   /* character to replace commas with */
X
X      /* determine the beginning and end of a struct */
#define  c_BEGIN_STRUCT  '{'
#define  c_END_STRUCT    '}'
X
X      /* determine beginning-of-arg-table string */
#define  s_START_ARGS    "STARTOFARGS"
#define  isSTART_ARGS(s)  strnEQ(s, s_START_ARGS, 5)
X
X      /* determine end-of-arg-table string */
#define  s_END_ARGS    "ENDOFARGS"
#define  isEND_ARGS(s)  strnEQ(s, s_END_ARGS, 3)
X
X      /* define #of entries per arg-descriptor */
#define  NFIELDS  5
X
X
/**************************************************************************
** ^SECTION: SHELLS
**    After the command line has been parsed, parseargs will print on stan-
**    dard output, a script to set the shell variables which correspond to
**    arguments that were present on the command-line.  This script may be
**    evaluated by redirecting it to a file and then executing the file,
**    or by directly evaluating the output from parseargs (under most UNIX
**    shells, this could be done using eval).  If any arguments on the com-
**    mand line contained any special characters that needed to be escaped
**    from the shell, these characters will remain intact (not be evaluated
**    by the shell) in the corresponding shell variable.
**
**    The -s shell option may be used to tell parseargs which shell syntax
**    to use. At present, parseargs only recognizes "sh", "csh", "ksh",
**    "tcsh", "bash", "rc", "awk", and "perl" as valid command interpreters.
**    Awk output is slightly different from that of the other shells in that
**    the actual variable setting are not printed but each line of an
**    associative array is printed (the first field is the array index, the
**    second is the value for that index).  If no shell is specified, then
**    the Bourne shell ("sh") will be assumed.
**
**    If the user wishes to use a value other than "TRUE" for a boolean
**    flag that is true, this may be done using the "-T string" option.
**    The same may also be done for a boolean flag that is false using the
**    "-F string" option.
**
**    Parseargs will only set the values of variables that correspond to
**    arguments that were given on the command line. If a particular argu-
**    ment was not supplied on the command line, then no assignment is made
**    for the corresponding shell variable and it will have the same value
**    that it had before parseargs was invoked. The only exception to this
**    is that if the -u option is specified, then the positional parameters
**    are unset before any shell variable assignments (which may reset the
**    positional parameters) are made.
***^^*********************************************************************/
X
X      /* #defines for possible shell names and corresponding types */
typedef short  shell_t;
#define  BASH  ((shell_t) 0)
#define  TCSH  ((shell_t) 1)
#define  CSH   ((shell_t) 2)
#define  KSH   ((shell_t) 3)
#define  SH    ((shell_t) 4)
#define  RC    ((shell_t) 5)
#define  AWK   ((shell_t) 6)
#define  PERL  ((shell_t) 7)
X
#define  BOURNE_AGAIN_SHELL  "bash"
#define  BOURNE_SHELL        "sh"
#define  KORN_SHELL          "ksh"
#define  C_SHELL             "csh"
#define  RC_SHELL            "rc"
#define  TC_SHELL            "tcsh"
#define  AWK_LANG            "awk"
#define  PERL_LANG           "perl"
X
X   /* structure for shell-specific info */
typedef struct {
X   char  *varname;   /* name of variable containing the positional parameters */
X   char  *setcmd;    /* formatted string (%s is replaced with variable name) */
X   char  *prefix;    /* beginning for variable setting */
X   char  *suffix;    /* ending for variable setting */
X   char  *escape;    /* format to escape chars (%c is the char to escape) */
X   char  *metachars; /* special characters that need to be escaped */
} shell_info;
X
X   /* array of shell info - indexed by the #define for the shell type */
static CONST shell_info  Shell[] = {
X
X   /* BASH : Positional parms in -- ; Assignment Syntax: name="value"; */
X   { "--", "%s=",   "'",   "';\n",   "'\\%c'",   "'" },
X
X   /* TCSH : Positional parms in argv ; Assignment Syntax: set name="value"; */
X   { "argv", "set %s=", "'",   "';\n",  "'\\%c'",   "'" },
X
X   /* CSH : Positional parms in argv ; Assignment Syntax: set name="value"; */
X   { "argv", "set %s=", "'",   "';\n",  "'\\%c'",   "'" },
X
X   /* KSH : Positional parms in -- ; Assignment Syntax: name="value"; */
X   { "--", "%s=",       "'",   "';\n",  "'\\%c'",   "'" },
X
X   /* SH : Positional parms in -- ; Assignment Syntax: name="value"; */
X   { "--", "%s=",       "'",   "';\n",  "'\\%c'",   "'" },
X
X   /* RC : Positional parms in -- ; Assignment Syntax: name="value"; */
X   { "*", "%s=",       "'",   "';\n",  "''",   "'" },
X
X   /* AWK : Positional parms in ARGV; Assignment Syntax: name\nvalue\n\n; */
X   { "ARGV", "%s\n",    "",   "\n\n",  "'\034'",   "\n" },
X
X   /* PERL : Positional parms in ARGV; Assignment Syntax: $name=value\n; */
X   { "ARGV", "$%s = ",  "",   ";\n",  "'.\"%c\".'",   "'" }
};
/*************************************************************************/
X
X   /* define all current arg-vector types */
typedef ARGVEC_T(char *)  strvec_t;
typedef ARGVEC_T(char)    charvec_t;
typedef ARGVEC_T(int)     intvec_t;
typedef ARGVEC_T(short)   shortvec_t;
typedef ARGVEC_T(long)    longvec_t;
typedef ARGVEC_T(float)   floatvec_t;
typedef ARGVEC_T(double)  doublevec_t;
typedef ARGVEC_T(VOID)    genericvec_t;  /* generic vector */
X
X      /* union to hold all possibles values of an argument */
typedef union {
X   BOOL    Bool_val;
X   short   Short_val;
X   int     Int_val;
X   long    Long_val;
X   float   Float_val;
X   double  Double_val;
X   char    Char_val;
X   char   *Str_val;
X   strvec_t      Str_vec;
X   charvec_t     Char_vec;
X   intvec_t      Int_vec;
X   shortvec_t    Short_vec;
X   longvec_t     Long_vec;
X   floatvec_t    Float_vec;
X   doublevec_t   Double_vec;
X   genericvec_t  Vector;
} storage_t;
X
X   /* structure to hold a command-line argument name, value, and fmt-string */
typedef struct {
X   CONST char *name;   /* name of shell variable to use */
X   storage_t   value;  /* storage for value of argument */
} cmdarg_t;
#define CMDARGNULL (cmdarg_t *)NULL
X
EXTERN  int   eprintf   ARGS((const char *, ...));
EXTERN  VOID  syserr    ARGS((const char *, ...));
EXTERN  VOID  usrerr    ARGS((const char *, ...));
EXTERN  char *getenv    ARGS((const char *));
EXTERN  VOID  manpage   ARGS((const ARGDESC *));
EXTERN  VOID  perror    ARGS((const char *));
X
extern  int  errno;  /* system wide error level */
X
/*************************************************************************/
X   /*
X   ** variables that are set via command-line arguments
X   */
static  char   *Cmd_Name;  /* name of this program */
X
static  ARGDESC   *UsrArgd = ARGDESCNULL;       /* users arg-table */
static  cmdarg_t  *UsrVals = CMDARGNULL;        /* variable names & values */
static  int        UsrArgc = 0;                 /* # of arg-table entries */
static  shell_t    UsrSh;                       /* shell indicator */
static  BOOL       UseStdin = TRUE;             /* read argd from stdin */
X
static  char   *ShellName  = CHARNULL;  /* name of user's shell */
static  char   *UsrName    = CHARNULL;  /* name of users program */
static  char   *FieldSep   = " ";       /* field-separators for arrays */
static  strvec_t UsrArgv   = ARGVEC_EMPTY(char *);   /* users args */
static  char   *ArgdString = CHARNULL;  /* argd string (with WhiteSpace) */
static  char   *ArgdEnv    = CHARNULL;  /* environment variable for argd */
static  char   *ArgdFname  = CHARNULL;  /* argd input file */
static  BOOL    Unset      = FALSE;     /* ?unset positional parms? */
static  char   *StrTrue    = CHARNULL;  /* string for TRUE values */
static  char   *StrFalse   = CHARNULL;  /* string for FALSE values */
static  char    OptsOnly   = FALSE;     /* parse options only? */
static  char    KwdsOnly   = FALSE;     /* parse keywords only? */
static  BOOL    ModArr     = FALSE;     /* modify array behavior */
static  BOOL    PrUsage    = FALSE;     /* ?just print usage? */
static  BOOL    PrManual   = FALSE;     /* ?just print manual page(s)? */
static  BOOL    Prompt     = FALSE;     /* ?prompt for missing args? */
static  BOOL    Ignore     = FALSE;     /* ?ignore bad syntax and continue? */
X
/*************************************************************************/
X   /* now we are ready to define the command-line */
static
CMD_OBJECT
X   Args
X
CMD_NAME
X   "parseargs  --  parse command-line arguments in shell scripts"
X
CMD_DESCRIPTION
X   "Given a description of the command-line and the command-line arguments, \
parseargs will parse all command-line arguments, convert them to their \
desired type, and print on standard output, a script of all the resulting \
shell assignment statements."
X
CMD_ARGUMENTS
X   'U', ARGOPT,  argBool,  __ &PrUsage,
X   "usage : just print program usage, dont parse command-line",
X
X   'M', ARGOPT,  argBool,  __ &PrManual,
X   "man1 : just print man1 template, dont parse command-line",
X
X   'T', ARGOPT,  argStr,  __ &StrTrue,
X   "TRUEstr : string to use for TRUE Booleans   (default=\"TRUE\")",
X
X   'F', ARGOPT,  argStr,  __ &StrFalse,
X   "FALSEstr : string to use for FALSE Booleans  (default=\"\")",
X
X   'A', ARGOPT,  argBool,  __ &ModArr,
X   "array : modify the behavior of arrays",
X
X   'S', ARGOPT,  argStr,  __ &FieldSep,
X   "SEParator : field-separator-string used to delimit array elements \
(default=\" \")",
X
X   'a', ARGOPT,  argStr,  __ &ArgdString,
X   "ARGSpec : argument specification string",
X
X   'e', ARGOPT,  argStr,  __ &ArgdEnv,
X   "ENVarname : environment variable containing arg-spec",
X
X   'f', ARGOPT,  argStr,  __ &ArgdFname,
X   "FILEname : read the arg-spec from <filename> (default=stdin)",
X
X   'l', ARGOPT,  argBool,  __ &KwdsOnly,
X   "Long-OPTionS : long-options only - do not parse options",
X
X   'o', ARGOPT,  argBool,  __ &OptsOnly,
X   "OPTionS : options only - do not parse long-options",
X
X   's', ARGOPT,  argStr,  __ &ShellName,
X   "SHell : use <shell> command syntax        (default=\"sh\")",
X
X   'u', ARGOPT,  argBool,  __ &Unset,
X   "unset : unset positional parameters before parsing",
X
X   'p', ARGOPT,  argBool,  __ &Prompt,
X   "prompt : prompt the user for missing required arguments",
X
X   'i', ARGOPT,  argBool,  __ &Ignore,
X   "ignore : ignore bad command-line syntax and continue processing \
(instead of aborting)",
X
X   '-', ARGOPT,  argDummy, __ NULL,
X   "+ : end of options - all remaining arguments are interpreted as \
positional parameters (even if one begins with '-' or '+')",
X
X   ' ', ARGREQ,  argStr, __ &UsrName,
X   "name : name of calling program",
X
X   ' ', ARGOPT|ARGVEC,  argStr,  __ &UsrArgv,
X   "arguments : arguments to calling program",
X
X   END_ARGUMENTS
X
CMD_END
X
X
/***************************************************************************
** ^FUNCTION: cleanup - deallocate all global storage
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static  VOID cleanup()
#endif  /* !__ANSI_C__ */
/*
** ^PARAMETERS:
**    None.
**
** ^DESCRIPTION:
**    Cleanup is used to deallocate any global storage. It is called
**    before exiting.
**
** ^REQUIREMENTS:
**    None.
**
** ^SIDE-EFECTS:
**    Storage associated with all dynamically allocated global-variables
**    is released and set to NULL.
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static void cleanup( void )
#endif
{
X   register ARGDESC *ad;
X   register storage_t val;
X   
X   /* free up any vectors from the command-line */
X   for ( ad = ARG_FIRST(UsrArgd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
X      if ( ! BTEST(arg_flags(ad), ARGVEC) )  continue;
X
X      val = *((storage_t *) arg_valp(ad));
X
X      if      ( arg_type(ad) == argStr )     vecFree( val.Str_vec, char * );
X      else if ( arg_type(ad) == argChar )    vecFree( val.Char_vec, char );
X      else if ( arg_type(ad) == argInt )     vecFree( val.Int_vec, int );
X      else if ( arg_type(ad) == argShort )   vecFree( val.Short_vec, short );
X      else if ( arg_type(ad) == argLong )    vecFree( val.Long_vec, long );
X      else if ( arg_type(ad) == argFloat )   vecFree( val.Float_vec, float );
X      else if ( arg_type(ad) == argDouble )  vecFree( val.Double_vec, double );
X   }
X
X   /* free up tables */
X   vecFree( UsrArgv, char * );
X   if ( UsrArgd ) {
X      free( UsrArgd );
X      UsrArgd = ARGDESCNULL;
X   }
X   if ( UsrVals ) {
X      free( UsrVals );
X      UsrVals = CMDARGNULL;
X   }
X   if ( ArgdFname   &&   !ArgdEnv ) {
X      free( ArgdString );
X      ArgdString = CHARNULL;
X   }
}
X
X
/***************************************************************************
** ^FUNCTION: ckalloc - allocate space, check for success
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static ARBPTR ckalloc( size )
/*
** ^PARAMETERS:
*/
X   size_t  size;
/*    -- the number of bytes to allocate
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Ckalloc will use malloc to attempt to fill the given request 
**    for memory. If The request cannot be met than a message is
**    printed and execution is terminated.
**
** ^REQUIREMENTS:
**    size should be > 0
**
** ^SIDE-EFECTS:
**    Memory is allocated that should later be deallocated using free().
**
** ^RETURN-VALUE:
**    The address of the allocated region.
**
** ^ALGORITHM:
**    - Allocate space, check for success
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static ARBPTR ckalloc( size_t size )
#endif
{
X   ARBPTR  ptr;
X
X   ptr = malloc( size );
X   if ( !ptr ) {
X      eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name );
X      cleanup();
X      if ( errno )  perror( Cmd_Name );
X      exit( e_SYSTEM );
X   }
X
X   return  ptr;
}
X
X
/***************************************************************************
** ^FUNCTION: ckrealloc - reallocate space, check for success
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static ARBPTR ckrealloc( ptr, size )
/*
** ^PARAMETERS:
*/
X   ARBPTR  ptr;
/*    -- address of the region to be expanded/shrunk
*/
X   size_t  size;
/*    -- the number of bytes to allocate
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Ckrealloc will use realloc to attempt to fill the given request 
**    for memory. If The request cannot be met than a message is
**    printed and execution is terminated.
**
** ^REQUIREMENTS:
**    size should be > 0
**
** ^SIDE-EFECTS:
**    Memory is allocated that should later be deallocated using free().
**
** ^RETURN-VALUE:
**    The address of the (re)allocated region (which may have been moved).
**
** ^ALGORITHM:
**    - Reallocate space, check for success
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static ARBPTR ckrealloc( ARBPTR ptr, size_t size )
#endif
{
X   ptr = realloc( ptr, (unsigned int)size );
X   if ( !ptr ) {
X      eprintf( "%s: Fatal Error: out of memory!!", Cmd_Name );
X      cleanup();
X      if ( errno )  perror( Cmd_Name );
X      exit( e_SYSTEM );
X   }
X
X   return  ptr;
}
X
X
/***************************************************************************
** ^FUNCTION: escape_char - (re)map a character
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static VOID escape_char( str, ch, esc )
/*
** ^PARAMETERS:
*/
X   char *str;
/*    -- the string to be translated
*/
X   int ch;
/*    --  the character to be replaced/translated
*/
X   int esc;
/*    -- the replacement character to use
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Escape_char will escape all occurences of a character by replacing
**    it with <esc> if the character appears in double or single quotes.
**
** ^REQUIREMENTS:
**    Both <ch> and <esc> should be non-zero.
**    <str> should be non-null and non-empty.
**
** ^SIDE-EFECTS:
**    Each occurrence in <str> of <ch> within single or double quotes is
**    replaced with <esc>.
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static void escape_char( char *str, int ch, int esc )
#endif
{
X   int   squoted = 0, dquoted = 0;
X
X   for ( ; *str ; str++ ) {
X      if ( *str == '\''  &&  !dquoted )
X         squoted = ~squoted;
X      else if ( *str == '"'  &&  !squoted )
X         dquoted = ~dquoted;
X      else if ( (squoted || dquoted)  &&  *str == ch )
X         *str = esc;
X   }
}
X
X
/***************************************************************************
** ^FUNCTION: restore_char - restore any chars escaped by escape_char()
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static VOID restore_char( str, ch, esc )
/*
** ^PARAMETERS:
*/
X   char *str;
/*    -- the string to be translated
*/
X   int ch;
/*    --  the character to be restored
*/
X   int esc;
/*    -- the replacement character to use to escape the above character.
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Restore_char will attempt to undo the results of a previous call
**    to escape_char by replacing each occurence of <esc> in <str> with <ch>.
**
** ^REQUIREMENTS:
**    <str> should be the victim of a previous escape_char(str, ch, esc) call.
**    Furthermore, <esc> should be a character that occurs only as a result
**    of this call (it should be VERY uncommon).
**
**    It should be noted that escape_char() only replaces characters in quotes
**    whereas this routine replaces all occurrences.
**
** ^SIDE-EFECTS:
**    Each occurrence of <esc> in <str> is replaced with <ch>.
**
** ^RETURN-VALUE:
**    None.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
X   void restore_char( char *str, int ch, int esc )
#endif
{
X   for ( ; *str ; str++ )
X      if ( *str == esc )   *str = ch;
}
X
X
/***************************************************************************
** ^FUNCTION: get_arg_type - return function corresponding to given string
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static argTypePtr_t  get_arg_type( type_str )
/*
** ^PARAMETERS:
*/
X   char *type_str;
/*    -- string corresponding to the name of an existing argXxxx type function.
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Get_arg_type will attempt to match <type_name> against the name of all
**    known argXxxx argumnent translation routines and routine the address of
**    the corresponding function. If no match is found, then an error message
**    is printed and execution is terminated.
**
** ^REQUIREMENTS:
**    type_str should be non-NULL and non-empty
**
** ^SIDE-EFECTS:
**    None.
**
** ^RETURN-VALUE:
**    Address of the corresponding function
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static  argTypePtr_t  get_arg_type( const char *type_str )
#endif
{
X   register  CONST char *str = type_str;
X
X   /* translate all listXxx into argXxx */
X   if ( strnEQ( str, "list", 4 ) )
X      str += 4;
X
X   if ( strnEQ( str, "arg", 3 ) )
X      str += 3;
X
X   if      ( strEQ( str, "Usage" ) )
X      return   argUsage;
X   else if ( strEQ( str, "Dummy" ) )
X      return   argDummy;
X   else if ( strEQ( str, "Bool" ) )
X      return   argBool;
X   else if ( strEQ( str, "SBool" ) )
X      return   argSBool;
X   else if ( strEQ( str, "UBool" ) )
X      return   argUBool;
X   else if ( strEQ( str, "Int" ) )
X      return   argInt;
X   else if ( strEQ( str, "Short" ) )
X      return   argShort;
X   else if ( strEQ( str, "Long" ) )
X      return   argLong;
X   else if ( strEQ( str, "Float" ) )
X      return   argFloat;
X   else if ( strEQ( str, "Double" ) )
X      return   argDouble;
X   else if ( strEQ( str, "Char" ) )
X      return   argChar;
X   else if ( strEQ( str, "Str" ) )
X      return   argStr;
X   else {
X      eprintf( "%s: Fatal Error: invalid argument type '%s'\n",
X               Cmd_Name, type_str );
X      cleanup();
X      exit( e_ARGD );
X   }
}
X
X
/***************************************************************************
** ^FUNCTION: get_arg_flag - return BITMASK corresponding to string
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static argMask_t get_arg_flag( flag_str )
/*
** ^PARAMETERS:
*/
X   char flag_str[];
/*    -- name of an ARGXXXXX argument-flag
*/
#endif  /* !__ANSI_C__ */
X
/* ^DESCRIPTION:
**    Get_arg_flag will attempt to match the given string against the name of
**    all valid argument-flags and return its associated bitmask.  If no match
**    is found, then an error message is printed and execution is terminated.
**
** ^REQUIREMENTS:
**    flag_str should be non-NULL and non-empty
**
** ^SIDE-EFECTS:
**    None.
**
** ^RETURN-VALUE:
**    The bitmask corresponding to named ARGXXXX flag.
**
** ^ALGORITHM:
**    Trivial.
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static argMask_t get_arg_flag( const char flag_str[] )
#endif
{
X   if ( strnEQ( flag_str, "ARG", 3 ) )   {
X      if      ( strEQ( flag_str+3, "OPT" ) )     return   ARGOPT;
X      else if ( strEQ( flag_str+3, "REQ" ) )     return   ARGREQ;
X      else if ( strEQ( flag_str+3, "POS" ) )     return   ARGPOS;
X      else if ( strEQ( flag_str+3, "VALREQ" ) )  return   ARGVALREQ;
X      else if ( strEQ( flag_str+3, "VALOPT" ) )  return   ARGVALOPT;
X      else if ( strEQ( flag_str+3, "HIDDEN" ) )  return   ARGHIDDEN;
X      else if ( strEQ( flag_str+3, "LIST" ) )    return   ARGVEC;
X      else if ( strEQ( flag_str+3, "VEC" ) )     return   ARGVEC;
X      else {
X         eprintf( "%s: Fatal Error: invalid argument flag '%s'\n",
X                  Cmd_Name, flag_str );
X         cleanup();
X         exit( e_ARGD );
X      }
X   }
X   else {
X      eprintf( "%s: Fatal Error: invalid argument flag '%s'\n",
X               Cmd_Name, flag_str );
X      cleanup();
X      exit( e_ARGD );
X   }
}
X
/***************************************************************************
** ^FUNCTION: get_argtable_string - read in the argument-table
**
** ^SYNOPSIS:
*/
#ifndef __ANSI_C__
X   static char *get_argtable_string()
#endif
/*
** ^PARAMETERS:
**    None.
**
** ^DESCRIPTION:
**    Get_argtable_string will read (from standard input if UseStdin is set)
**    the entire argument descriptor table into a string and return its address.
**
**    Execution is terminated if there is an error reading STDIN or if the
**    string is too big to fit into memory.
**
** ^REQUIREMENTS:
**    Standard input should be open for reading and be non-interactive.
**
** ^SIDE-EFECTS:
**    Memory is allocated that should later be deallocated using free.
**
** ^RETURN-VALUE:
**    NULL if STDIN is connected to a terminal (after all,
**    this program is for Non-interactive input)
**
** ^ALGORITHM:
**    - start off with a 1k buffer
**    - open the file (if necessary)
**    - while (not eof)
**      - read 1k bytes (or whatever is left).
**      - increase the buffer size by 1k bytes.
**      end-while
**    - shrink the buffer down to the number of bytes used.
**    - close the file (if we had to open it).
**    - return the buffer address
***^^**********************************************************************/
#ifdef __ANSI_C__
X   static char *get_argtable_string( void )
#endif
{
X   int  isatty  ARGS((int));
X   int  fread   ARGS((char *, int, int, FILE *));
X   FILE *fp;
X   char *buf;
X   register int  nchars = 0;   /* # bytes read */
X   register int  bufsiz = 0;   /* actual buffer-size needed */
X
X   /* open file if necessary */
X   if ( UseStdin )  {
X      if ( isatty(STDIN) ) {
X            /* we wont read the arg-table from a terminal */
X         eprintf( "\
%s: Fatal Error:\n\
\tcannot read arg-descriptor table from stdin\n\
\tif stdin is connected to a terminal\n!",
X                  Cmd_Name );
X         cleanup();
X         exit( e_ARGD );
X      }
X      errno = 0;   /* reset errno if isatty() was not a terminal */
X      fp = stdin;
X   }
X   else {
X      if ( (fp = fopen( ArgdFname, "r")) == FILENULL )   {
X         eprintf( "%s: Fatal error: Unable to open %s for reading\n",
SHAR_EOF
true || echo 'restore of parseargs/parseargs.c failed'
fi
echo 'End of  part 5'
echo 'File parseargs/parseargs.c is continued in part 6'
echo 6 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.