(yet Another test AND a PD getopt) AND symbol table routines

Peter da Silva peter at ficc.ferranti.com
Sun May 26 02:24:46 AEST 1991


In article <1078 at isgtec.UUCP> robert at isgtec.UUCP writes:
> In article <3454 at travis.csd.harris.com>, brad at SSD.CSD.HARRIS.COM (Brad Appleton) writes:
> |> getopt is a piece of crap.

> You seem very, uumh, emotional about this.

> |>Take Parseargs for example (which I released).

> Ahhh,  I see why.

Actually, Parseargs was written by Eric Allman at Berkeley. I read his article
in Unix Review about it, and got all excited (I've been very emotional about
getopt myself for many years: it doesn't do enough to make it worth the
hassle of remembering the options. Some of you might remember me flaming
about it at Usenix a few years back). I got a copy of the program, adapted
it to MS-DOS and AmigaOS, and released it as a portable argument parsing
program. It enhances portability by using the native command line syntax
no matter what O/S you're running it on.

Anyway, after I released it Brad got all excited (apparently he had been
bummed out about getopt too) and further enhanced it.

So, he released it because he was emotional about it. Not the other way
around.

> For simple,  ten line test programs,  getopt is a quick way to
> modify the way the program runs.   Besides it pretty standard,
> everybody knows how it works.

Getopt reduces the coding effort over just banging the argv itself by
about 10%. Why bother? Parseargs is a different kind of flying altogether.
I use it all the time... for simple 10-line test programs even. And I get
better programs than you do using getopt. For example:

$ USAGE=3 userrep
userrep: flag -x unknown
Usage: userrep [-q] (quiet) [-g] (GeoTRIM) [-t] (ticks) [<FILES>]...
Options:
	-q (quiet)   Don't print headers
	-g (GeoTRIM) don't display users below geometric mean
	-t (ticks)   Show time in raw ticks
	   FILES     file containing acctusers output

All this is automatically generated by the parseargs program based on
the argument descriptor table.

The code? The program as a whole is pretty boring. I'll just provide the
relevant fragments:

/* process acctusers output file and generate summary statistics */

...

#include <useful.h>
#include <parseargs.h>

BOOL killmeans = 0;
BOOL quiet = 0;
BOOL showticks = 0;
struct arglist *Files = NULL;

ARGDESC	ArgDesc[] =
{
	'q',	ARGOPT,		argBool,	__ &quiet,
		"quiet (Don't print headers)",
	'g',	ARGOPT,		argBool,	__ &killmeans,
		"GeoTRIM (don't display users below geometric mean)",
	't',	ARGOPT,		argBool,	__ &showticks,
		"ticks (Show time in raw ticks)",
	' ',	ARGOPT|ARGLIST,	listStr,	__ &Files,
		"FILES (file containing acctusers output)",
	ENDOFARGS
};

...

main(ac, av)
int ac;
char **av;
{
	parseargs(av, ArgDesc);
	...
}

OBcode: a set of handy little symbol table routines that I also found
useful in this same program. The actual implementation is a little
simplistic, but I haven't found any need to go into balanced binary
trees or anything. The "char *" in symtab.h was supposed to be "void *",
but one of our compilers barfed on that. Docco is minimal (basically,
comments in the symtab.h file and the test program itself), but the code
itself is pretty solid. Tested on System III and System V only, but
runs on 286, 386 and 68000, so there are no promotion or byte order
problems. 

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of shell archive."
# Contents:  symtab.h symtab.c symtest.c
# Wrapped by peter at ficc.uu.net on Sat May 25 11:17:55 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'symtab.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'symtab.h'\"
else
echo shar: Extracting \"'symtab.h'\" \(248 characters\)
sed "s/^X//" >'symtab.h' <<'END_OF_FILE'
Xtypedef char *symtab;
X
Xsymtab table(); /* table(int size, void (*init)(), void (*dispose)()) */
Xchar *lookup(); /* lookup(symtab t, char *name); */
Xvoid traverse(); /* traverse(symtab t, void (*func)()); */
Xvoid dispose(); /* dispose(symtab t); */
END_OF_FILE
if test 248 -ne `wc -c <'symtab.h'`; then
    echo shar: \"'symtab.h'\" unpacked with wrong size!
fi
# end of 'symtab.h'
fi
if test -f 'symtab.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'symtab.c'\"
else
echo shar: Extracting \"'symtab.c'\" \(1960 characters\)
sed "s/^X//" >'symtab.c' <<'END_OF_FILE'
X#include <stdio.h>
X
Xchar *malloc();
X
Xstruct _e {
X	struct _e *left, *right;
X	char *name;
X	char data[1];
X};
X
Xstruct _s {
X	struct _e *tree;
X	int size;
X	void (*init)();
X	void (*dispose)();
X};
X
Xtypedef struct _s *symtab;
X
Xsymtab table(size, init, dispose)
Xint size;
Xvoid (*init)(), (*dispose)();
X{
X	symtab t = (struct _s *) malloc(sizeof (struct _s));
X	if(!t) return 0;
X	t->tree = NULL;
X	t->size = size;
X	t->init = init;
X	t->dispose = dispose;
X	return t;
X}
X
Xstatic struct _e *new_e(t, name)
Xsymtab t;
Xchar *name;
X{
X	struct _e *tmp;
X	tmp = (struct _e *)malloc(sizeof(struct _e) + t->size);
X	if(!tmp) return NULL;
X	tmp->left = tmp->right = NULL;
X	tmp->name = (char *)malloc(strlen(name) + 1);
X	if(!tmp->name) {
X		free(tmp);
X		return NULL;
X	}
X	strcpy(tmp->name, name);
X	if(t->init) (*t->init)(tmp->data);
X	else {
X		/* Replace by bset or memset if you have it */
X		int i;
X		char *s;
X		for(s=tmp->data, i = t->size; i > 0; i--, s++)
X			*s = 0;
X	}
X	return tmp;
X}
X
Xchar *lookup(t, name)
Xsymtab t;
Xchar *name;
X{
X	struct _e *ptr, *parent;
X	int diff;
X
X	ptr = t->tree;
X	parent = NULL;
X	if(!ptr) {
X		ptr = new_e(t, name);
X		if(!ptr) return NULL;
X		t->tree = ptr;
X		return ptr->data;
X	}
X	while(ptr) {
X		diff = strcmp(name, ptr->name);
X		if(diff==0) return ptr->data;
X		parent = ptr;
X		if(diff<0) ptr = ptr->left;
X		else ptr = ptr->right;
X	}
X	/* assert: parent != NULL */
X	ptr = new_e(t, name);
X	if(!ptr) return NULL;
X	if(diff<0) parent->left = ptr;
X	else parent->right = ptr;
X	return ptr->data;
X}
X
Xstatic void dispose_e(t, e)
Xsymtab t;
Xstruct _e *e;
X{
X	if(e->left) dispose_e(t, e->left);
X	if(e->right) dispose_e(t, e->right);
X	free(e->name);
X	if(t->dispose) (*t->dispose)(e->data);
X	free(e);
X}
X
Xvoid dispose(t)
Xsymtab t;
X{
X	if(t->tree) dispose_e(t, t->tree);
X	free(t);
X}
X
Xstatic void traverse_e(e, f)
Xstruct _e *e;
Xvoid (*f)();
X{
X	if(e->left) traverse_e(e->left, f);
X	(*f)(e->name, e->data);
X	if(e->right) traverse_e(e->right, f);
X}
X
Xvoid traverse(t, func)
Xsymtab t;
Xvoid (*func)();
X{
X	if(t->tree) traverse_e(t->tree, func);
X}
END_OF_FILE
if test 1960 -ne `wc -c <'symtab.c'`; then
    echo shar: \"'symtab.c'\" unpacked with wrong size!
fi
# end of 'symtab.c'
fi
if test -f 'symtest.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'symtest.c'\"
else
echo shar: Extracting \"'symtest.c'\" \(1600 characters\)
sed "s/^X//" >'symtest.c' <<'END_OF_FILE'
X/* Test symtab code */
X#include <stdio.h>
X#include <ctype.h>
X#include "symtab.h"
X
Xchar *malloc();
X
Xstruct val {
X	int t;
X	union {
X		int i;
X		char *s;
X	} u;
X};
X
X#define UNKNOWN 0
X#define INT 1
X#define STRING 2
X
Xval_init(v)
Xstruct val *v;
X{
X	v->t = UNKNOWN;
X	printf("Init\n");
X}
X
Xval_dispose(v)
Xstruct val *v;
X{
X	if(v->t == STRING) {
X		printf("Disposing string \"%s\"\n", v->u.s);
X		free(v->u.s);
X	} else if(v->t == INT) {
X		printf("Disposing integer %d\n", v->u.i);
X	} else {
X		printf("Disposing unknown\n");
X	}
X}
X
Xlist(n, v)
Xchar *n;
Xstruct val *v;
X{
X	printf("%s ", n);
X	if(v->t == STRING) {
X		printf("\"%s\"\n", v->u.s);
X	} else if(v->t == INT) {
X		printf("%d\n", v->u.i);
X	} else {
X		printf("unknown\n");
X	}
X}
X
Xmain()
X{
X	char buf[BUFSIZ];
X	symtab valtab;
X	char *s, *name;
X	struct val *v;
X	valtab = table(sizeof(struct val), val_init, val_dispose);
X	if(!valtab) perror("table"), exit(1);
X	while(gets(buf)) {
X		s = buf;
X		while(isspace(*s)) s++;
X		name = s;
X		while(*s && !isspace(*s)) s++;
X		if(*s) {
X			*s = 0;
X			s++;
X		}
X		v = (struct val *)lookup(valtab, name);
X		if(!v) perror("lookup"), exit(1);
X		switch(v->t) {
X			case UNKNOWN:
X				if(!*s)
X					printf("%s Undefined\n", name);
X				else if(isdigit(*s)) {
X					v->t = INT;
X					v->u.i = atoi(s);
X					break;
X				} else {
X					v->u.s = malloc(strlen(s)+1);
X					if(!v->u.s) perror("copy"), exit(1);
X					strcpy(v->u.s, s);
X					v->t = STRING;
X				}
X				break;
X			case INT:
X				printf("%s = %d\n", name, v->u.i);
X				break;
X			case STRING:
X				printf("%s = \"%s\"\n", name, v->u.s);
X				break;
X		}
X	}
X	traverse(valtab, list);
X	dispose(valtab);
X}
END_OF_FILE
if test 1600 -ne `wc -c <'symtest.c'`; then
    echo shar: \"'symtest.c'\" unpacked with wrong size!
fi
# end of 'symtest.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Peter da Silva; Ferranti International Controls Corporation; +1 713 274 5180;
Sugar Land, TX  77487-5012;         `-_-' "Have you hugged your wolf, today?"



More information about the Alt.sources mailing list