dan@srs.UUCP (04/05/88)
Submitted-By: "A. Nonymous" <dan@srs.UUCP> Archive-Name: argproc 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