[comp.sources.unix] v24i092: Program identifier database tools, Part04/07

rsalz@bbn.com (Rich Salz) (06/06/91)

Submitted-by: Tom Horsley <tom@hcx2.ssd.csd.harris.com>
Posting-number: Volume 24, Issue 92
Archive-name: mkid2/part04

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 7)."
# Contents:  TUTORIAL iid.y scan-c.c
# Wrapped by tom@hcx2 on Tue Feb 26 10:03:04 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'TUTORIAL' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'TUTORIAL'\"
else
echo shar: Extracting \"'TUTORIAL'\" \(16037 characters\)
sed "s/^X//" >'TUTORIAL' <<'END_OF_FILE'
X
XThis is a program identifier database package.  These tools provide a
Xlogical extension to ctags. (which is limited in that it only stores the
Xlocation of function and type *definitions*a)  The ID facility
Xstores the locations for all uses of identifiers, pre-processor
Xnames, and numbers. (in decimal, octal or hex)
X
XWhen fixing or enhancing a large program (particularly one that is
Xunfamiliar) it is often necessary to audit the use of global
Xdata-structures in order to verify that the proposed modification will
Xnot trigger any hidden `gotchas'.  Often this entails grepping through
Xmany thousands of lines of source code spread over dozens and sometimes
Xhundreds of source files in multiple sub-directories.  This process
Xplaces a significant load on computing resources, and takes a long
Xtime.  There is even the danger that a programmer will avoid doing a
Xcomplete audit due to the perceived cost--he or she will rely on memory
Xand hope that there are no booby traps.
X
XThe id-database is most useful for maintaining large programs that
Xconsist of many source files.  The database is simply a two dimensional
Xboolean array indexed by identifier-name and source-file-name.  For a
Xgiven identifier and source-file, if the identifier occurs in the file,
Xthe boolean value is TRUE.  The database may be queried either by
Xidentifier-name or file-name.
X
XThe following types of queries supported:
X
X* name lookup
X	list all the files where an identifier occurs.  The name
X	may be a regular expression.
X
X* name apropos
X	list all the files for all identifiers that have the sub-string
X	name in them.  Matches are done in a case-insensitive mammer.
X
X* name `grep'
X	search for an identifier in all the files where it occurs.
X	This is an optimized `grep' over all the sources--we only
X	search on files that contain the identifier.
X
X* name edit
X	invoke an editor on the files where an identifier occurs,
X	and use the identifier as an initial search string.
X
X* file lookup
X	list all identifiers that occur in a file, or list
X	the identifiers that are common between two files.
X
X* non-unique names
X	list the names of all indentifiers whose names are non-unique
X	within some number of characters.  This is useful when porting
X	a program from a `flexnames' system to one more limited names.
X
X* solo
X	list all identifiers that occur exactly once in a software
X	system.  This may be useful for locating identifiers that are
X	declared but never used, or library functions that are used
X	but never declared.
X	
X
XThe first four queries are handled by one program.  The type of query
Xis determined by the name the program was invoked with.  The four links
Xare lid(1) for `lookup id', aid(1) for `apropos id', gid(1) for `grep
Xid' and eid(1) for `edit id'.  One or more identifiers may be passed on
Xthe command line.  The identifiers may be literal strings or regular
Xexpressions.  Here are some examples:
X
X$ lid FILE
XFILE           extern.h {fid,gets0,getsFF,idx,init,lid,mkid,opensrc,scan-asm,scan-c}.c
X
X$ lid FILE$
XAF_FILE        mkid.c
XAF_IDFILE      mkid.c
XFILE           extern.h {fid,gets0,getsFF,idx,init,lid,mkid,opensrc,scan-asm,scan-c}.c
XIDFILE         id.h {fid,lid,mkid}.c
XIdFILE         {fid,lid}.c
XargFILE        mkid.c
XgidFILE        lid.c
XidFILE         {init,mkid}.c
XinFILE         {gets0,getsFF,scan-asm,scan-c}.c
XopenSrcFILE    extern.h {idx,mkid,opensrc}.c
XsrcFILE        {idx,mkid,opensrc}.c
X
X$ lid ^get
Xget            opensrc.c
XgetAdaId       getscan.c
XgetAsmId       extern.h {getscan,scan-asm}.c
XgetCId         extern.h {getscan,scan-c}.c
XgetDirToName   extern.h {fid,lid,paths}.c
XgetId          {idx,mkid}.c
XgetLanguage    extern.h {getscan,idx,mkid}.c
XgetLispId      getscan.c
XgetPascalId    getscan.c
XgetRoffId      getscan.c
XgetSCCS        extern.h opensrc.c
XgetScanner     extern.h {getscan,idx,mkid}.c
XgetTeXId       getscan.c
XgetTextId      getscan.c
Xgetc           {gets0,getsFF,lid,scan-asm,scan-c}.c
Xgetchar        lid.c
Xgetenv         extern.h lid.c
Xgets           lid.c
XgetsFF         extern.h {bitsvec,fid,getsFF,lid,mkid}.c
X
XAs you can see, when a regular expression is used, it is possible to
Xget more than one line of output.  If you wish multiple lines to be
Xmerged into one, supply the `-m' option:
X
X$ lid -m ^get
X^get           extern.h {bitsvec,fid,gets0,getsFF,getscan,idx,lid,mkid,opensrc,paths,scan-asm,scan-c}.c
X
XThe query program searches for numbers numerically rather than
Xtextually.  Therefore you may search for multiple representations of a
Xnumber.  It is best to illustrate this with examples:
X
X$ lid -a 0x10
X020            numtst.c
X0x00010        numtst.c
X0x0010         scan-c.c
X0x10           {id,radix}.h {scan-asm,stoi}.c
X16             numtst.c
X
XThe `-a' argument tells lid(1) to look for 0x10 in all radixes.  (For
Xnumbers 0 through 7, lid(1) looks for all radixes by default.  For numbers
Xgreater than 7, lid(1) only looks for the radix that the argument is
Xsupplied in.) It is also possible to restrict the search to selected
Xradixes by supplying an argument consisting of one or more of the
Xkey-letters `o', `d', and `x' for octal decimal and hexadecimal
Xrespectively:
X
X$ lid -o 0x10
X020            numtst.c
X
X$ lid -x 16
X0x00010        numtst.c
X0x0010         scan-c.c
X0x10           {id,radix}.h  {scan-asm,stoi}.c
X
X$ lid -d 020
X16             numtst.c
X
X
XThe grep interface behaves somewhat like the following command:
X
X$ grep -w -n `lid TRUE`
X
XHeres some sample output for the equivalent gid command:
X
X$ gid TRUE
Xbool.h:5: #define	TRUE	(0==0)
Xlid.c:102: 		case 'm': forceMerge = TRUE; break;
Xlid.c:170: 			Merging = TRUE;
Xlid.c:204: 			crunching = TRUE;
Xlid.c:553: 			hitDigits = TRUE;
Xlid.c:787: 			return TRUE;
Xmkid.c:117: 			Verbose = TRUE;
Xmkid.c:191: 					keepLang = TRUE;
Xscan-asm.c:79: static bool eatUnder = TRUE;
Xscan-asm.c:80: static bool preProcess = TRUE;
Xscan-asm.c:96: 	static bool	newLine = TRUE;
Xscan-asm.c:130: 		newLine = TRUE;
Xscan-asm.c:141: 		newLine = TRUE;
Xscan-asm.c:145: 		newLine = TRUE;
Xscan-asm.c:150: 		newLine = TRUE;
Xscan-asm.c:165: 				newLine = TRUE;
Xscan-c.c:88: static bool eatUnder = TRUE;
Xscan-c.c:101: 	static bool	newLine = TRUE;
Xscan-c.c:138: 		newLine = TRUE;
Xscan-c.c:199: 				newLine = TRUE;
Xscan-c.c:205: 		newLine = TRUE;
Xscan-c.c:210: 			newLine = TRUE;
Xwmatch.c:37: 			return TRUE;
X
XNotice that each line is reported in the same format as a
XC-preprocessor error message.  This feature allows gid(1) lines to be
Xdigested by any program that parses error messages, such as error(1)
Xand gnu-emacs.
X
XIf you want to edit all files that have an identifier, you may
Xconveniently do so with eid(1):
X
X$ eid TRUE
XTRUE           bool.h {lid,mkid,scan-asm,scan-c,wmatch}.c
XEdit? [y1-9^S/nq] 
X
XBefore the editor is invoked, you are given the lid(1) output to review
Xand comfirm.  If you want to edit all files listed, respond with a
Xnewline or with `y'.  If you want to skip some number of files into the
Xargument list, respond with a single digit `1' through `9' to skip that
Xmany files, or do a string-search to the first file you want with
X`^S<string>' or `/<string>'.  If you don't want to edit anything, type
X`n' to go on to the next argument you gave to eid(1) or type `q' to
Xquit altogether.
X
XThe behavior of the editing interface is controlled by three
Xenvironment variables called EIDARG, EIDLDEL, and EIDRDEL.  The best
Xway to illustrate their use by an example.  Here is how to define them
Xfor vi(1) (using /bin/sh syntax)
X
XEIDARG='+/%s/'	# printf(3) string for initial search-string argument
XEIDLDEL='\<'	# left word-delimiter
XEIDRDEL='\>'	# right word-delimiter
X
X`EID[LR]DEL' are positioned around the identifier as left and right
Xword-delimiters if your editor supports that notion.  Then the whole
Xname-string is sprintf(3)'ed into `EIDARG' to construct the initial
Xsearch-string argument to the editor.  If your editor can't digest such
Xan argument, simply leave these variables undefined in the
Xenvironment.
X
XSome emacs users are appalled at the notion of starting up a fresh editor
Xsimply to follow an identifier.  For those who are fortunate enough to have
Xa programmable emacs such as gnu-emacs, it is fairly simple to devise
Xa command that invokes gid(1) and digests its output as though it were
X/lib/cpp error strings to be examined.  (Sorry, no such code is provided
Xat this posting...)
X
XAnother type of query is to find all identifiers that are non-unique
Xwithin some number of characters.  This is useful for finding potential
Xportability problems when moving to a system whose compiler or linker
Xlimits the number of significant characters in a name.  The `-u<n>'
Xargument does the trick.  Here's a list of identifiers that may yield
Xmultiply-defined errors in a symbol table that only knows about the
Xfirst 7 characters:
X
X$ lid -u7
XSCAN_TEX       getscan.c
XSCAN_TEXT      getscan.c
Xidh_argc       id.h {init,mkid}.c
Xidh_argo       id.h {init,mkid}.c
Xidh_namc       id.h {fid,mkid}.c
Xidh_namo       id.h {fid,init,lid,mkid}.c
XoldHashSize    mkid.c
XoldHashTable   mkid.c
X
XBetter yet, if you want to edit these, try
X
X$ eid -u7
X^SCAN_TE       getscan.c
XEdit? [y1-9^S/nq] n
X^idh_arg       getscan.c id.h {init,mkid}.c
XEdit? [y1-9^S/nq] n
X^idh_nam       {fid,getscan}.c id.h {init,lid,mkid}.c
XEdit? [y1-9^S/nq] n
X^oldHash       {fid,getscan}.c id.h {init,lid,mkid}.c
XEdit? [y1-9^S/nq] n
X
X
XAn additional feature of lid(1) is that pathnames are automatically
Xadjusted for the current working directory.  Large programs such as the
XUNIX kernel are often partitioned into subsystems whose sources live in
Xdifferent directories.  What follows are several examples of the same
Xsearch conducted from different points in the UNIX kernel source
Xhierarchy:
X
X$ cd /src/uts/m68k
X$ lid bdevsw
Xbdevsw         sys/conf.h  cf/conf.c  io/bio.c  os/{fio,main,prf,sys3}.c
X
X$ cd io
X$ lid bdevsw
Xbdevsw         ../sys/conf.h  ../cf/conf.c  bio.c  ../os/{fio,main,prf,sys3}.c
X
X$ cd ../os
Xbdevsw         ../sys/conf.h  ../cf/conf.c  ../io/bio.c  {fio,main,prf,sys3}.c
X
XThe database is built with mkid(1).  The user supplies pathnames
Xeither on the command line or on stdin.  Here's the output of the
X`verbose' option to mkid(1):
X
X$ mkid -v *.h *.c
Xc: bitops.h
Xc: bool.h
Xc: extern.h
Xc: id.h
Xc: patchlevel.h
Xc: radix.h
Xc: string.h
Xc: basename.c
Xc: bitcount.c
Xc: bitops.c
Xc: bitsvec.c
Xc: bsearch.c
Xc: bzero.c
Xc: document.c
Xc: fid.c
Xc: gets0.c
Xc: getsFF.c
Xc: getscan.c
Xc: hash.c
Xc: idx.c
Xc: init.c
Xc: lid.c
Xc: mkid.c
Xc: numtst.c
Xc: opensrc.c
Xc: paths.c
Xc: scan-asm.c
Xc: scan-c.c
Xc: stoi.c
Xc: tty.c
Xc: uerror.c
Xc: wmatch.c
XCompressing Hash Table...
XSorting Hash Table...
XWriting `ID'...
XNames: 593, Numbers: 64, Strings: 43, Solo: 119, Total: 697
XOccurrances: 11.67, Load: 0.17, Probes: 1.07
X
XMkid(1) echoes the name of each file as it is scanned, prefixed by the
Xname of the language it thinks the file is written in.  Mkid(1) reports
Xhow many unique names and numbers were found, how many names occurred
Xonly once, and the total for names and numbers.  It also reports the
Xaverage number of occurrances for all names and numbers.  Next, there
Xare some hash-table statistics on the load-factor and the average
Xnumber of open-addressed probes.
X
XMkid(1) can take arguments from the command line, from stdin, or from
Xa file.  A file full of filenames may also contain mkid options of the form
X-<option>.  Filenames and options appear in the file one-per-line.  Typical
Xusage for this feature is as follows:
X
X$ find . -name '*.[chys]' -print >IDFILES
X$ mkid -aIDFILES
X
X-- or --
X
X$ find . -name '*.[chys]' -print |mkid -
X
XMkid(1) stashes the filenames and relevant arguments in the database
Xitself.  It uses these to support the ``incremental-update' option.
XIf invoked with `-u', mkid(1) checks the modification times of all
Xconstituent files, and only re-scans those that are newer than the
Xdatabase itself.  It is invoked like so:
X
X$ mkid -u
X
XIn summation, mkid(1) can get arguments from one of four places:
X1) the command line, 2) a file, 3) stdin, 4) the database itself.
X
XMkid(1) accepts a number of scanner-specific arguments.  Generally,
Xthese are introduced with `-S<lang>' where <lang> is the name of
Xa language, such as `c' or `asm'.  You can get a scanner-specific
Xusage-report with `-S<lang>?'  (Of course, the `?' must be escaped
Xto get it past the shell)
X
XHere's scanner-usage for the assembly language scanner:
X
X$ mkid -Sasm\?
XThe Assembler scanner arguments take the form -Sasm<arg>, where
X<arg> is one of the following: (<cc> denotes one or more characters)
X  -c<cc> . . . . <cc> introduce(s) a comment until end-of-line.
X  (+|-)u . . . . (Do|Don't) strip a leading `_' from ids.
X  (+|-)a<cc> . . Allow <cc> in ids, and (keep|ignore) those ids.
X  (+|-)p . . . . (Do|Don't) handle C-preprocessor directives.
X  (+|-)C . . . . (Do|Don't) handle C-style comments. (/* */)
X
X`-Sasm-c<cc>' tells the scanner what characters are used to introduce comments
Xthat extend to end-of-line.
X
XUse `-Sasm+u' if your C compiler prepends leading underscores to external
Xnames.  This way, mkid(1) will strip leading underscores, and the name
X`foo' in a C source will be correctly associated with the name `_foo'
Xin an assembler source.  If your compiler doesn't prepend leading
Xunderscores, use `-Sasm-u'.
X
XMany assemblers allow special characters to be mixed with
Xalpha-numerics in label, constant and register names.  Common choices
Xare `.', `%', and `$'.  Thus, a label such as `L%123' should be scanned
Xas one token, not broken up into the name `L' and the number 123.
X`-Sasm-a%.' tells the scanner to allow `%' and `.' in tokens, but to throw
Xaway tokens containing `%' or `.' `-Sasm+a%.' tells the scanner to keep such
Xtokens and put them into the database.
X
X
X`-Sasm+p' tells the scanner to handle `#include' and `#define' lines as
Xin C source, and `-Sasm+C' tells it to ignore C-style comments.
X
XHere's the scanner-usage for C:
X
X$ mkid -Sc\?
XThe C scanner arguments take the form -Sc<arg>, where <arg>
Xis one of the following: (<cc> denotes one or more characters)
X  (+|-)u . . . . (Do|Don't) strip a leading `_' from ids in strings.
X  -s<cc> . . . . Allow <cc> in string ids.
X
XThe `+u' argument is akin to the argument for the assembly-language
Xscanner.  Mkid(1) keeps the contents of quoted-strings if the string
Xcontains a single valid C name and nothing else.  E.g.  mkid(1) would
Xkeep the contents of "_proc".  Such strings are interesting because
Xthey may contain symbol names that a program uses for nlist lookups.
XSo, if your compiler prepends underscores to external symbols, use
X`-Sc+u' so that mkid(1) will strip them back off.
X
XMkid(1) normally throws away the contents of quoted strings that have
Xanything other than a single name in them.  You can tell mkid(1) to
Xaccept additional characters in strings with `-Sc-s<cc>' where <cc> is
Xone or more special characters.  E.g. `-Sc-s/.-:,' will include most of
Xthe strings containing pathnames that you are likely to encounter.
X
XAnother class of scanner argument allows you to associate a suffix
Xwith a language.  E.g. `-S.y=c' tells mkid(1) to use the C language
Xscanner on all files ending with .y.  You can ask mkid(1) for the
Xavailable scanners and associated suffixes like so:
X
X$ mkid -S\?=\?
X.c=c, .h=c, .y=c, .s=asm, .p=pascal, .pas=pascal
X
XPlease note, mkid(1) is lying to you about its Pascal prowess!
XAt the time of this posting, there are scanners for C and assembly
Xlanguage sources.  There are also stubs for Pascal, Ada and LISP.  The
Xscanners are very fast.  The assembly language scanner knows how
Xto throw away C-style comments as well as the traditional `comment-
Xcharacter-until-end-of-line' style.  In order to test new scanners,
Xthere is a scanner driver called idx(1).  Idx(1) simply calls the
Xscanner to get identifiers one-at-a-time prints them on stdout one-per-line.
X
XFor more information, read the manual pages!
X
XHappy Hacking,
X--
X-- Greg McGary
X-- P.O. Box 286
X-- Lincoln, MA  01773
X--
X-- 9/15/87
X--
X--	Until the end of 1987,
X--	Consulting to Sun's East Coast Division:
X--		gmcgary@ecd.sun.com
X--		gmcgary@suneast.uu.net
X--
X--	After that, probably consulting in Europe...
END_OF_FILE
if test 16037 -ne `wc -c <'TUTORIAL'`; then
    echo shar: \"'TUTORIAL'\" unpacked with wrong size!
fi
# end of 'TUTORIAL'
fi
if test -f 'iid.y' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'iid.y'\"
else
echo shar: Extracting \"'iid.y'\" \(11278 characters\)
sed "s/^X//" >'iid.y' <<'END_OF_FILE'
X%{
X/* This is the yacc definition for the iid command language.
X * The main program, scanner, and parser are defined here.
X * The utility functions invoked from here are in iidfun.c
X */
X
X#include "iiddef.h"
X
X%}
X
X%union {
X   set_type *     setdef ;   
X   id_type *      strdef ;
X   id_list_type * listdef ;
X}
X
X%token < setdef > SET
X
X%token < strdef > ID SHELL_QUERY SHELL_COMMAND
X
X%type < setdef > Query Primitive
X
X%type < listdef > Lid_group Aid_group Id_list Command_list
X
X%token LID AID BEGIN SETS SS FILES SHOW HELP OFF MATCH
X
X%left OR
X
X%left AND
X
X%left NOT
X
X%start Command
X
X%%
X
XCommand :
X   BEGIN ID
X      {
X         /* cd to the directory specified as argument, flush sets */
X
X         SetDirectory($2) ;
X         FlushSets() ;
X      }
X|  Set_query Query
X|  File_query Query
X      {
X         /* print the list of files resulting from Query */
X
X         PrintSet($2) ;
X      }
X|  SHOW SET
X      {
X         /* run PAGER on the list of files in SET */
X
X         RunPager(Pager, $2) ;
X      }
X|  SETS
X      {
X         /* describe sets created so far */
X
X         DescribeSets() ;
X      }
X|  HELP
X      {
X         /* run PAGER on the help file */
X
X         RunPager(Pager, HelpSet) ;
X      }
X|  OFF
X      {
X         exit(0) ;
X      }
X| SHELL_QUERY Command_list
X      {
X         /* run the shell command and eat the results as a file set */
X
X         OneDescription(RunProg($1->id, $2)) ;
X         free($1) ;
X      }
X| SHELL_COMMAND Command_list
X      {
X         /* run the shell command */
X
X         RunShell($1->id, $2) ;
X         free($1) ;
X      }
X;
X
XSet_query :
X   SS
X      {
X         /* Turn on verbose query flag */
X
X         VerboseQuery = TRUE ;
X      }
X;
X
XFile_query :
X   FILES
X      {
X         /* Turn off verbose query flag */
X
X         VerboseQuery = FALSE ;
X      }
X;
X
XQuery :
X   Primitive
X      {
X         /* value of query is set associated with primitive */
X
X         $$ = $1 ;
X      }
X|  Query AND Query
X      {
X         /* value of query is intersection of the two query sets */
X
X         $$ = SetIntersect($1, $3) ;
X         if (VerboseQuery) {
X            OneDescription($$) ;
X         }
X      }
X|  Query OR Query
X      {
X         /* value of query is union of the two query sets */
X
X         $$ = SetUnion($1, $3) ;
X         if (VerboseQuery) {
X            OneDescription($$) ;
X         }
X      }
X|  NOT Query
X      {
X         /* value of query is inverse of other query */
X         
X         $$ = SetInverse($2) ;
X         if (VerboseQuery) {
X            OneDescription($$) ;
X         }
X      }
X;
X
XPrimitive :
X   SET
X      {
X         /* Value of primitive is value of recorded set */
X
X         $$ = $1 ;
X      }
X|  Lid_group
X      {
X         /* Value of primitive is obtained by running an lid query */
X
X         $$ = RunProg(LidCommand, $1) ;
X         if (VerboseQuery) {
X            OneDescription($$) ;
X         }
X      }
X|  Aid_group
X      {
X         /* Value of primitive is obtained by running an aid query */
X
X         $$ = RunProg("aid -kmn", $1) ;
X         if (VerboseQuery) {
X            OneDescription($$) ;
X         }
X      }
X|  MATCH Id_list
X      {
X         /* Match names from database against pattern */
X         $$ = RunProg("pid -kmn", $2) ;
X         if (VerboseQuery) {
X            OneDescription($$) ;
X         }
X      }
X|  '(' Query ')'
X      {
X         /* value of primitive is value of query */
X
X         $$ = $2 ;
X      }
X;
X
XLid_group :
X   ID
X      {
X         /* make arg list holding single ID */
X
X         $$ = InitList() ;
X         $$ = ExtendList($$, $1) ;
X         LidCommand = DefaultCommand ;
X      }
X|  LID Id_list
X      {
X         /* arg list is Id_list */
X
X         $$ = $2 ;
X         LidCommand = "lid -kmn" ;
X      }
X;
X
XAid_group :
X   AID Id_list
X      {
X         /* arg list is Id_list */
X
X         $$ = $2 ;
X      }
X;
X
XCommand_list :
X   ID
X      {
X         /* make arg list holding single ID */
X
X         $$ = InitList() ;
X         $$ = ExtendList($$, $1) ;
X      }
X|  SET
X      {
X         /* make arg list holding names from set */
X
X         $$ = InitList() ;
X         $$ = SetList($$, $1) ;
X      }
X|  Command_list ID
X      {
X         /* extend arg list with additional ID */
X
X         $$ = ExtendList($1, $2) ;
X      }
X|  Command_list SET
X      {
X         /* extend arg list with additional file names */
X
X         $$ = SetList($1, $2) ;
X      }
X;
X
XId_list :
X   ID
X      {
X         /* make arg list holding single ID */
X
X         $$ = InitList() ;
X         $$ = ExtendList($$, $1) ;
X      }
X|  Id_list ID
X      {
X         /* extend arg list with additional ID */
X
X         $$ = ExtendList($1, $2) ;
X      }
X;
X
X%%
X
X/* ScanLine - a global variable holding a pointer to the current
X * command being scanned.
X */
Xchar *             ScanLine ;
X
X/* ScanPtr - a global pointer to the current scan position in ScanLine.
X */
Xchar *             ScanPtr ;
X
X/* yytext - buffer holding the token.
X */
Xchar               yytext [ MAXCMD ] ;
X
X/* yyerror - process syntax errors.
X */
Xint
Xyyerror(s)
X   char *     s ;
X{
X   if (*ScanPtr == '\0') {
X      fprintf(stderr,"Syntax error near end of command.\n") ;
X   } else {
X      fprintf(stderr,"Syntax error on or before %s\n",ScanPtr) ;
X   }
X   return(0) ;
X}
X
X/* ScanInit - initialize the yylex routine for the new line of input.
X * Basically just initializes the global variables that hold the char
X * ptrs the scanner uses.
X */
Xvoid
XScanInit(line)
X   char *    line ;
X{
X   /* skip the leading white space - the yylex routine is sensitive
X    * to keywords in the first position on the command line.
X    */
X   
X   while (isspace(*line)) ++line ;
X   ScanLine = line ;
X   ScanPtr = line ;
X}
X
X/* NameEq - compare two names for equality in a case insensitive manner.
X * return TRUE for equal, FALSE otherwise.
X */
Xint
XNameEq(n1,n2)
X   char *      n1 ;
X   char *      n2 ;
X{
X   char        c1 ;
X   char        c2 ;
X   
X   for ( ; ; ) {
X      c1 = *n1++ ;
X      c2 = *n2++ ;
X      if (isalpha(c1)) c1 = tolower(c1) ;
X      if (isalpha(c2)) c2 = tolower(c2) ;
X      if (c1 != c2) return FALSE ;
X      if (c1 == '\0') return TRUE ;
X   }
X}
X
X/* yylex - the scanner for iid. Basically a kludge ad-hoc piece of junk,
X * but what the heck, if it works...
X *
X * Mostly just scans for non white space strings and returns ID for them.
X * Does check especially for '(' and ')'. Just before returning ID it
X * checks for command names if it is the first token on line or
X * AND, OR, LID, AID if it is in the middle of a line.
X */
Xint
Xyylex()
X{
X   char *      bp ;
X   char        c ;
X   int         code = ID ;
X   char *      dp ;
X   char *      sp ;
X   int         val ;
X   
X   bp = ScanPtr ;
X   while (isspace(*bp)) ++bp ;
X   sp = bp ;
X   c = *sp++ ;
X   if ((c == '(') || (c == ')') || (c == '\0')) {
X      ScanPtr = sp ;
X      if (c == '\0') {
X         --ScanPtr ;
X      }
X      return(c) ;
X   } else {
X      dp = yytext ;
X      while (! ((c == '(') || (c == ')') || (c == '\0') || isspace(c))) {
X         *dp++ = c ;
X         c = *sp++ ;
X      }
X      *dp++ = '\0' ;
X      ScanPtr = sp - 1 ;
X      if (bp == ScanLine) {
X
X         /* first token on line, check for command names */
X
X         if (NameEq(yytext, "SS")) return(SS) ;
X         if (NameEq(yytext, "FILES")) return(FILES) ;
X         if (NameEq(yytext, "F")) return(FILES) ;
X         if (NameEq(yytext, "HELP")) return(HELP) ;
X         if (NameEq(yytext, "H")) return(HELP) ;
X         if (NameEq(yytext, "?")) return(HELP) ;
X         if (NameEq(yytext, "BEGIN")) return(BEGIN) ;
X         if (NameEq(yytext, "B")) return(BEGIN) ;
X         if (NameEq(yytext, "SETS")) return(SETS) ;
X         if (NameEq(yytext, "SHOW")) return(SHOW) ;
X         if (NameEq(yytext, "P")) return(SHOW) ;
X         if (NameEq(yytext, "OFF")) return(OFF) ;
X         if (NameEq(yytext, "Q")) return(OFF) ;
X         if (NameEq(yytext, "QUIT")) return(OFF) ;
X         if (yytext[0] == '!') {
X            code = SHELL_COMMAND ;
X         } else {
X            code = SHELL_QUERY ;
X         }
X      } else {
X
X         /* not first token, check for operator names */
X
X         if (NameEq(yytext, "LID")) return(LID) ;
X         if (NameEq(yytext, "AID")) return(AID) ;
X         if (NameEq(yytext, "AND")) return(AND) ;
X         if (NameEq(yytext, "OR")) return(OR) ;
X         if (NameEq(yytext, "NOT")) return(NOT) ;
X         if (NameEq(yytext, "MATCH")) return(MATCH) ;
X         if ((yytext[0] == 's' || yytext[0] == 'S') && isdigit(yytext[1])) {
X            
X            /* this might be a set specification */
X            
X            sp = &yytext[1] ;
X            val = 0 ;
X            for ( ; ; ) {
X               c = *sp++ ;
X               if (c == '\0') {
X                  if (val < NextSetNum) {
X                     yylval.setdef = TheSets[val] ;
X                     return(SET) ;
X                  }
X               }
X               if (isdigit(c)) {
X                  val = (val * 10) + (c - '0') ;
X               } else {
X                  break ;
X               }
X            }
X         }
X      }
X      yylval.strdef = (id_type *)malloc(sizeof(id_type) + strlen(yytext)) ;
X      if (yylval.strdef == NULL) {
X         fatal("Out of memory in yylex") ;
X      }
X      yylval.strdef->next_id = NULL ;
X      if (code == SHELL_COMMAND) {
X         strcpy(yylval.strdef->id, &yytext[1]) ;
X      } else {
X         strcpy(yylval.strdef->id, yytext) ;
X      }
X      return(code) ;
X   }
X}
X
X/* The main program for iid - parse the command line, initialize processing,
X * loop processing one command at a time.
X */
Xmain(argc, argv)
X   int         argc ;
X   char *      argv [ ] ;
X{
X   int         c ;                     /* current option */
X   char *      CmdPtr ;                /* Points to the command string */
X   char        Command [ MAXCMD ] ;    /* Buffer for reading commands */
X   int         Do1 = FALSE ;           /* TRUE if should only do 1 command */
X   int         DoPrompt ;              /* TRUE if should write a prompt */
X   int         errors = 0 ;            /* error count */
X
X   DoPrompt = isatty(fileno(stdin)) ;
X   while ((c = getopt(argc, argv, "Hac:")) != EOF) {
X      switch(c) {
X      case 'a':
X         DefaultCommand = "aid -kmn" ;
X         break ;
X      case 'c':
X         CmdPtr = optarg ;
X         Do1 = TRUE ;
X         break ;
X      case 'H':
X         fputs("\
Xiid: interactive ID database query tool. Call with:\n\
X   iid [-a] [-c] [-H]\n\
X\n\
X-a\tUse the aid as the default query command (not lid).\n\
X-c cmd\tExecute the single query cmd and exit.\n\
X-H\tPrint this message and exit.\n\
X\n\
XTo get help after starting program type 'help'.\n\
X",stderr) ;
X         exit(0) ;
X      default:
X         ++errors ;
X         break ;
X      }
X   }
X   if (argc != optind) {
X      fputs("iid: Excess arguments ignored.\n",stderr) ;
X      ++errors ;
X   }
X   if (errors) {
X      fputs("run iid -H for help.\n",stderr) ;
X      exit(1) ;
X   }
X
X   /* initialize global data */
X
X   InitIid() ;
X
X   /* run the parser */
X
X   if (Do1) {           
X      ScanInit(CmdPtr) ;
X      exit(yyparse()) ;
X   } else {
X      for ( ; ; ) {
X         if (DoPrompt) {
X            fputs(Prompt, stdout) ;
X            fflush(stdout) ;
X         }
X         gets(Command) ;
X         if (feof(stdin)) {
X            if (DoPrompt) fputs("\n", stdout) ;
X            strcpy(Command, "off") ;
X         }
X         ScanInit(Command) ;
X         errors += yyparse() ;
X      }
X   }
X}
END_OF_FILE
if test 11278 -ne `wc -c <'iid.y'`; then
    echo shar: \"'iid.y'\" unpacked with wrong size!
fi
# end of 'iid.y'
fi
if test -f 'scan-c.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'scan-c.c'\"
else
echo shar: Extracting \"'scan-c.c'\" \(8401 characters\)
sed "s/^X//" >'scan-c.c' <<'END_OF_FILE'
X/* Copyright (c) 1986, Greg McGary */
X/* Vhil scanning mods added Jan 7th, 1988 by Tom Horsley */
Xstatic char sccsid[] = "@(#)scan-c.c	1.1 86/10/09";
X
X#include	<bool.h>
X#include	<stdio.h>
X#include	<string.h>
X#include	<id.h>
X
Xchar *getCId();
Xchar *getVhilId();
Xvoid setCArgs();
X
Xstatic void clrCtype();
Xstatic void setCtype();
X
X#define	I1	0x0001	/* 1st char of an identifier [a-zA-Z_] */
X#define	DG	0x0002	/* decimal digit [0-9] */
X#define	NM	0x0004	/* extra chars in a hex or long number [a-fA-FxXlL] */
X#define	C1	0x0008	/* C comment introduction char: / */
X#define	C2	0x0010	/* C comment termination  char: * */
X#define	Q1	0x0020	/* single quote: ' */
X#define	Q2	0x0040	/* double quote: " */
X#define	ES	0x0080	/* escape char: \ */
X#define	NL	0x0100	/* newline: \n */
X#define	EF	0x0200	/* EOF */
X#define	SK	0x0400	/* Make these chars valid for names within strings */
X#define	VH	0x0800	/* VHIL comment introduction char: # */
X#define	WS	0x1000	/* White space characters */
X
X/*
X	character class membership macros:
X*/
X#define	ISDIGIT(c)	((rct)[c]&(DG))		/* digit */
X#define	ISNUMBER(c)	((rct)[c]&(DG|NM))	/* legal in a number */
X#define	ISEOF(c)	((rct)[c]&(EF))		/* EOF */
X#define	ISID1ST(c)	((rct)[c]&(I1))		/* 1st char of an identifier */
X#define	ISIDREST(c)	((rct)[c]&(I1|DG))	/* rest of an identifier */
X#define	ISSTRKEEP(c)	((rct)[c]&(I1|DG|SK))	/* keep contents of string */
X#define	ISSPACE(c)	((rct)[c]&(WS))		/* white space character */
X/*
X	The `BORING' classes should be skipped over
X	until something interesting comes along...
X*/
X#define	ISBORING(c)	(!((rct)[c]&(EF|NL|I1|DG|Q1|Q2|C1|VH)))	/* fluff */
X#define	ISCBORING(c)	(!((rct)[c]&(EF|C2)))	/* comment fluff */
X#define	ISVBORING(c)	(!((rct)[c]&(EF|NL)))	/* vhil comment fluff */
X#define	ISQ1BORING(c)	(!((rct)[c]&(EF|NL|Q1|ES)))	/* char const fluff */
X#define	ISQ2BORING(c)	(!((rct)[c]&(EF|NL|Q2|ES)))	/* quoted str fluff */
X
Xstatic short idctype[] = {
X
X	EF,
X
X	/*      0       1       2       3       4       5       6       7   */
X	/*    -----   -----   -----   -----   -----   -----   -----   ----- */
X
X	/*000*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*010*/	0,	0,	NL,	0,	0,	0,	0,	0,
X	/*020*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*030*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*040*/	0,	0,	Q2,	0,	0,	0,	0,	Q1,
X	/*050*/	0,	0,	C2,	0,	0,	0,	0,	C1,
X	/*060*/	DG,	DG,	DG,	DG,	DG,	DG,	DG,	DG,	
X	/*070*/	DG,	DG,	0,	0,	0,	0,	0,	0,
X	/*100*/	0,	I1|NM,	I1|NM,	I1|NM,	I1|NM,	I1|NM,	I1|NM,	I1,
X	/*110*/	I1,	I1,	I1,	I1,	I1|NM,	I1,	I1,	I1,
X	/*120*/	I1,	I1,	I1,	I1,	I1,	I1,	I1,	I1,
X	/*130*/	I1|NM,	I1,	I1,	0,	ES,	0,	0,	I1,
X	/*140*/	0,	I1|NM,	I1|NM,	I1|NM,	I1|NM,	I1|NM,	I1|NM,	I1,
X	/*150*/	I1,	I1,	I1,	I1,	I1|NM,	I1,	I1,	I1,
X	/*160*/	I1,	I1,	I1,	I1,	I1,	I1,	I1,	I1,
X	/*170*/	I1|NM,	I1,	I1,	0,	0,	0,	0,	0,
X
X	/*200*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*210*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*220*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*230*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*240*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*250*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*260*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*270*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*300*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*310*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*320*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*330*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*340*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*350*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*360*/	0,	0,	0,	0,	0,	0,	0,	0,
X	/*370*/	0,	0,	0,	0,	0,	0,	0,	0,
X
X};
X
Xstatic bool eatUnder = TRUE;
Xstatic bool scanVhil = FALSE;
X
X/*
X*/
Xchar *
XgetVhilId(inFILE, flagP)
X{
X	if (! scanVhil) {
X		setCArgs("vhil",'+',"v");
X	}
X	return(getCId(inFILE, flagP));
X}
X
X/*
X	Grab the next identifier the C source
X	file opened with the handle `inFILE'.
X	This state machine is built for speed, not elegance.
X*/
Xchar *
XgetCId(inFILE, flagP)
X	FILE		*inFILE;
X	int		*flagP;
X{
X	static char	idBuf[BUFSIZ];
X	static bool	newLine = TRUE;
X	register short	*rct = &idctype[1];
X	register int	c;
X	register char	*id = idBuf;
X
Xtop:
X	c = getc(inFILE);
X	if (newLine) {
X		newLine = FALSE;
X		if (c == '.') {
X			/* Auto-recognize vhil code when you see a . in column 1.
X			 * also ignore lines that start with a .
X			 */
X			if (! scanVhil) {
X				setCArgs("vhil",'+',"v");
X			}
X			while (ISVBORING(c))
X				c = getc(inFILE);
X			newLine = TRUE;
X			goto top;
X		}
X		if (c != '#')
X			goto next;
X		c = getc(inFILE);
X		if (scanVhil && ISSPACE(c)) {
X			while (ISVBORING(c))
X				c = getc(inFILE);
X			newLine = TRUE;
X			goto top;
X		}
X		while (ISBORING(c))
X			c = getc(inFILE);
X		if (!ISID1ST(c))
X			goto next;
X		id = idBuf;
X		*id++ = c;
X		while (ISIDREST(c = getc(inFILE)))
X			*id++ = c;
X		*id = '\0';
X		if (strequ(idBuf, "include")) {
X			while (c == ' ' || c == '\t') c = getc(inFILE) ;
X			if (c == '\n') {newLine = TRUE; goto top;}
X			id = idBuf;
X			if (c == '"') {
X				c = getc(inFILE);
X				while (c != '\n' && c != EOF && c != '"') {
X					*id++ = c;
X					c = getc(inFILE);
X				}
X				*flagP = IDN_STRING;
X			} else if (c == '<') {
X				c = getc(inFILE);
X				while (c != '\n' && c != EOF && c != '>') {
X					*id++ = c;
X					c = getc(inFILE);
X				}
X				*flagP = IDN_STRING;
X			} else if (ISID1ST(c)) {
X				*id++ = c ;
X				while (ISIDREST(c = getc(inFILE))) *id++ = c;
X				*flagP = IDN_NAME;
X			} else {
X				while (c != '\n' && c != EOF) c = getc(inFILE);
X				newLine = TRUE;
X				goto top;
X			}
X			while (c != '\n' && c != EOF) c = getc(inFILE) ;
X			newLine = TRUE;
X			*id = '\0';
X			return idBuf;
X		}
X		if (strnequ(idBuf, "if", 2)
X		|| strequ(idBuf, "define")
X		|| strequ(idBuf, "elif")         /* ansi C */
X		|| (scanVhil && strequ(idBuf, "elsif"))
X		|| strequ(idBuf, "undef"))
X			goto next;
X		while ((c != '\n') && (c != EOF))
X			c = getc(inFILE);
X		newLine = TRUE;
X		goto top;
X	}
X
Xnext:
X	while (ISBORING(c))
X		c = getc(inFILE);
X
X	switch (c)
X	{
X	case '"':
X		id = idBuf;
X		*id++ = c = getc(inFILE);
X		for (;;) {
X			while (ISQ2BORING(c))
X				*id++ = c = getc(inFILE);
X			if (c == '\\') {
X				*id++ = c = getc(inFILE);
X				continue;
X			} else if (c != '"')
X				goto next;
X			break;
X		}
X		*--id = '\0';
X		id = idBuf;
X		while (ISSTRKEEP(*id))
X			id++;
X		if (*id || id == idBuf) {
X			c = getc(inFILE);
X			goto next;
X		}
X		*flagP = IDN_STRING;
X		if (eatUnder && idBuf[0] == '_' && idBuf[1])
X			return &idBuf[1];
X		else
X			return idBuf;
X		
X	case '\'':
X		c = getc(inFILE);
X		for (;;) {
X			while (ISQ1BORING(c))
X				c = getc(inFILE);
X			if (c == '\\') {
X				c = getc(inFILE);
X				continue;
X			} else if (c == '\'')
X				c = getc(inFILE);
X			goto next;
X		}
X
X	case '/':
X		if ((c = getc(inFILE)) != '*')
X			goto next;
X		c = getc(inFILE);
X		for (;;) {
X			while (ISCBORING(c))
X				c = getc(inFILE);
X			if ((c = getc(inFILE)) == '/') {
X				c = getc(inFILE);
X				goto next;
X			} else if (ISEOF(c)) {
X				newLine = TRUE;
X				return NULL;
X			}
X		}
X
X	case '\n':
X		newLine = TRUE;
X		goto top;
X
X	case '#':
X		if (! scanVhil) {
X			/* Auto-recognize vhil when find a # in the middle of a line.
X			 */
X			setCArgs("vhil",'+',"v");
X		}
X		c = getc(inFILE);
X		while (ISVBORING(c))
X			c = getc(inFILE);
X		newLine = TRUE;
X		goto top;
X	default:
X		if (ISEOF(c)) {
X			newLine = TRUE;
X			return NULL;
X		}
X	name:
X		id = idBuf;
X		*id++ = c;
X		if (ISID1ST(c)) {
X			*flagP = IDN_NAME;
X			while (ISIDREST(c = getc(inFILE)))
X				*id++ = c;
X		} else if (ISDIGIT(c)) {
X			*flagP = IDN_NUMBER;
X			while (ISNUMBER(c = getc(inFILE)))
X				*id++ = c;
X		} else
X			fprintf(stderr, "junk: `\\%3o'", c);
X		ungetc(c, inFILE);
X		*id = '\0';
X		*flagP |= IDN_LITERAL;
X		return idBuf;
X	}
X}
X
Xstatic void
XsetCtype(chars, type)
X	char		*chars;
X	int		type;
X{
X	short		*rct = &idctype[1];
X
X	while (*chars)
X		rct[*chars++] |= type;
X}
Xstatic void
XclrCtype(chars, type)
X	char		*chars;
X	int		type;
X{
X	short		*rct = &idctype[1];
X
X	while (*chars)
X		rct[*chars++] &= ~type;
X}
X
Xextern char	*MyName;
Xstatic void
Xusage(lang)
X	char		*lang;
X{
X	fprintf(stderr, "Usage: %s does not accept %s scanner arguments\n", MyName, lang);
X	exit(1);
X}
Xstatic char *cDocument[] =
X{
X"The C scanner arguments take the form -Sc<arg>, where <arg>",
X"is one of the following: (<cc> denotes one or more characters)",
X"  (+|-)u . . . . (Do|Don't) strip a leading `_' from ids in strings.",
X"  -s<cc> . . . . Allow <cc> in string ids.",
X"  -v . . . . . . Skip vhil comments.",
XNULL
X};
Xvoid
XsetCArgs(lang, op, arg)
X	char		*lang;
X	int		op;
X	char		*arg;
X{
X	if (op == '?') {
X		document(cDocument);
X		return;
X	}
X	switch (*arg++)
X	{
X	case 'u':
X		eatUnder = (op == '+');
X		break;
X	case 's':
X		setCtype(arg, SK);
X		break;
X	case 'v':
X		setCtype("$", I1);
X		setCtype("#", VH);
X		setCtype(" \t", WS);
X		scanVhil = TRUE;
X		break;
X	default:
X		if (lang)
X			usage(lang);
X		break;
X	}
X}
END_OF_FILE
if test 8401 -ne `wc -c <'scan-c.c'`; then
    echo shar: \"'scan-c.c'\" unpacked with wrong size!
fi
# end of 'scan-c.c'
fi
echo shar: End of archive 4 \(of 7\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 7 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.