home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
EFFO
/
forum7.lzh
/
RICO
/
C
/
LIBSOURCE
/
ARGPROC
/
argproc.c
next >
Wrap
Text File
|
2009-11-06
|
17KB
|
589 lines
/*--------------------------------------------------------------------------
This module defines argproc(), a simple-to-use command line option grabber.
It was written by Steve Colwell @ srs.uucp.
--------------------------------------------------------------------------*/
#include <stdio.h>
#include <ctype.h>
#ifndef os9
#include <string.h>
#else
#include <strings.h>
#define void int
#define strchr(x,y) index(x,y)
/* strspn(s,w) - return number of leading white chars (in w) of s */
int strspn(s,w)
char *s,*w;
{
register int i,j;
for(i=0;;i++)
{
if (s[i] == '\0')
return (i);
else
for(j=0;;j++)
{
if (w[j] == '\0')
return (i);
else if (s[i] == w[j])
break;
}
}
}
#endif
#include <varargs.h>
#include <boolean.h>
#include <lose.h>
#define ERROR -1
#ifndef os9
/*LINTLIBRARY*/
#ifndef lint
static char Sccs_id[] = "@(#)argproc.c from 1.19 4/15/87 (C) SRS";
#endif
#endif
/* Dummy function declaration; this is lint's idea of a function prototype.
* If you don't use lint, you can safely omit this section.
*/
#ifdef lint
/*VARARGS3*/
long argproc(argc, argv, format)
int argc;
char **argv, *format;
{
/* Dummy function body; make lint think all the args were used. */
*argv[0]= *format; *format = (char) argc; return 0L;
}
#else
#define MAX_ARG_NAME_LEN 20
#define MAX_ARG_FORM_LEN 50
#define MAX_ARGS 200
#define SWITCH 8
#define NORM_ARG 4
#define BOOL_FLAG (SWITCH|1)
#define BOOL_VAR_FLAG (SWITCH|2)
#define VALUE_FLAG (SWITCH|NORM_ARG)
/*----------------------------------------------------------------------
The format string entry structure. Holds different information, depending
on whether the entry is a boolean "-x", and boolean with variable "=x",
a normal argument "%s", a switched argument "-x%s", or a combination "=x%s".
----------------------------------------------------------------------*/
typedef
struct argpType {
/* one of the above defines */
int type;
/* the number of the bit in the returned long which this entry
* corresponds to */
int bitnum;
/* name of the switch for SWITCH types */
char name[MAX_ARG_NAME_LEN];
/* format reading parameters for NORM_ARG types */
/* the scanf format string */
char format[MAX_ARG_FORM_LEN];
/* the number of format commands in format, e.g. %d,%d gives 2 */
int fmt_count;
/* Are the values for the flag optional, or must they be present? */
boolean optional;
/* An index into the pointer array for the processed args recipient;
* used for NORM_ARG and BOOL_VAR_FLAG */
int pnt_index;
}
argp_t;
/*--------------------------------------------------------------------------
Return pointer to first char in s which matches a char from untilstr.
--------------------------------------------------------------------------*/
char *
sindex(s, untilstr)
register char *s, *untilstr;
{
register char c1, c2, *up;
while ((c1= *s++) != '\0') {
up = untilstr;
while ((c2= *up++) != '\0')
if (c1 == c2)
return(s-1);
}
return(s-1);
}
/*--------------------------------------------------------------------------
Copy chars from beg to end-1 into res, add null char.
--------------------------------------------------------------------------*/
static void
be_strcpy(res, beg, end)
register char *res, *beg, *end;
{
while (beg != end)
*res++ = *beg++;
*res = '\0';
}
/*--------------------------------------------------------------------------
Copy s2 to s1 until a char from untilstr occurs, then null
terminate s1 and return a pointer to the terminal char in s2.
--------------------------------------------------------------------------*/
static char *
copy_until(s1, s2, untilstr)
char *s1, *s2, *untilstr;
{
char *olds2;
olds2 = s2;
s2 = sindex(s2, untilstr);
be_strcpy(s1, olds2, s2);
return(s2);
}
/*----------------------------------------------------------------------
Count the number of format specifications.
Ignore literal "%" and dummy field spec "*".
----------------------------------------------------------------------*/
static int
format_count(str)
char *str;
{
int count=0;
str = strchr(str, '%');
while (str++ != NULL) {
if (*str == '%')
str++;
else if (*str != '*')
count++;
str = strchr(str, '%');
}
return count;
}
/*----------------------------------------------------------------------
Process the format word which started with a '%'. The whole word should
look like '%s' or '%*d' or some other scanf format word starting with %.
It may have multiple entries, for instance '%d%s'.
-----------------------------------------------------------------------*/
static char *
normArgParse(form, argp, argpcountp, bitnump, pntcountp)
char *form;
argp_t *argp;
int *argpcountp, *bitnump, *pntcountp;
{
int pos = (*argpcountp)++;
int fmt_count;
/* copy everything, including the first '%' */
form = copy_until(argp[pos].format, form-1, " \t\n");
argp[pos].type = NORM_ARG;
argp[pos].optional = FALSE;
argp[pos].bitnum = *bitnump;
argp[pos].pnt_index = *pntcountp;
argp[pos].fmt_count = fmt_count = format_count(argp[pos].format);
*pntcountp += fmt_count;
*bitnump += fmt_count;
return form;
}
/*----------------------------------------------------------------------
Make a new entry in the form entry table 'form' for a boolean switch. It
may be a boolean variable (according to isBoolVar), and should use the
specified bit number and name. If it is boolVar, a pointer to the user
variables is used up too (pntcountp).
-----------------------------------------------------------------------*/
static void
newBoolEntry(form, bitnum, name, nameLen, isBoolVar, pntcountp)
argp_t *form;
char *name;
int bitnum, nameLen, *pntcountp;
boolean isBoolVar;
{
(void) strncpy(form->name, name, nameLen);
form->name[nameLen] = '\0';
form->bitnum = bitnum;
if (isBoolVar) {
form->type = BOOL_VAR_FLAG;
form->pnt_index = (*pntcountp)++;
} else
form->type = BOOL_FLAG;
}
/*----------------------------------------------------------------------
Process the format word which started with a dash. The whole word should
look like '-xy%s' or '=xy%s' where x is a boolean switch, y is a value switch.
Also handles the case where there are braces around the switch '{-long%d}'
which means that the switch has a long name, and the case of optional values
'-x[%d]', or both '{-longname[%d]}'. The 'form' always points to the char
after the '-' or '=', and 'wasBrace' indicates whether there was a left
brace before that.
-----------------------------------------------------------------------*/
static char *
switchParse(form, argp, argpcountp, bitnump, pntcountp, wasBrace)
char *form;
argp_t *argp;
int *argpcountp, *bitnump, *pntcountp;
boolean wasBrace;
{
char *oldform = form;
int pos = *argpcountp;
boolean leftBracket, isBoolVar;
/* if switch started with '=', will return result in a boolean variable */
isBoolVar = (form[-1] == '=');
form = sindex(form, "}%[ \t\n");
leftBracket = (*form == '[');
if (oldform == form)
Epitaph("argproc: switch must include 1 char flag name(s)");
if (wasBrace) {
/* Use the entire chunk as one long name since this is in braces.
* It may have its type changed, to VALUE for instance if % is the
* next char */
newBoolEntry(&argp[pos++], (*bitnump)++, oldform, form - oldform,
isBoolVar, pntcountp);
} else {
/* Assign the one character switch names to individual array places.
* The type of the last one may be changed, to VALUE for instance
* if the next char is a %. */
while (oldform != form)
newBoolEntry(&argp[pos++], (*bitnump)++, oldform++, 1,
isBoolVar, pntcountp);
}
/* skip the left bracket if there is one */
if (leftBracket)
++form;
/* if there is a %, set up the last switch as a VALUE, not a BOOL. */
if (*form == '%') {
int fmt_count;
--pos;
argp[pos].optional = leftBracket;
argp[pos].type |= VALUE_FLAG;
form = copy_until(argp[pos].format, form,
leftBracket ? "]" : "} \t\n");
/* figure out how many variables-to-be-filled this will require */
argp[pos].fmt_count = fmt_count = format_count(argp[pos].format);
/* show where the first variable-to-be-filled is unless this switch
* also had a boolean variable-to-fill, in which case the position
* of the variables is already set. */
if (!isBoolVar)
argp[pos].pnt_index = *pntcountp;
*pntcountp += fmt_count;
*bitnump += fmt_count;
++pos;
}
if (leftBracket)
if (*form++ != ']')
Epitaph("argproc: missing closing ']' in format string");
if (wasBrace)
if (*form++ != '}')
Epitaph("argproc: missing closing '}' in format string");
*argpcountp = pos;
return form;
}
/*----------------------------------------------------------------------
Analyse the contents of the argproc format string. The result is an
array, one element per switch in the format string. The number of
elements in the array is returned in *argpcountp. The number of variables
needed to receive the values from the string is the return value. That is,
if the string were "%f -x%d -q =y" the return value would be 3: 1 each for
the %f, the %d, and the =y variables.
-----------------------------------------------------------------------*/
static int
format_parse(form, argp, argpcountp)
char *form;
argp_t *argp;
int *argpcountp;
{
int pntcount, bitnum;
*argpcountp = 0;
/* The position in the argument list of variables-to-be-filled. */
pntcount = 0;
/* The output bit number to be affected by the current format entries */
bitnum = 0;
/* skip white space, process the next word of the format string */
for (form += strspn(form, " \t\n"); *form; form += strspn(form, " \t\n")) {
char c;
switch(*form++) {
case '{': /* A multi-character name switch */
if ((c = *form++) != '-' && c != '=')
Epitaph("argproc: brace must start with a switch");
form = switchParse(form, argp, argpcountp, &bitnum, &pntcount,
TRUE);
break;
case '-': /* One or several concatenated switches */
case '=': /* same as '-' but returns boolean result variable */
form = switchParse(form, argp, argpcountp, &bitnum, &pntcount,
FALSE);
break;
case '%': /* A normal scanf type format string argument */
form = normArgParse(form, argp, argpcountp, &bitnum, &pntcount);
break;
default:
Epitaph("argproc: invalid char (%c) in format string", *--form);
}
}
return pntcount;
}
/*----------------------------------------------------------------------
Set the variable corresponding to any BOOL_VAR types in the format string
to an initial value of FALSE; they will be reset to TRUE when the use
of that switch is discovered in the user argument string.
----------------------------------------------------------------------*/
static void
initBoolVar(vars, form, formCnt)
char *vars[];
argp_t *form;
int formCnt;
{
int i;
for (i=0; i<formCnt; ++i)
if ((form[i].type & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
*((boolean *)vars[ form[i].pnt_index ]) = FALSE;
}
/*----------------------------------------------------------------------
Read in up to argp->fmt_count values from indata using sscanf,
return with bits argp->bitnum+x set if the xth parameter was there.
----------------------------------------------------------------------*/
static long
argscanf(indata, argp, ps, errMode)
char *indata;
argp_t *argp;
char **ps;
int errMode;
{
long bits;
int i, howmany, pos;
char *p1, *p2, *p3, *p4;
/* look up the position of the user variable to put the data into; if the
* format entry has a boolean variable too, skip that to get the position
* for the scanf. */
pos = argp->pnt_index;
if ((argp->type & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
++pos;
/* set up the parameters that are needed for the sscanf. */
switch (argp->fmt_count) {
case 4: p4 = ps[pos + 3];
case 3: p3 = ps[pos + 2];
case 2: p2 = ps[pos + 1];
case 1: p1 = ps[pos + 0];
case 0: break;
default:
Epitaph("argproc: can only have 4 variables per argument");
}
howmany = sscanf(indata, argp->format, p1, p2, p3, p4);
/* set the bit in the result for each parameter that was there */
bits = 0;
for (i=0; i<howmany; i++)
bits |= 1 << (argp->bitnum+i);
if (!argp->optional && howmany < 1 && argp->fmt_count > 0) {
/* This error is caused by the user, not by the programmer,
* so let the programmer say whether to abort or not
*/
PrintErr(errMode, "argproc: bad or missing value for flag %s",
argp->name);
return ERROR;
}
return bits;
}
/*----------------------------------------------------------------------
Assign values from the user's switch to the appropriate variables.
'str' is the contents of the switch, starting just after the '-'.
----------------------------------------------------------------------*/
static long
processSwitch(str, form, formCnt, vars, errMode)
char *str, *vars[];
argp_t *form;
int formCnt, errMode;
{
int offset, j, ty;
long found = 0;
/* go through each character of the string looking for multiple switches */
for (offset=0; str[offset] != '\0';) {
/* check each of the format string entries to see if any match */
for (j=0; j<formCnt; j++) {
if ( (form[j].type & SWITCH) && strncmp(str+offset, form[j].name,
strlen(form[j].name))==0) {
/* skip over the name of the switch */
offset += strlen(form[j].name);
/* mark that this switch was found */
found |= 1 << form[j].bitnum;
ty = form[j].type;
if ((ty & BOOL_VAR_FLAG) == BOOL_VAR_FLAG)
/* set the boolean variable to show the line had this
switch */
*((boolean *)vars[ form[j].pnt_index ]) = TRUE;
if ((ty & VALUE_FLAG) == VALUE_FLAG)
/* if VALUE, no more string to examine after argscanf,
so return. */
return found |
(argscanf(str+offset, &form[j], vars, errMode) << 1);
/* don't have to do anything for BOOL_FLAG, since the 'found'
bit was already set by the code before this switch. */
/* go on to any other switches in the string */
break;
}
}
/* if didn't find switch in format list, it's an error */
if (j == formCnt) {
PrintErr(errMode,"argproc: invalid flag -%s", str+offset);
return ERROR;
}
}
return found;
}
/*----------------------------------------------------------------------
Go through the argument list, assigning values to the user's variables
as indicated by the format string breakdown.
----------------------------------------------------------------------*/
static long
processArgs(form, formCnt, vars, argv, argCnt, errMode)
argp_t *form;
char *vars[], *argv[];
int formCnt, argCnt, errMode;
{
long found;
int i, normArgPos;
found = 0;
/* go through the normal arguments in the format string in order as they
* come off the command line. */
normArgPos = 0;
for (i=1; i<argCnt; i++)
/* if argument is a switch... */
if (argv[i][0] == '-' && argv[i][1] != '\0')
found |= processSwitch(argv[i] + 1, form, formCnt, vars, errMode);
else
/* argument is not a switch, must be a NORM_ARG */
/* look for the next NORM_ARG from the format string */
for(; normArgPos < formCnt; ++normArgPos)
if (form[normArgPos].type == NORM_ARG) {
found |= argscanf(argv[i], &form[normArgPos++],
vars, errMode);
break;
}
return found;
}
/*----------------------------------------------------------------------
The actual argument list is argproc(argc, argv, format, vars . . .). The
argc and argv are from the user's main(), the format is a switch describing
string, and the vars are pointers to variables like those passed to scanf
for receiving the values extracted from argv and arranged as indicated in
the format string.
----------------------------------------------------------------------*/
int argproc(va_alist)
va_dcl
{
va_list ap;
char **argv, *form;
char *vars[MAX_ARGS];
int argpcount, varCnt, i, argc, errMode;
argp_t argp[MAX_ARGS];
va_start(&ap);
argc = va_arg(&ap, int);
argv = va_arg(&ap, char **);
form = va_arg(&ap, char *);
if (form == NULL)
return 0;
switch (*form++) {
case 'N': case 'n': errMode = ERR_NOTE; break;
case 'W': case 'w': errMode = ERR_WARN; break;
case 'F': case 'f': errMode = ERR_FATAL; break;
default : --form; errMode = ERR_FATAL; break;
}
/* setup argp with contents of format string, get how many arguments should
* be following the format string */
if ((varCnt = format_parse(form, argp, &argpcount)) > MAX_ARGS)
Epitaph("too many args. (limit %d)", MAX_ARGS);
/* load in the pointers for the rest of the args */
for (i=0; i<varCnt; i++)
vars[i] = va_arg(&ap, char *);
va_end(ap);
/* initialize the boolean variables to FALSE */
initBoolVar(vars, argp, argpcount);
return processArgs(argp, argpcount, vars, argv, argc, errMode);
}
#endif /* "ifndef lint" around real guts of module */