v02i088: argproc(), a command line parser
dan at srs.UUCP
dan at srs.UUCP
Wed Apr 6 01:30:11 AEST 1988
Submitted-By: "A. Nonymous" <dan at srs.UUCP>
Archive-Name: argproc
comp.sources.misc: Volume 2, Issue 88
Submitted-By: "A. Nonymous" <dan at srs.UUCP>
Archive-Name: argproc
#!/bin/sh
#
# Recently, there's been discussion on comp.lang.c and comp.unix.wizards
# about what a convenient command line option parser should be like.
# By popular demand, here is our contribution to the cause: argproc().
# It has saved us a lot of programming time; perhaps it will help others.
# It isn't getopt compatible, but you can't have everything.
# Perhaps somebody would like to make it conform to getopt()? Any takers?
#
# Dan Kegel "... earn it anew if thou wouldst possess it." - Goethe: Faust
# srs!dan at cs.rochester.edu rochester!srs!dan dan%srs.uucp at harvard.harvard.edu
#
# shar archiver, delete everything above the #!/bin/sh line
# and run through sh (not csh)
#
echo 'shar: extracting "argproc.doc" (5442 characters)'
# 'argproc.doc' has a checksum of 29484 on BSD and 54780 on System V.
sed 's/^X//' > argproc.doc << 'XXX_EOF_XXX'
Xargproc - process command line arguments
X
XSYNOPSIS
X #include "argproc.h"
X
X long argproc(argc, argv, format [ , pointer ] . . . )
X int argc;
X char *argv[], *format;
X
X Format string contents:
X - introduces one or more switches with single-letter names
X = is same as -, but sets a boolean telling whether each switch
X actually appeared on the command line
X {} surround switches with long names
X %s, %d, %f, %hd, %ld, %lf, etc. may appear alone to indicate
X positional parameters, or after a switch name to indicate
X arguments to that switch. These are processed by sscanf().
X , separates arguments of a single switch
X [] surround optional arguments
X
XNOTICE
X Argproc is a routine written by Steve Colwell at S.R. Systems.
X It is being posted to Usenet to stimulate discussion about
X command line argument parsers, and may be freely used and copied
X for any purpose. This routine has been in daily use for over a year
X now; however, the version being posted was modified to use vsprintf
X instead of _doprnt, and has been tested only lightly.
X
XDESCRIPTION
X This routine provides an easy way to parse command line
X arguments. The main routine passes its argc, argv, and a
X scanf-type format string to argproc, which parses the com-
X mand line for parameters and switches as described below,
X returning the various values in the pointed-to variables.
X It does not alter argv[].
X
X Each entry in the format string has one of the following
X forms:
X
X -XYZ X, Y, and Z are the one-char names of boolean flags.
X
X -X%s %s is a scanf-type format specification, which is used
X only if -X precedes it.
X
X -X[%s] %s is a optional scanf-type format specification, which
X may or may not be supplied after the -X.
X -X[%d,%s] is allowed, but -X%d[,%s] is not supported.
X
X =X X is a boolean flag, a boolean value is put in the associated
X variable. The '=' may be used in place of the '-' in any
X of the above formats.
X
X {=XYZ}
X XYZ is a many-char name for a single boolean flag.
X {-outfile%s}
X 'outfile' is a many-char name for a flag with a string argument.
X
X %s the next argument that doesn't start with - is taken as a %s
X type.
X
X Generally, anywhere a "%d" or "%s" is listed above, ANY scanf-style
X format specifier may appear.
X
X The only way to have a switch with a greater than one char
X name is by using the braces. For example, "{-longname}".
X All other flag names are only one character long. The flags
X are detected anywhere in the argument list, no matter where
X they appear in the format string.
X
X A single argument may contain more than one format command.
X For example, "%d,%d" gets two integers, seperated by a
X comma, from one argument. This form implies two relevant
X bits in the returned bit map as well as two variables in the
X variable list.
X
X A format may use the scanf "%*c" form to throw away an argu-
X ment or a part of an argument. In this case a bit is
X assigned, but no variable is used as there is no value to
X receive. This may be used to get two ints separated by any
X char (ie. "%d%*c%d" to read 5,6 5-6 etc).
X
X The order of switches in the format string doesn't imply
X that the switches must be given in any order on the commandline.
X
XRETURN VALUE
X The return value is -1 for error.
X
X If there was no error, one bit is set for each unit of the
X format string which was successfully parsed.
X This turns out to be difficult to use in practice, especially when
X adding new options; I suggest using the = notation instead.
X (A unit is a flag or argument format command in the format string.
X For example, "-moyx" has four units and "-X%d" has two units.
X The leftmost unit is assigned bit 0, the second unit is bit 1,
X etc. If there are more than 32 "units" in the format string,
X only the first 32 have bits in the return value.
X This is one reason why the equals sign "=x" notation was added.)
X
XEXAMPLES
X Always call lose_title(argv[0]) first thing in main().
X
X boolean x, y, z;
X argproc(argc,argv,"=xy =z", &x, &y, &z);
X
X short a = 5;
X argproc(argc,argv,"-a%hd", &a);
X
X char infile[100];
X static char logfile[100] = "";
X argproc(argc,argv,"{-logfile%s} %s", logfile, infile);
X
X char ofile[100], pfile[100];
X boolean pGiven;
X argproc(argc,argv,"-o%s =p[%s]", ofile, &pGiven, pfile);
X
X See also demo.c.
X
XDIAGNOSTICS
X If the user enters an unknown option, argproc usually prints a message to
X stderr and exits with a status of 1.
X However, if the first character of the format string is 'W',
X argproc prints an error message and returns with a value of -1.
X If the first character of the format string is 'N',
X argproc places an error message in the global ErrString, and returns
X with a value of -1.
X See 'lose.doc'.
X
XBUGS
X Longer switch names must occur before shorter ones to override them;
X that is, the format string "-f {-fc}" will never allow the flag
X -fc to be found, it will just give the error "no flag -c".
X
X Does not support the "--" convention.
X
X Does not allow spaces between an option and its argument
X (e.g. "-o foo" does NOT match the format "-o%s"; "-ofoo" does).
XXX_EOF_XXX
if test 5442 -ne "`wc -c < argproc.doc`"
then
echo 'shar: transmission error on "argproc.doc"'
fi
chk=`sum argproc.doc | awk '{print $1}'`
if test 29484 -ne $chk -a 54780 -ne $chk
then
echo 'shar: checksum error on "argproc.doc"'
fi
echo 'shar: extracting "lose.doc" (1861 characters)'
# 'lose.doc' has a checksum of 29331 on BSD and 17779 on System V.
sed 's/^X//' > lose.doc << 'XXX_EOF_XXX'
Xlose, warn, lose_title - easy routines for printing error messages
X
XSYNOPSIS
X extern char *ErrStr, *ProgTitle;
X
X lose_title(title)
X char *title;
X
X lose(format [ , pointer ] . . . )
X char *format;
X
X warn(format [ , pointer ] . . . )
X char *format;
X
X #include "lose.h" /* for values of errMode */
X
X PrintErr(errMode, format [ , pointer ] . . . )
X int errMode; /* ERR_FATAL, ERR_WARN, or ERR_NOTE */
X char *format;
X
XDESCRIPTION
X These routines provide an easy to use error printing system.
X
X lose_title is called at the beginning of the user's main
X with argv[0] as title. This sets ProgTitle with the name of
X the program. Errors of various severity levels can now be
X processed with calls to lose, warn or PrintErr.
X
X lose prints an error message (described below) and exits the
X program with value 1.
X
X warn prints an error message, sets errno to 0, and returns.
X
X PrintErr performs one of the above depending on the value of
X errMode; ERR_FATAL selects lose, ERR_WARN selects warn, and
X ERR_NOTE just places the error message in ErrStr.
X
XERROR MESSAGE CONSTRUCTION
X The error message is built by calling vsprintf with the given
X parameters; it is then prefixed with the program name.
X
X Then, if errno (see man errno) is non-zero, the appropriate
X error message (see sys_nerr in perror(3)) is appended. Once
X the error message has been constructed, errno is reset to 0.
X
XEXAMPLE
X lose_title(argv[0]);
X if (argc != 2)
X Epitaph("Usage: %s filename", argv[0]);
X ...
X if ((fp=fopen(argv[1], "r")) == NULL)
X lose("Can't open source file %s", argv[1]);
X ...
X if (line is bad)
X Epitaph("Badly formatted line %s at line %d of file %s",
X line, lineNumber, argv[1]);
X
XSEE ALSO
X perror, argproc, vsprintf, varargs
XXX_EOF_XXX
if test 1861 -ne "`wc -c < lose.doc`"
then
echo 'shar: transmission error on "lose.doc"'
fi
chk=`sum lose.doc | awk '{print $1}'`
if test 29331 -ne $chk -a 17779 -ne $chk
then
echo 'shar: checksum error on "lose.doc"'
fi
echo 'shar: extracting "demo.c" (4163 characters)'
# 'demo.c' has a checksum of 42031 on BSD and 5936 on System V.
sed 's/^X//' > demo.c << 'XXX_EOF_XXX'
X/*--------------------------------------------------------------------------
X Demo program for argproc().
X Demonstrates boolean, string, integer, and float argument-getting.
X
X Just about every program written here follows the scheme shown below:
X #define default values
X main() {
X declare all command-line-settable variables and initialize
X call lose_title() to register the program name for error messages
X call argproc() to parse the command line
X if any mistakes, print usage message and abort
X else loop over argv[] and do the real work.
X }
X--------------------------------------------------------------------------*/
X
X#include <stdio.h>
X#include <string.h>
X#include "argproc.h"
X
X/* Default values for variables set by command line options */
X#define DEF_X 32
X#define DEF_PI 3.1445
X#define DEF_S "this is a test"
X
Xmain(argc, argv)
X int argc;
X char **argv;
X{
X /* These variables are set according to the command line */
X boolean b_bool, c_bool, help_bool;
X static char s_string[256] = DEF_S;
X boolean s_string_given; /* TRUE if user gave value for s_string */
X int x = DEF_X;
X double pi = DEF_PI;
X char arg_string[256];
X
X /* Variables */
X int i;
X
X /* Register program name for use in error messages generated by lose() */
X lose_title(argv[0]);
X
X /* Parse command-line options.
X * - and = introduce switches with single-letter names.
X * {-name...} and {=name...} introduce switches with longer names.
X * Switches introduced with = set or clear a corresponding boolean
X * so you can tell if the user gave them; - is only useful for switches
X * that take arguments.
X *
X * Switch names followed by scanf-style format specifiers separated by
X * commas, e.g. "-m%d" or "-m%d,%f" etc., indicate required arguments.
X * If such a switch appears in argv[i], its arguments must also appear
X * in argv[i], separated by commas.
X *
X * Format specifiers surrounded by square brackets, e.g. "=m[%d]", indicate
X * optional arguments; both "... -m " and "... -m546 " are accepted.
X * To tell if the optional argument was given, you must either
X * check to see if its value changed during the call to argproc(), or
X * (yech!) check the bitmask returned by argproc. Checking the bitmask
X * is rather difficult; nowadays, I just ignore argproc's return value.
X */
X argproc(argc, argv, "=bc {=help} =s%s -x%d {-pi%lf} %s",
X &b_bool, &c_bool, &help_bool, &s_string_given, s_string,
X &x, &pi, arg_string);
X
X /* If user didn't give a value for arg_string, or if she gave the
X * -help switch, print a terse usage message.
X * In a real program, this usage message is very helpful for the user
X * who just needs a little reminder about which options do what.
X */
X if (!arg_string[0] || help_bool)
X lose("Usage: %s file ...\n\
XDemonstration program for argproc(). Note that no spaces are allowed\n\
Xbetween a switch and its arguments (i.e. -x3 is okay, -x 3 is not allowed).\n\
XOptions:\n\
X -help Print this message\n\
X -b Set b_bool TRUE\n\
X -c Set c_bool TRUE\n\
X -sSTRING Set string s [default: %s]\n\
X -xINT Set integer x [default: %d]\n\
X -piFLOAT Set double pi [default: %f]",
X argv[0], DEF_S, DEF_X, DEF_PI);
X
X /* argproc() accepts options anywhere on the command line; filenames
X * and switches may be freely intermixed. (C'mon, haven't you wished you
X * could type "ls *.c -l", and have it work?)
X * Therefore, to handle an arbitrary number of filenames, you must
X * step through argv[], and treat each string not beginning with a dash
X * as a filename argument.
X */
X for (i=1; i<argc; i++) {
X if (argv[i][0] != '-') {
X /* Do something with this argument. */
X do_file(argv[i], b_bool, c_bool, s_string_given, s_string, x, pi);
X }
X }
X
X exit(0);
X}
X
X/* Dummy routine to do something with each file argument. */
X
Xdo_file(arg, b, c, sGiven, s, x, pi)
X char *arg;
X boolean b, c, sGiven;
X char *s;
X int x;
X double pi;
X{
X (void) printf("arg=%s, b=%d, c=%d, sGiven=%d, s=%s, x=%d, pi=%f\n",
X arg, b, c, sGiven, s, x, pi);
X}
X
XXX_EOF_XXX
if test 4163 -ne "`wc -c < demo.c`"
then
echo 'shar: transmission error on "demo.c"'
fi
chk=`sum demo.c | awk '{print $1}'`
if test 42031 -ne $chk -a 5936 -ne $chk
then
echo 'shar: checksum error on "demo.c"'
fi
echo 'shar: extracting "boolean.h" (328 characters)'
# 'boolean.h' has a checksum of 19429 on BSD and 21422 on System V.
sed 's/^X//' > boolean.h << 'XXX_EOF_XXX'
X/*--------------------------------------------------------------------------
X Local definitions; peoples' preferences for these are hard to predict...
X--------------------------------------------------------------------------*/
X
Xtypedef short boolean;
X
X#ifndef TRUE
X#define TRUE ((boolean) 1)
X#define FALSE ((boolean) 0)
X#endif
XXX_EOF_XXX
if test 328 -ne "`wc -c < boolean.h`"
then
echo 'shar: transmission error on "boolean.h"'
fi
chk=`sum boolean.h | awk '{print $1}'`
if test 19429 -ne $chk -a 21422 -ne $chk
then
echo 'shar: checksum error on "boolean.h"'
fi
echo 'shar: extracting "Makefile" (119 characters)'
# 'Makefile' has a checksum of 61063 on BSD and 9549 on System V.
sed 's/^X//' > Makefile << 'XXX_EOF_XXX'
X# Makefile for argproc() demo
X
XCFLAGS = -g
Xdemo: demo.o argproc.o lose.o
X cc $(CFLAGS) demo.o argproc.o lose.o -o demo
XXX_EOF_XXX
if test 119 -ne "`wc -c < Makefile`"
then
echo 'shar: transmission error on "Makefile"'
fi
chk=`sum Makefile | awk '{print $1}'`
if test 61063 -ne $chk -a 9549 -ne $chk
then
echo 'shar: checksum error on "Makefile"'
fi
echo 'shar: extracting "argproc.c" (16865 characters)'
# 'argproc.c' has a checksum of 18141 on BSD and 31035 on System V.
sed 's/^X//' > argproc.c << 'XXX_EOF_XXX'
X/*--------------------------------------------------------------------------
X This module defines argproc(), a simple-to-use command line option grabber.
X It was written by Steve Colwell @ srs.uucp.
X--------------------------------------------------------------------------*/
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include <varargs.h>
X#include "boolean.h"
X#include "lose.h"
X
X#define ERROR -1
X
X/*LINTLIBRARY*/
X#ifndef lint
Xstatic char Sccs_id[] = "@(#)argproc.c from 1.19 4/15/87 (C) SRS";
X#endif
X
X/* Dummy function declaration; this is lint's idea of a function prototype.
X * If you don't use lint, you can safely omit this section.
X */
X#ifdef lint
X /*VARARGS3*/
X long argproc(argc, argv, format)
X int argc;
X char **argv, *format;
X {
X /* Dummy function body; make lint think all the args were used. */
X *argv[0]= *format; *format = (char) argc; return 0L;
X }
X#else
X
X#define MAX_ARG_NAME_LEN 20
X#define MAX_ARG_FORM_LEN 50
X#define MAX_ARGS 200
X
X#define SWITCH 8
X#define NORM_ARG 4
X#define BOOL_FLAG (SWITCH|1)
X#define BOOL_VAR_FLAG (SWITCH|2)
X#define VALUE_FLAG (SWITCH|NORM_ARG)
X
X/*----------------------------------------------------------------------
X The format string entry structure. Holds different information, depending
X on whether the entry is a boolean "-x", and boolean with variable "=x",
X a normal argument "%s", a switched argument "-x%s", or a combination "=x%s".
X----------------------------------------------------------------------*/
Xtypedef
X struct argpType {
X /* one of the above defines */
X int type;
X /* the number of the bit in the returned long which this entry
X * corresponds to */
X int bitnum;
X
X /* name of the switch for SWITCH types */
X char name[MAX_ARG_NAME_LEN];
X
X /* format reading parameters for NORM_ARG types */
X /* the scanf format string */
X char format[MAX_ARG_FORM_LEN];
X /* the number of format commands in format, e.g. %d,%d gives 2 */
X int fmt_count;
X /* Are the values for the flag optional, or must they be present? */
X boolean optional;
X
X /* An index into the pointer array for the processed args recipient;
X * used for NORM_ARG and BOOL_VAR_FLAG */
X int pnt_index;
X }
X argp_t;
X
X
X/*--------------------------------------------------------------------------
X Return pointer to first char in s which matches a char from untilstr.
X--------------------------------------------------------------------------*/
Xchar *
Xsindex(s, untilstr)
X register char *s, *untilstr;
X{
X register char c1, c2, *up;
X
X while ((c1= *s++) != '\0') {
X up = untilstr;
X while ((c2= *up++) != '\0')
X if (c1 == c2)
X return(s-1);
X }
X
X return(s-1);
X}
X
X/*--------------------------------------------------------------------------
X Copy chars from beg to end-1 into res, add null char.
X--------------------------------------------------------------------------*/
Xstatic void
Xbe_strcpy(res, beg, end)
X register char *res, *beg, *end;
X{
X while (beg != end)
X *res++ = *beg++;
X *res = '\0';
X}
X
X/*--------------------------------------------------------------------------
X Copy s2 to s1 until a char from untilstr occurs, then null
X terminate s1 and return a pointer to the terminal char in s2.
X--------------------------------------------------------------------------*/
Xstatic char *
Xcopy_until(s1, s2, untilstr)
X char *s1, *s2, *untilstr;
X{
X char *olds2;
X
X olds2 = s2;
X s2 = sindex(s2, untilstr);
X be_strcpy(s1, olds2, s2);
X
X return(s2);
X}
X
X
X/*----------------------------------------------------------------------
X Count the number of format specifications.
X Ignore literal "%" and dummy field spec "*".
X----------------------------------------------------------------------*/
Xstatic int
Xformat_count(str)
X char *str;
X{
X int count=0;
X
X str = strchr(str, '%');
X while (str++ != NULL) {
X if (*str == '%')
X str++;
X else if (*str != '*')
X count++;
X
X str = strchr(str, '%');
X }
X
X return count;
X}
X
X
X/*----------------------------------------------------------------------
X Process the format word which started with a '%'. The whole word should
X look like '%s' or '%*d' or some other scanf format word starting with %.
X It may have multiple entries, for instance '%d%s'.
X-----------------------------------------------------------------------*/
Xstatic char *
XnormArgParse(form, argp, argpcountp, bitnump, pntcountp)
X char *form;
X argp_t *argp;
X int *argpcountp, *bitnump, *pntcountp;
X{
X int pos = (*argpcountp)++;
X int fmt_count;
X
X/* copy everything, including the first '%' */
X form = copy_until(argp[pos].format, form-1, " \t\n");
X
X argp[pos].type = NORM_ARG;
X argp[pos].optional = FALSE;
X argp[pos].bitnum = *bitnump;
X argp[pos].pnt_index = *pntcountp;
X argp[pos].fmt_count = fmt_count = format_count(argp[pos].format);
X
X *pntcountp += fmt_count;
X *bitnump += fmt_count;
X
X return form;
X}
X
X
X
X/*----------------------------------------------------------------------
X Make a new entry in the form entry table 'form' for a boolean switch. It
X may be a boolean variable (according to isBoolVar), and should use the
X specified bit number and name. If it is boolVar, a pointer to the user
X variables is used up too (pntcountp).
X-----------------------------------------------------------------------*/
Xstatic void
XnewBoolEntry(form, bitnum, name, nameLen, isBoolVar, pntcountp)
X argp_t *form;
X char *name;
X int bitnum, nameLen, *pntcountp;
X boolean isBoolVar;
X{
X (void) strncpy(form->name, name, nameLen);
X form->name[nameLen] = '\0';
X
X form->bitnum = bitnum;
X
X if (isBoolVar) {
X form->type = BOOL_VAR_FLAG;
X form->pnt_index = (*pntcountp)++;
X } else
X form->type = BOOL_FLAG;
X}
X
X
X/*----------------------------------------------------------------------
X Process the format word which started with a dash. The whole word should
X look like '-xy%s' or '=xy%s' where x is a boolean switch, y is a value switch.
X Also handles the case where there are braces around the switch '{-long%d}'
X which means that the switch has a long name, and the case of optional values
X '-x[%d]', or both '{-longname[%d]}'. The 'form' always points to the char
X after the '-' or '=', and 'wasBrace' indicates whether there was a left
X brace before that.
X-----------------------------------------------------------------------*/
Xstatic char *
XswitchParse(form, argp, argpcountp, bitnump, pntcountp, wasBrace)
X char *form;
X argp_t *argp;
X int *argpcountp, *bitnump, *pntcountp;
X boolean wasBrace;
X{
X char *oldform = form;
X int pos = *argpcountp;
X boolean leftBracket, isBoolVar;
X
X/* if switch started with '=', will return result in a boolean variable */
X isBoolVar = (form[-1] == '=');
X
X form = sindex(form, "}%[ \t\n");
X leftBracket = (*form == '[');
X
X if (oldform == form)
X Epitaph("argproc: switch must include 1 char flag name(s)");
X
X if (wasBrace) {
X /* Use the entire chunk as one long name since this is in braces.
X * It may have its type changed, to VALUE for instance if % is the
X * next char */
X newBoolEntry(&argp[pos++], (*bitnump)++, oldform, form - oldform,
X isBoolVar, pntcountp);
X } else {
X /* Assign the one character switch names to individual array places.
X * The type of the last one may be changed, to VALUE for instance
X * if the next char is a %. */
X while (oldform != form)
X newBoolEntry(&argp[pos++], (*bitnump)++, oldform++, 1,
X isBoolVar, pntcountp);
X }
X
X/* skip the left bracket if there is one */
X if (leftBracket)
X ++form;
X
X/* if there is a %, set up the last switch as a VALUE, not a BOOL. */
X if (*form == '%') {
X int fmt_count;
X
X --pos;
X argp[pos].optional = leftBracket;
X argp[pos].type |= VALUE_FLAG;
X form = copy_until(argp[pos].format, form,
X leftBracket ? "]" : "} \t\n");
X
X /* figure out how many variables-to-be-filled this will require */
X argp[pos].fmt_count = fmt_count = format_count(argp[pos].format);
X
X /* show where the first variable-to-be-filled is unless this switch
X * also had a boolean variable-to-fill, in which case the position
X * of the variables is already set. */
X if (!isBoolVar)
X argp[pos].pnt_index = *pntcountp;
X *pntcountp += fmt_count;
X
X *bitnump += fmt_count;
X
X ++pos;
X }
X
X if (leftBracket)
X if (*form++ != ']')
X Epitaph("argproc: missing closing ']' in format string");
X
X if (wasBrace)
X if (*form++ != '}')
X Epitaph("argproc: missing closing '}' in format string");
X
X *argpcountp = pos;
X return form;
X}
X
X
X/*----------------------------------------------------------------------
X Analyse the contents of the argproc format string. The result is an
X array, one element per switch in the format string. The number of
X elements in the array is returned in *argpcountp. The number of variables
X needed to receive the values from the string is the return value. That is,
X if the string were "%f -x%d -q =y" the return value would be 3: 1 each for
X the %f, the %d, and the =y variables.
X-----------------------------------------------------------------------*/
Xstatic int
Xformat_parse(form, argp, argpcountp)
X char *form;
X argp_t *argp;
X int *argpcountp;
X{
X int pntcount, bitnum;
X
X *argpcountp = 0;
X
X /* The position in the argument list of variables-to-be-filled. */
X pntcount = 0;
X
X /* The output bit number to be affected by the current format entries */
X bitnum = 0;
X
X/* skip white space, process the next word of the format string */
X for (form += strspn(form, " \t\n"); *form; form += strspn(form, " \t\n")) {
X char c;
X
X switch(*form++) {
X case '{': /* A multi-character name switch */
X if ((c = *form++) != '-' && c != '=')
X Epitaph("argproc: brace must start with a switch");
X
X form = switchParse(form, argp, argpcountp, &bitnum, &pntcount,
X TRUE);
X break;
X case '-': /* One or several concatenated switches */
X case '=': /* same as '-' but returns boolean result variable */
X form = switchParse(form, argp, argpcountp, &bitnum, &pntcount,
X FALSE);
X break;
X case '%': /* A normal scanf type format string argument */
X form = normArgParse(form, argp, argpcountp, &bitnum, &pntcount);
X break;
X default:
X Epitaph("argproc: invalid char (%c) in format string", *--form);
X }
X }
X
X return pntcount;
X}
X
X
X/*----------------------------------------------------------------------
X Set the variable corresponding to any BOOL_VAR types in the format string
X to an initial value of FALSE; they will be reset to TRUE when the use
X of that switch is discovered in the user argument string.
X----------------------------------------------------------------------*/
Xstatic void
XinitBoolVar(vars, form, formCnt)
X char *vars[];
X argp_t *form;
X int formCnt;
X{
X int i;
X
X for (i=0; i<formCnt; ++i)
X if ((form[i].type & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
X *((boolean *)vars[ form[i].pnt_index ]) = FALSE;
X}
X
X
X/*----------------------------------------------------------------------
X Read in up to argp->fmt_count values from indata using sscanf,
X return with bits argp->bitnum+x set if the xth parameter was there.
X----------------------------------------------------------------------*/
Xstatic long
Xargscanf(indata, argp, ps, errMode)
X char *indata;
X argp_t *argp;
X char **ps;
X int errMode;
X{
X long bits;
X int i, howmany, pos;
X char *p1, *p2, *p3, *p4;
X
X/* look up the position of the user variable to put the data into; if the
X * format entry has a boolean variable too, skip that to get the position
X * for the scanf. */
X pos = argp->pnt_index;
X if ((argp->type & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
X ++pos;
X
X/* set up the parameters that are needed for the sscanf. */
X switch (argp->fmt_count) {
X case 4: p4 = ps[pos + 3];
X case 3: p3 = ps[pos + 2];
X case 2: p2 = ps[pos + 1];
X case 1: p1 = ps[pos + 0];
X case 0: break;
X default:
X Epitaph("argproc: can only have 4 variables per argument");
X }
X howmany = sscanf(indata, argp->format, p1, p2, p3, p4);
X
X/* set the bit in the result for each parameter that was there */
X bits = 0;
X for (i=0; i<howmany; i++)
X bits |= 1 << (argp->bitnum+i);
X
X if (!argp->optional && howmany < 1 && argp->fmt_count > 0) {
X /* This error is caused by the user, not by the programmer,
X * so let the programmer say whether to abort or not
X */
X PrintErr(errMode, "argproc: bad or missing value for flag %s",
X argp->name);
X return ERROR;
X }
X
X return bits;
X}
X
X
X/*----------------------------------------------------------------------
X Assign values from the user's switch to the appropriate variables.
X 'str' is the contents of the switch, starting just after the '-'.
X----------------------------------------------------------------------*/
Xstatic long
XprocessSwitch(str, form, formCnt, vars, errMode)
X char *str, *vars[];
X argp_t *form;
X int formCnt, errMode;
X{
X int offset, j, ty;
X long found = 0;
X
X/* go through each character of the string looking for multiple switches */
X for (offset=0; str[offset] != '\0';) {
X /* check each of the format string entries to see if any match */
X for (j=0; j<formCnt; j++) {
X if ( (form[j].type & SWITCH) && strncmp(str+offset, form[j].name,
X strlen(form[j].name))==0) {
X /* skip over the name of the switch */
X offset += strlen(form[j].name);
X
X /* mark that this switch was found */
X found |= 1 << form[j].bitnum;
X
X ty = form[j].type;
X
X if ((ty & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
X /* set the boolean variable to show the line had this
X switch */
X *((boolean *)vars[ form[j].pnt_index ]) = TRUE;
X
X if ((ty & VALUE_FLAG) == VALUE_FLAG)
X /* if VALUE, no more string to examine after argscanf,
X so return. */
X return found |
X (argscanf(str+offset, &form[j], vars, errMode) << 1);
X
X /* don't have to do anything for BOOL_FLAG, since the 'found'
X bit was already set by the code before this switch. */
X
X /* go on to any other switches in the string */
X break;
X }
X }
X
X /* if didn't find switch in format list, it's an error */
X if (j == formCnt) {
X PrintErr(errMode,"argproc: invalid flag -%s", str+offset);
X return ERROR;
X }
X }
X
X return found;
X}
X
X
X/*----------------------------------------------------------------------
X Go through the argument list, assigning values to the user's variables
X as indicated by the format string breakdown.
X----------------------------------------------------------------------*/
Xstatic long
XprocessArgs(form, formCnt, vars, argv, argCnt, errMode)
X argp_t *form;
X char *vars[], *argv[];
X int formCnt, argCnt, errMode;
X{
X long found;
X int i, normArgPos;
X
X found = 0;
X
X /* go through the normal arguments in the format string in order as they
X * come off the command line. */
X normArgPos = 0;
X
X for (i=1; i<argCnt; i++)
X /* if argument is a switch... */
X if (argv[i][0] == '-' && argv[i][1] != '\0')
X found |= processSwitch(argv[i] + 1, form, formCnt, vars, errMode);
X else
X /* argument is not a switch, must be a NORM_ARG */
X /* look for the next NORM_ARG from the format string */
X for(; normArgPos < formCnt; ++normArgPos)
X if (form[normArgPos].type == NORM_ARG) {
X found |= argscanf(argv[i], &form[normArgPos++],
X vars, errMode);
X break;
X }
X
X return found;
X}
X
X
X/*----------------------------------------------------------------------
X The actual argument list is argproc(argc, argv, format, vars . . .). The
X argc and argv are from the user's main(), the format is a switch describing
X string, and the vars are pointers to variables like those passed to scanf
X for receiving the values extracted from argv and arranged as indicated in
X the format string.
X----------------------------------------------------------------------*/
Xint argproc(va_alist)
X va_dcl
X{
X va_list ap;
X char **argv, *form;
X char *vars[MAX_ARGS];
X int argpcount, varCnt, i, argc, errMode;
X argp_t argp[MAX_ARGS];
X
X va_start(ap);
X
X argc = va_arg(ap, int);
X argv = va_arg(ap, char **);
X form = va_arg(ap, char *);
X
X if (form == NULL)
X return 0;
X
X switch (*form++) {
X case 'N': case 'n': errMode = ERR_NOTE; break;
X case 'W': case 'w': errMode = ERR_WARN; break;
X case 'F': case 'f': errMode = ERR_FATAL; break;
X default : --form; errMode = ERR_FATAL; break;
X }
X
X/* setup argp with contents of format string, get how many arguments should
X * be following the format string */
X if ((varCnt = format_parse(form, argp, &argpcount)) > MAX_ARGS)
X Epitaph("too many args. (limit %d)", MAX_ARGS);
X
X/* load in the pointers for the rest of the args */
X for (i=0; i<varCnt; i++)
X vars[i] = va_arg(ap, char *);
X
X va_end(ap);
X
X/* initialize the boolean variables to FALSE */
X initBoolVar(vars, argp, argpcount);
X
X return processArgs(argp, argpcount, vars, argv, argc, errMode);
X}
X
X#endif /* "ifndef lint" around real guts of module */
XXX_EOF_XXX
if test 16865 -ne "`wc -c < argproc.c`"
then
echo 'shar: transmission error on "argproc.c"'
fi
chk=`sum argproc.c | awk '{print $1}'`
if test 18141 -ne $chk -a 31035 -ne $chk
then
echo 'shar: checksum error on "argproc.c"'
fi
echo 'shar: extracting "argproc.h" (99 characters)'
# 'argproc.h' has a checksum of 55178 on BSD and 8130 on System V.
sed 's/^X//' > argproc.h << 'XXX_EOF_XXX'
X/* Include file for users of argproc(). */
X#include "boolean.h"
X#include "lose.h"
X
Xlong argproc();
XXX_EOF_XXX
if test 99 -ne "`wc -c < argproc.h`"
then
echo 'shar: transmission error on "argproc.h"'
fi
chk=`sum argproc.h | awk '{print $1}'`
if test 55178 -ne $chk -a 8130 -ne $chk
then
echo 'shar: checksum error on "argproc.h"'
fi
echo 'shar: extracting "lose.c" (4704 characters)'
# 'lose.c' has a checksum of 43151 on BSD and 15971 on System V.
sed 's/^X//' > lose.c << 'XXX_EOF_XXX'
X/*--------------------------------------------------------------------------
X Routines to build and print error messages.
X Includes: ErrStr, ProgTitle, lose_title(), lose(), warn(), and PrintErr().
X--------------------------------------------------------------------------*/
X
X#include <stdio.h>
X#include <string.h>
X#include <varargs.h>
X#include <errno.h>
X#include "lose.h"
X
X/*LINTLIBRARY*/
X#ifndef lint
Xstatic char Sccs_id[] = "@(#)lose.c from 1.16 5/31/87 (C) SRS";
X#endif
X
X#define MAX_STR_LEN 200
X#define MAX_ERR_STR_LEN 1000
X
X/* Private variables */
Xstatic char title[MAX_STR_LEN] = "program_name";
Xstatic char errorString[MAX_ERR_STR_LEN] = "";
X
X/* Public variables */
Xchar *ProgTitle = title;
Xchar *ErrStr = errorString;
X
X/*--------------------------------------------------------------------------
X Store the title of the program, initialize errno to zero.
X--------------------------------------------------------------------------*/
Xvoid
Xlose_title(name)
X char *name;
X{
X errno = 0;
X ProgTitle[MAX_STR_LEN-1] = '\0';
X (void) strncpy(ProgTitle, name, MAX_STR_LEN-1);
X}
X
X
X/*--------------------------------------------------------------------------
X Build an error message, then maybe print it to stderr and maybe exit,
X as indicated by errMode.
X--------------------------------------------------------------------------*/
X
Xstatic void
X_print_error(errMode, format, ap)
X int errMode;
X char *format;
X va_list ap;
X{
X extern int sys_nerr;
X extern char *sys_errlist[];
X
X /* Print the program name to the buffer */
X (void) sprintf(ErrStr, "%s: ", ProgTitle);
X
X /* Append the message to the buffer */
X vsprintf(ErrStr + strlen(ErrStr), format, ap);
X
X /* Append the system error message, if any */
X if (errno > 0 && errno < sys_nerr) {
X (void) strcat(ErrStr, "-- ");
X (void) strcat(ErrStr, sys_errlist[errno]);
X }
X
X /* Print the message to the console and/or exit, as indicated by errMode */
X switch(errMode) {
X case ERR_FATAL:
X (void) fprintf(stderr, "%s\n", ErrStr);
X exit(1);
X break;
X case ERR_WARN:
X (void) fprintf(stderr, "%s\n", ErrStr);
X break;
X case ERR_NOTE:
X break;
X default:
X (void) fprintf(stderr, "%s\n", ErrStr);
X lose("illegal call to PrintErr with error mode %d\n", errMode);
X break;
X }
X}
X
X/*--------------------------------------------------------------------------
X Lint definitions to make lint shut up...
X you may safely omit this section if you don't use lint.
X--------------------------------------------------------------------------*/
X#ifdef lint
X /*ARGSUSED*/
X /*VARARGS1*/ /* Only check first argument */
X void warn(message) char *message; /* First arg must be char * */
X { message[0]=0; } /* Dummy function body */
X
X /*ARGSUSED*/
X /*VARARGS1*/ /* Only check first argument */
X void lose(message) char *message; /* First arg must be char * */
X { message[0]=0; } /* Dummy function body */
X
X /*ARGSUSED*/
X /*VARARGS1*/ /* Only check first argument */
X void Epitaph(message) char *message;/* First arg must be char * */
X { message[0]=0; } /* Dummy function body */
X
X /*ARGSUSED*/
X /*VARARGS2*/ /* Check first 2 arguments */
X void PrintErr(errMode, message)
X int errMode; /* First arg must be int */
X char *message; /* Second arg must be char * */
X { message[0]=(char)errMode; } /* Dummy function body */
X#else
X
X/*--------------------------------------------------------------------------
X Various methods of calling _print_error():
X
X For nonfatal errors:
X warn(format, ...) -> _print_error(ERR_WARN, format, ...)
X
X For fatal I/O errors (this one is the most useful):
X lose(format, ...) -> _print_error(ERR_FATAL, format, ...)
X
X For fatal non-I/O errors:
X Epitaph(format, ...) -> errno=0, _print_error(ERR_FATAL, format, ...)
X
X For nonfatal errors when you don't want message printed to stderr:
X PrintErr(errMode, format, ...) -> _print_error(errMode, format, ...)
X--------------------------------------------------------------------------*/
X/*VARARGS2*/
Xvoid
XPrintErr(errMode, format, va_alist)
X int errMode;
X char *format;
X va_dcl
X{
X va_list ap;
X va_start(ap);
X _print_error(errMode, format, ap);
X}
X
X/*VARARGS1*/
Xvoid
Xwarn(format, va_alist)
X char *format;
X va_dcl
X{
X va_list ap;
X va_start(ap);
X _print_error(ERR_WARN, format, ap);
X}
X
X/*VARARGS1*/
Xvoid
Xlose(format, va_alist)
X char *format;
X va_dcl
X{
X va_list ap;
X va_start(ap);
X _print_error(ERR_FATAL, format, ap);
X}
X
X/*VARARGS1*/
Xvoid
XEpitaph(format, va_alist)
X char *format;
X va_dcl
X{
X va_list ap;
X va_start(ap);
X errno = 0;
X _print_error(ERR_FATAL, format, ap);
X}
X
X#endif
XXX_EOF_XXX
if test 4704 -ne "`wc -c < lose.c`"
then
echo 'shar: transmission error on "lose.c"'
fi
chk=`sum lose.c | awk '{print $1}'`
if test 43151 -ne $chk -a 15971 -ne $chk
then
echo 'shar: checksum error on "lose.c"'
fi
echo 'shar: extracting "lose.h" (123 characters)'
# 'lose.h' has a checksum of 51399 on BSD and 9611 on System V.
sed 's/^X//' > lose.h << 'XXX_EOF_XXX'
X#define ERR_FATAL 1
X#define ERR_WARN 2
X#define ERR_NOTE 3
X
Xvoid lose_title();
Xvoid lose();
Xvoid warn();
Xvoid PrintErr();
XXX_EOF_XXX
if test 123 -ne "`wc -c < lose.h`"
then
echo 'shar: transmission error on "lose.h"'
fi
chk=`sum lose.h | awk '{print $1}'`
if test 51399 -ne $chk -a 9611 -ne $chk
then
echo 'shar: checksum error on "lose.h"'
fi
echo 'shar: extracting "makeargv.doc" (965 characters)'
# 'makeargv.doc' has a checksum of 52222 on BSD and 13347 on System V.
sed 's/^X//' > makeargv.doc << 'XXX_EOF_XXX'
Xmakeargv - build an argv-style array from a string
X
XSYNOPSIS
X int makeargv(str, argvp)
X char *str;
X char ***argvp;
X
XDESCRIPTION
X makeargv breaks up an ordinary string into a count and array like
X those passed to main() as argc & argv.
X Argv is MALLOC'd in makeargv; to free it, call FREE(argv).
X
X The first arg is placed in argv[0].
X The original string is peppered with null chars to mark the ends of
X the args, and argv[i] is filled with pointers into the original string.
X An extra entry of NULL is included in argv, at argv[argc], for routines
X that use a final NULL for loop termination.
X
XEXAMPLE
X #include "argproc.h"
X char **argv;
X int argc;
X char cmdline[256], arg[256];
X boolean xflag;
X
X gets(cmdline);
X argc = makeargv(cmdline, &argv);
X argproc(argc+1, argv-1, "%s =x", arg, &xflag);
X
XBUGS
X Perhaps this should set argv[0] to the empty string
X to be more compatible with the real argv[].
XXX_EOF_XXX
if test 965 -ne "`wc -c < makeargv.doc`"
then
echo 'shar: transmission error on "makeargv.doc"'
fi
chk=`sum makeargv.doc | awk '{print $1}'`
if test 52222 -ne $chk -a 13347 -ne $chk
then
echo 'shar: checksum error on "makeargv.doc"'
fi
echo 'shar: extracting "makeargv.c" (1193 characters)'
# 'makeargv.c' has a checksum of 37514 on BSD and 18775 on System V.
sed 's/^X//' > makeargv.c << 'XXX_EOF_XXX'
X#include <string.h>
X#include <ctype.h>
X#include "boolean.h"
X
X#ifndef NULL
X#define NULL 0L
X#endif
X
X#define MAX_ARGS 1000
X
X/*----------------------------------------------------------------------
X Given a string, extracts the arguments (separated by white-space)
X and creates an argv type array full of pointers to the input string.
X Modifies the input string.
X Returns the count of arguments.
X----------------------------------------------------------------------*/
Xint
Xmakeargv(s, argvp)
X char *s, ***argvp;
X{
X int cnt=0;
X char *vals[MAX_ARGS]; /* temporary argv until we know argc */
X
X while (TRUE) {
X s += strspn(s, " \t\n");
X if (*s == '\0')
X break;
X
X if (cnt >= MAX_ARGS)
X lose("%s: only %d args allowed to makeargv", MAX_ARGS);
X
X if (*s == '"') {
X /* if the quote is unmatched, just stop processing */
X vals[cnt] = s;
X if ((s = strchr(s, '"')) == NULL)
X break;
X
X ++cnt;
X *s++ = '\0';
X } else {
X vals[cnt++] = s;
X s += strcspn(s, " \n\t");
X if (*s == '\0')
X break;
X else
X *s++ = '\0';
X }
X }
X
X *argvp = malloc(sizeof(char *) * (cnt+1));
X
X vals[cnt] = 0;
X bcopy(vals, *argvp, (cnt+1) * sizeof(char *));
X
X return cnt;
X}
XXX_EOF_XXX
if test 1193 -ne "`wc -c < makeargv.c`"
then
echo 'shar: transmission error on "makeargv.c"'
fi
chk=`sum makeargv.c | awk '{print $1}'`
if test 37514 -ne $chk -a 18775 -ne $chk
then
echo 'shar: checksum error on "makeargv.c"'
fi
More information about the Comp.sources.misc
mailing list