Path: utzoo!utgpu!cs.utexas.edu!sdd.hp.com!news.cs.indiana.edu!nstn.ns.ca!uupsi!ficc!peter From: peter@ficc.ferranti.com (Peter da Silva) Newsgroups: alt.sources Subject: Re: (yet Another test AND a PD getopt) AND symbol table routines Message-ID: Date: 25 May 91 16:24:46 GMT References: <1074@isgtec.UUCP> <3454@travis.csd.harris.com> <1078@isgtec.UUCP> Reply-To: peter@ficc.ferranti.com (Peter da Silva) Organization: Xenix Support, FICC Lines: 367 In article <1078@isgtec.UUCP> robert@isgtec.UUCP writes: > In article <3454@travis.csd.harris.com>, brad@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) []... 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 #include 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@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@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 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 X#include 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?"