home *** CD-ROM | disk | FTP | other *** search
- From: dan@srs.UUCP
- Newsgroups: comp.sources.misc
- Subject: v02i088: argproc(), a command line parser
- Message-ID: <8804051530.AA28824@rem.srs.com>
- Date: 5 Apr 88 15:30:11 GMT
- Approved: allbery@ncoast.UUCP
-
- comp.sources.misc: Volume 2, Issue 88
- Submitted-By: "A. Nonymous" <dan@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@cs.rochester.edu rochester!srs!dan dan%srs.uucp@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
-