[comp.sys.apollo] Registry propagation delays

goldfish@CONCOUR.CS.CONCORDIA.CA (03/08/91)

Below  are two "shar"  files I have  created  containing a  command line
argument handler and two useful routines for handling password queries.

Apologies to  all if this is not   the  correct  place to  post sources,
however  I received  several  requests  on    this  group  and   several
suggestions on how to write the programs, so I  thought this would be an
appropriate place for the sources.

The routines should work on most-anything  that maintains some semblance
of the UNIX  /etc/passwd paradigm, except that I  have not tested  it on
other platforms ...  :-( The problem with Sun  is that they don't ship a
modern C compiler,   just this  anachronistic throwback to  the days  of
Lunar landings and SIXTEEN BIT mainframes.

I also have not had time to get it running  on  our DECStations, since I
don't remember seeing a debugger which required a  manual in a long time
(I currently  use  DDE and  Turbo  C).   I  will re-post this   bunch to
Comp.Sources or some-such when I get the time to fix it, however it runs
on Apollo Domain-OS 10.2 RIGHT NOW.   so laziness aside,  these programs
will work (until HP's next "foot-hunt")

To  install these programs,  unpack the "shar"  files (there are  two of
them) into separate directories and edit the "Makefile" in each to point
to things in the right  place.   (the "userinfo"  program needs binaries
from the "argtok" package.)

You should be able to   safely  run the  programs with  little  fear  of
damage, since they never write anywhere save stdout and stderr.

The man pages are self explanatory  and give examples  of how to use the
programs.

I don't describe myself as an expert "C" hacker, so  comments as to form
are welcome.

Since most software usually has a disclaimer:

	(-: The author of the software  makes  no assertions :-)
	(-: whatsoever  that the   software or documentation :-)
	(-: contained   herein  performs   according  to its :-)
	(-: stated function, the function for which  its use :-)
	(-: is  intended, or   indeed   any useful  function :-)
	(-: whatsoever.                                      :-)

	(-:I am  not limiting  the use of  this  code in any :-)
	(-: way, or  requesting any  royalties,  however  if :-)
	(-: anyone  has  an   unmarried  Jewish  Princess to :-)
	(-: spare, I would not  complain.  If you want to be :-)
	(-: nice, leave my  name and E-Mail address  in  the :-)
	(-: source code; if  you want to  be nasty, claim it :-)
	(-: was  your idea and  I rewrote it to add  lots of :-)
	(-: bugs                                             :-)

Oh YES! if you really want to thank me, send me source of the program
"Shar", my good version dissappeared and this was generated on a PC.

--	  Paul Goldsmith
<goldfish@concour.cs.concordia.ca>				 (514) 848-3031
	(Shirley Maclaine told me there would be LIFETIMES like this)


*********************   First SHAR file  ***********************
#!/bin/sh
echo extracting Makefile ...
cat >Makefile <<xzyyz
# Makefile for userinfo
# 			-- Goldfish 90/09/14

############################################################
#	Global assignments 
############################################################
CC=cc
#	the preceding assignment points to the "personal"
#	include directory
INCLUDE=$(HOME)/Include 

#	the preceding assignment points to the "personal"
#	library directory
LIBRARY=$(HOME)/Lib

#	This is the directory to which the "production" 
#	binary goes
BIN=/site/bin

#	This is the directory to which the "production" man
#	page goes
MAN=/usr/man/mann

############################################################
#	options and flags
############################################################
COMPILEOPTION=-g -I$(INCLUDE)


############################################################
#	First action performs the complete compile
############################################################
all: userinfo freeunixno

############################################################
#	Compile and link the parts of freeunixno
############################################################
freeunixno: freeunixno.o
	$(CC) $(COMPILEOPTION) freeunixno.o $(LIBRARY)/argtok.o $(LIBRARY)/bstran.o -o freeunixno

freeunixno.o: freeunixno.c
	$(CC) $(COMPILEOPTION) -c freeunixno.c

freeunixno.c: freeunixno.c,v
	co freeunixno.c

############################################################
#	Compile and link the parts of userinfo
############################################################
userinfo: userinfo.o
	$(CC) $(COMPILEOPTION) userinfo.o $(LIBRARY)/argtok.o $(LIBRARY)/bstran.o -o userinfo

userinfo.o: userinfo.c
	$(CC) $(COMPILEOPTION) -c userinfo.c

userinfo.c: userinfo.c,v
	co userinfo.c

############################################################
#	execute the current version for testing purposes
############################################################
test: testuserinfo testfreeunixno

testuserinfo: userinfo
	echo root | ./userinfo root -
	./userinfo -pw ./test.pw -all -f 'Name:\t"%u"\tPassword:\t"%p"\
	User No:\t"%U"\tGroup:\t"%G"\
	GECOS:\
	"%i"\
	\tName: (last)"%l"\t(first)"%f"\tsub-0:"%0"\
	\tOffice:\t"%o"\tsub-1:"%1"\
	\tRoom:\t"%r"\tsub-2:"%2"\
	\tPhone:\t"%t"\tsub-3:"%3"\
	\t\tsub-4:"%4"\
	\t\tsub-5:"%5"\
	\t\tsub-6:"%6"\
	\t\tsub-7:"%7"\
	\t\tsub-8:"%8"\
	\tID:\t"%I"\tsub-9:"%9"\
	Home:\t"%h"\
	Shell:\t"%s"'

testfreeunixno: freeunixno
	./freeunixno -lo0 -hi20 -count 2
#	./freeunixno -lo0 -hi20

############################################################
#	remove re-creatable files
############################################################
clean:
	rm *.o *~ *.BAK *.bak arg

############################################################
# install global files onto appropriate libraries
############################################################
production: userinfoproduction freeunixnoproduction

userinfoproduction: $(BIN)/userinfo $(MAN)/userinfo.n

freeunixnoproduction: $(BIN)/freeunixno $(MAN)/freeunixno.n

############################################################
# install userinfo
############################################################
$(BIN)/userinfo: userinfo userinfo.n
	cp userinfo $(BIN)/userinfo

userinfo.n: userinfo.n,v
	co userinfo.n

$(MAN)/userinfo.n: userinfo.n 
	cp userinfo.n $(MAN)/userinfo.n 

############################################################
# install freeunixno
############################################################
$(BIN)/freeunixno: freeunixno
	cp freeunixno $(BIN)/freeunixno

freeunixno.n: freeunixno.n,v
	co freeunixno.n

$(MAN)/freeunixno.n: freeunixno.n
	cp freeunixno.n $(MAN)/freeunixno.n 

############################################################
#	end of Makefile
############################################################
xzyyz
echo extracting freeunixno.c ...
cat >freeunixno.c <<xzyyz
#include <stdio.h>
#include <math.h>
#include <pwd.h>
#include <grp.h>
#include "argtok.h"
#include "bstran.h"

#define	TRUE	1
#define	FALSE	0

#define	DEFFMT	"%5d\n"
#define	DEFLO	"0"
#define	DEFHI	"32767"
#define	DEFCOUNT	"32767"
#define	USERNOSPACE	32768
#define STARTTICK	16

/* purpose: to return a range of unix numbers
 * 
 * comments: This program is used in place of "grep" calls to locate
 * 
 * Notes:
 * 
 * Author: Paul Goldsmith 90/09/13
 */

char *silent;

main(int argc,
     char *argv[]
)
{
  int i, ihi, ilo, icount;
  struct passwd *pass;

  char *format	= argtok(argc, argv, "-f", DEFFMT);
  char *lo	= argtok(argc, argv, "-lo", DEFLO);
  char *hi	= argtok(argc, argv, "-hi", DEFHI);
  char *count	= argtok(argc, argv, "-count", DEFCOUNT);
  char numberspace[USERNOSPACE];
  char wkfmt[256];
  register int tickcount;

  ilo = atoi(lo);
  ihi = atoi(hi);
  icount = atoi(count);
  silent = argtok(argc, argv, "-s", 0);
  tickcount = (silent ? -1 : STARTTICK);

  while ( pass = getpwent() ) {
    if ( ! tickcount--) {
      fputc ( '.', stderr );
      fflush (stderr);
      tickcount = STARTTICK;
    }
    numberspace[pass->pw_uid]=TRUE;
  }
  if ( ! silent ) fputc ( '\n', stderr );

  if ( strcmp(format, "-") == 0 ) {
    format = wkfmt;
  }
  for ( i = ((ilo>0)?ilo:0) ;
        (i <= ((ihi<USERNOSPACE)?ihi:USERNOSPACE) ) && (icount > 0) ;
        i++ ){
    if ( ! numberspace[i]) {
      if ( format == wkfmt ) {
	if ( gets (wkfmt) ) {
	  printf(bstran(wkfmt), i);
	}else {
	  return;
	}
      }else {
        printf(format,i);
      }
      icount--;
    }
  }
}
xzyyz
echo extracting test.pw ...
cat >test.pw <<xzyyz
Name:Password:UserNo:GroupNo:LASTNAME  firstname, Office, (Room), Phone, fill-4, fill-5, fill-6, fill-7, fill-8, ID Number:/home/path/name:/bin/shell 
xzyyz
echo extracting userinfo.c ...
cat >userinfo.c <<xzyyz
/* $Log:	userinfo.c,v $
 * Revision 1.7  91/01/30  14:22:21  goldfish
 * include a "%H" real home token, tidy up docs, use bstran instead of bschar
 * 
 * Revision 1.6  91/01/18  14:13:18  root
 * *** empty log message ***
 * 
 * Revision 1.5  91/01/18  14:12:15  root
 * remove default trailing newline from format string
 * 
 * Revision 1.4  91/01/17  11:58:59  root
 * finetune main.c
 * 
 * Revision 1.3  91/01/16  16:50:01  root
 * fix "-version flag"
 * fix "-all" flag.
 * reformat error messages.
 * 
 * Revision 1.2  91/01/16  16:22:35  root
 * cosmetic revisions to accomodate RCS;  add a "-version" flag.
 * 
 */
#include <string.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include "argtok.h"
#include "bstran.h"
#include <sys/param.h>

#define	TRUE	1
#define	FALSE	0

#define	DEFFMT	"%u:%p:%U:%G:%i:%h:%s\n"
#define	INVALIDGROUP	"**INVALID**"
#define	MAXUSERNAMELEN	127
#define	MAXFILENAMELEN	127
#define	NULLPWFILE	""
#define VERSION	"$Revision: 1.7 $\n"
/* purpose: to query password registry reliably using unix calls
 * 
 * comments: This program is used in place of "grep" calls to locate
 *	information in the /etc/passwd file.  I employs the (hopefully)
 *	reliable c function call "getpwnam()".  Considerable flexibility is
 *	afforded in interpreting the output.  *
 * 
 * Author: Paul Goldsmith 90/07/11
 */
int chdir();
char *getwd();


char *silent;

char *vraipath ( char *name, char *vrainame)
{ if ( ! chdir(name) ) {
    if ( getwd (vrainame) ) {
      return vrainame  ;
    }else {
      return 0 ;
    }
  }else {
      return 0 ;
  }
};

char bschar( char tc )
{ switch (tc) {
    case 'n'  : return '\n';
    case 't'  : return '\t';
    case 'v'  : return '\v';
    case 'b'  : return '\b';
    case 'r'  : return '\r';
    case 'f'  : return '\f';
    case 'a'  : return '\a';
    case '\\' : return '\\';
    case '?'  : return '\?';
    default   : return tc ;
  }
};

struct gecosfields {
	char *firstname;
	char *lastname;
	char *office;
	char *room;
	char *phone;
	char *spare[5];
	char *id;
};

char *eatwhitespace ( char * str)
{ int length;
  char *begstr = str;
  if ( begstr == 0 ) return 0 ;
  while ( isspace(*begstr) ) { begstr++; };
  length = strlen(begstr);
  while ( --length >= 0 && isspace(begstr[length]) ) { begstr[length] = 0; };
  return begstr;
}

void splitgecos ( char *gecos, struct gecosfields *fields )
{ char *dblspc;
  fields->lastname	= eatwhitespace(strtok(gecos, ","));
  dblspc = fields->lastname;
  while ( (*dblspc != 0 )  &&
          strncmp( "  ", dblspc, 2)  ) {
    dblspc++;
  }
  if ( *dblspc != 0 ) {
    *dblspc = 0;
    dblspc += 2;
  }
  fields->firstname	= eatwhitespace(dblspc);
  fields->office	= eatwhitespace(strtok (0, ","));
  fields->room		= eatwhitespace(strtok (0, ","));
  fields->phone		= eatwhitespace(strtok (0, ","));
  fields->spare[0]	= eatwhitespace(strtok (0, ","));
  fields->spare[1]	= eatwhitespace(strtok (0, ","));
  fields->spare[2]	= eatwhitespace(strtok (0, ","));
  fields->spare[3]	= eatwhitespace(strtok (0, ","));
  fields->spare[4]	= eatwhitespace(strtok (0, ","));
  fields->id		= eatwhitespace(strtok (0, ","));
}

printpasswd (char *format, struct passwd *pass)
{ struct group  *grp = getgrgid(pass->pw_gid);
  struct gecosfields  gecos;
  char scr[256];
 
  splitgecos( strcpy(scr,pass->pw_gecos), &gecos);

  { char *c;
    for (c = format; *c; c++) {
      if (*c == '%') {
        switch (*(++c)) {
          case '%':	putchar('%');			break;
          case 'u':	printf("%s", pass->pw_name);	break;
          case 'p':	printf("%s", pass->pw_passwd);	break;
          case 'U':	printf("%d", pass->pw_uid);	break;
          case 'G':	printf("%d", pass->pw_gid);	break;
          case 'g':	printf("%s", ( (grp) ? (grp->gr_name) : (INVALIDGROUP))

 );
			break;
/*        case 'q':	printf("%d", pass->pw_quota);	break;
 */
          case 'c':	printf("%s", pass->pw_comment);	break;
          case 'i':	printf("%s", pass->pw_gecos);	break;
          case '0':
          case 'l':	printf("%s", gecos.lastname);	break;
          case 'f':	printf("%s", gecos.firstname);	break;
          case '1':
          case 'o':	printf("%s", gecos.office);	break;
          case '2':
          case 'r':	printf("%s", gecos.room);	break;
          case '3':
          case 't':	printf("%s", gecos.phone);	break;
          case '4':     printf("%s", gecos.spare[0]);	break;
          case '5':	printf("%s", gecos.spare[1]);	break;
          case '6':	printf("%s", gecos.spare[2]);	break;
          case '7':	printf("%s", gecos.spare[3]);	break;
          case '8':	printf("%s", gecos.spare[4]);	break;
          case '9':	
          case 'I':	printf("%s", gecos.id);		break;
          case 'h':	printf("%s", pass->pw_dir);	break;
          case 'H':	{ char path[MAXFILENAMELEN] ;
			  if ( vraipath( pass->pw_dir, path) )
			    printf("%s", path);
			}				break;
          case 's':	printf("%s", pass->pw_shell);	break;
          default:	putchar('%'); putchar(*c);	break;
        }
      } else {
        putchar(*c);
      }
    }
  }
};

printuser (char *format, char  *name)
{ struct passwd *pass;
  struct group  *grp;
  struct gecosfields  gecos;

  if (pass = getpwnam(name)) {
    printpasswd( format, pass);
  } else if (!silent) {
    fprintf(stderr, "user %s not found\n", name);
} };

printall (char *format)
{ struct passwd *pass;
  struct group  *grp;
  struct gecosfields gecos;

  while (pass = getpwent() ) {
    printpasswd( format, pass);
  }
  return (int)pass;
};

main(int argc,
     char *argv[]
)
{
  char **user = 0;

  char *pwfile = argtok(argc, argv, "-pw", NULLPWFILE);
  char *format = argtok(argc, argv, "-f", DEFFMT);
  char *all = argtok(argc, argv, "-all", 0);
  char *version = argtok(argc, argv, "-version", 0);
  silent = argtok(argc, argv, "-c", 0);

  if ( version ) { fputs(VERSION, stderr ); return 0; }

  if ( *pwfile ) {
    endpwent();
    setpwfile(pwfile);
  }

  if ( all ) {
    return printall (format) ; 
  }else {
    for (user = argv, user++; *user; user++) {
      if (! strcmp(*user, "-")){
        char name[MAXUSERNAMELEN];
        while ( gets(name) ) {
	  printuser (format, name);
        }
      }else {
        printuser (format, *user);
      }
    }
  }
};
xzyyz
echo extracting userinfo.n ...
cat >userinfo.n <<xzyyz
.\"	$Log$
.TH USERINFO
.SH NAME
userinfo \- display USER information from 
.B /etc/passwd
.SH VERSION
.B $Revision$
.SH SYNOPSIS
.B userinfo
tokenized display of information from
.B /etc/passwd
and
.B /etc/group
files with 
.I printf(3)
style output
.SH DESCRIPTION
.I userinfo
is a command which will accept a list of user names
from either the command line or standard input.
The parameter flag "-all" selects all the records
in the password file.

.I userinfo
locates
the users with the 
.I getpwnam(3)
and 
.I getpwent(3)
calls which on simple non\-networked systems resolves to the files
.I /etc/passwd
and
.I /etc/group. 
.I Userinfo
writes the information about the users to
.I stdout.

An optional format string may be given on the command line. It allows
for a mixture of text and fields from
.I /etc/passwd
and
.I /etc/group
to be output in lieu of the default format.  The default format is
exactly what appears in the
.I /etc/passwd
file.

This program may be used in place of 
.I grep(3)
and
.I awk(3)
calls to locate
information in the
.I /etc/passwd
file.  It employs the
reliable
.B c
function call
.I getpwnam(3).

Considerable flexibility is
afforded in interpreting the output.
.br
.SH ARGUMENTS
.B names ... \-
.SH COMMAND LINE OPTIONS
.TP
.B \-
read a list of
names, one per line from
.B stdin;
the
.B \-
argument may appear anywhere in the list of names and will be inserted
in place in that list
.TP
.B \-c
operate silently; omits some optional warning message
.TP
.B \-f
format string
.TP
.B \-pw
name of an alternate password file.  The default is:
.I /etc/passwd
.TP
.B \-all
Process
.B all
records in the password file instead of a
selection.  This option overrides all selections
.TP
.B \-version
displays the version number and date of
.Iuserinfo


.SH EXECUTION
The parameters are a list of names of the user
being queried.  The "-f"
parameter denotes the format string.  If it is
omitted, the user
information is written in the format of the
/etc/passwd file.  If a
format string is included it is interpreted as
follows:
.TP
.B %u	user name
.TP
.B %p	password (encrypted)
.TP
.B %U
user number
.TP
.B %G
group number
.TP
.B %g
group name
.TP
.B %q
quota
.TP
.B %c
comment
.TP
.B %i
GECOS fields.  The GECOS fields are individually
accessable.  Their tokens follow below.
.TP
.B %h
home directory
.TP
.B %H
Fully qualified home directory pathname containing no soft links.  Output is a null
string if the pathname does not resolve to a real directory.
.TP
.B %s
shell
.TP
.B %%
"%"

The following fields are components of the GECOS
fields.
.TP
.B %l %0
GECOS last\-name field. (The lastname and
firstname fields are delimited by a doublespace
("  ").  This is a local standard only.  Also, by
convention, the last name is in uppercase.)
.TP
.B %f
GECOS first\-name field. (See above note
This is a local standard only.  Also, by
convention, the last name is in lowercase.)
.TP
.B %o %1
GECOS office field. 
.TP
.B %r %2
GECOS room field. 
.TP
.B %t %3
GECOS Telephone number field. 
.TP
.B %4
GECOS Fourth field (starting from zero). 
.TP
.B %5
GECOS Fifth field (starting from zero). 
.TP
.B %6
GECOS Sixth field (starting from zero). 
.TP
.B %7
GECOS Seventh field (starting from zero). This
field is used locally to hold the Apollo
"organization" field.  It does not get coppied
into the actual password file.
.TP
.B %8
GECOS Eighth field (starting from zero). This
field usually contains a "F" if the application
form is correctly submitted, or is blank if no
form has been submitted.                                    <
.TP
.B %I %9
GECOS ID Number field. Locally defined to
contain the unique ID
number for each user.  This field is confidential
and must not be imbedded into the public
"/etc/passwd" file.

All other character seqences are printed verbatim.
.SH EXAMPLES:
The following example will print the
.I /etc/passwd
file entry for
.B root
.IP
userinfo root
.P
or
.IP
echo root | userinfo -
.P
The following prints 
.I "user is root with home /"
.IP
userinfo root -f \"user is %u with home %h\\n\"
.P
The following prints a list of all account names
with full user name and ID number 
.IP
userinfo -all -f \"%u\\t%l\\t%f\\t%i\\n\"
.SH NOTES
Arguments and options may be freely mixed in any
order.  See argtok(3), a local package for
command line argument parsing, for details.

Remember to place the \"\\n\" at the end of the
format string, or the entire output will be run
together.
.SH FILES
.B /etc/passwd
.B /etc/group
.PD
.SH SEE ALSO
ypcat(8)[on Sun], grep(1), printf(3)
.SH DIAGNOSTICS
invalid user names will be echoed to
.I stderr
as:
.br
.B user
.I username
.B not found
.br
.
.SH BUGS
User names read from standard input must be
stripped of leading and trailing blanks.  The
names are taken verbatim.

Apollo maintains its password registry in a manner that will
occasionally lock it for brief periods of time.  The command will
almost never fail on two consecutive tries when the network is
functioning properly.
xzyyz
echo done
exit 0
exit 0

***************  Second SHAR File ***************
#!/bin/sh
echo extracting Makefile ...
cat >Makefile <<xzyyz
# Makefile for argtok
# 			-- Goldfish 90/09/14

############################################################
#	Global assignments 
############################################################
CC=cc
#	the preceding assignment points to the "personal" include
#	directory
INCLUDE=$(HOME)/Include

#	the preceding assignment points to the "personal" library
#	directory
LIBRARY=$(HOME)/Lib

#	This is the directory to which the "production"
BIN=/site/bin

############################################################
#	options and flags
############################################################
#COMPILEOPTION=-g
COMPILEOPTION=


############################################################
#	First action performs the complete compile
############################################################
all: tools test

############################################################
#	Compile and link the parts of the program
############################################################
tools: .o/main.o .o/argtok.o argtok.h .o/bstran.o bstran.h
	$(CC) $(COMPILEOPTION) .o/main.o .o/bstran.o .o/argtok.o -o tools

.o/main.o: main.c .o/argtok.o argtok.h .o/bstran.o bstran.h
	$(CC) $(COMPILEOPTION) -c main.c -o .o/main.o

.o/argtok.o: argtok.c argtok.h
	$(CC) $(COMPILEOPTION) -c argtok.c -o .o/argtok.o

.o/bstran.o: bstran.c bstran.h
	$(CC) $(COMPILEOPTION) -c bstran.c -o .o/bstran.o

############################################################
#	execute the current version for testing purposes
############################################################
test: tools
	echo "hello\\ttab\\nlinebreak" \
	| ./tools -a one two three -b=four -c=five -d six -e | fmt

############################################################
#	remove re-creatable files
############################################################
clean:
	rm -f .o/*.o *~ *.BAK *.bak tools

############################################################
# install global files onto appropriate libraries
############################################################
production: $(INCLUDE)/argtok.h $(LIBRARY)/argtok.o $(INCLUDE)/bstran.h $(LIBRARY)/bstran.o

$(LIBRARY)/argtok.o: .o/argtok.o
	cp .o/argtok.o $(LIBRARY)/argtok.o

$(INCLUDE)/argtok.h: argtok.h
	cp argtok.h $(INCLUDE)/argtok.h

$(LIBRARY)/bstran.o: .o/bstran.o
	cp .o/bstran.o $(LIBRARY)/bstran.o

$(INCLUDE)/bstran.h: bstran.h
	cp bstran.h $(INCLUDE)/bstran.h

############################################################
#	end of Makefile
############################################################
xzyyz
echo extracting argtok.c ...
cat >argtok.c <<xzyyz
#include "argtok.h"

char *cutfirstelt(char **list)
{ char *first = *list;				/* save first element */
  while (list[0] = list[1]) list++;		/* bump each element back one */
  return first;
}

char *ARGTOK(int *argc,
	          char **argv,
	          char *token,
	          char *deflt)
{ char **cur;
  char *value = deflt;
  int  toklen = strlen(token);
 
  for (cur = argv; *cur; cur++) {
    if (!strncmp(token, *cur, toklen)) {
      if (strlen(*cur) == toklen) {		/* token value is next parameter */
	if (deflt) cutfirstelt(cur);		/* token is not flag; leave */
						/*   flag on arg list */
	value = cutfirstelt(cur);		/* cut token value from list */
      } else {					/* token value is parm tail */
	value = cutfirstelt(cur) + toklen;	/* cut token parm from list and */
						/*   set value to parm tail */
      };
      break;
    }
    if (!strcmp("--",token) ) break;		/* stop scanning at double minus */
  }
  for (*argc = 0, cur = argv; *(cur++) ; (*argc)++); /* count remaining args */
  
  return value;
};

xzyyz
echo extracting argtok.h ...
cat >argtok.h <<xzyyz

#define ARGDELIM "--"
#define argtok(c,v,t,d) ARGTOK(&(c),v,t,d)


char *ARGTOK(     int  *argc,
	          char **argv,
	          char *token,
	          char *deflt);
xzyyz
echo extracting bstran.c ...
cat >bstran.c <<xzyyz
#include "bstran.h"

char *bstran( char *ts )
{ char *curin, *curout;

  for ( curout = curin = ts ; *curin ; curin++, curout++ ) {
    if ( *curin == '\\' ) {
      switch ( *(++curin) ) {
        case '\000' : *curout = '\000' ; return ts;
        case 'n'  : *curout = '\n'; break ;
        case 't'  : *curout = '\t'; break ;
        case 'v'  : *curout = '\v'; break ;
        case 'b'  : *curout = '\b'; break ;
        case 'r'  : *curout = '\r'; break ;
        case 'f'  : *curout = '\f'; break ;
        case 'a'  : *curout = '\a'; break ;
        case '\\' : *curout = '\\'; break ;
        case '?'  : *curout = '\?'; break ;
        default   : *curout = *curin ;
      }
    } else {
        *curout = *curin;
    }
  }
  *curout = '\000';
  return ts;
};

xzyyz
echo extracting bstran.h ...
cat >bstran.h <<xzyyz

char *bstran( char *ts );
xzyyz
echo extracting main.c ...
cat >main.c <<xzyyz
#include <stdio.h>
#include "argtok.h"
#include "bstran.h"

#define booltostr(b) (b ? "True" : "False")

main(int argc,
     char **argv,
     char **envp)
{
  char *a, *b, *c, *d, *e, *f, *g;
  a = argtok(argc, argv, "-a", "a not found");
  b = argtok(argc, argv, "-b", "b not found");
  c = argtok(argc, argv, "-c", 0);
  d = argtok(argc, argv, "-d", "d not found");
  e = argtok(argc, argv, "-e", "e not found");
  f = argtok(argc, argv, "-f", "f not found");
  g = argtok(argc, argv, "-g", 0);

  printf("a=[%s] ", a);
  printf("b=[%s] ", b);
  printf("c=[%s] ", booltostr(c));
  printf("d=[%s] ", d);
  printf("e=[%s] ", e);
  printf("f=[%s] ", f);
  printf("g=[%s] ", booltostr(g));

  printf(" %d remaining args", argc);
  while (*argv) {
    printf(" [%s]", *argv);
    argv++;
  }
  { char work [256];
    while (gets (work)) {
      printf ("\ninput is: [%s]\n", work ); 
      printf ("output is: [%s]\n",  bstran(work) ); 
    }
  }
}
xzyyz
echo done
exit 0
exit 0