summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYING339
-rw-r--r--INSTALL118
-rw-r--r--Makefile.in264
-rw-r--r--NEWS25
-rw-r--r--README23
-rw-r--r--THANKS17
-rw-r--r--TODO51
-rw-r--r--acconfig.h27
-rw-r--r--aclocal.m468
-rw-r--r--alloc.h28
-rw-r--r--alloca.c492
-rw-r--r--bitops.c116
-rw-r--r--bitops.h31
-rw-r--r--config.h.in140
-rwxr-xr-xconfigure2013
-rw-r--r--configure.in77
-rw-r--r--depend.out40
-rw-r--r--fid.126
-rw-r--r--fid.c191
-rw-r--r--filenames.c603
-rw-r--r--filenames.h38
-rw-r--r--getopt.c748
-rw-r--r--getopt1.c180
-rw-r--r--gid.el22
-rw-r--r--idarg.h33
-rw-r--r--idfile.c184
-rw-r--r--idfile.h55
-rwxr-xr-xidtest32
-rw-r--r--idx.c88
-rw-r--r--iid.1235
-rw-r--r--iid.c2301
-rw-r--r--iid.help92
-rw-r--r--iid.y1334
-rwxr-xr-xinstall-sh238
-rw-r--r--lid.1211
-rw-r--r--lid.c1390
-rw-r--r--misc.c134
-rw-r--r--misc.h31
-rw-r--r--mkid.1187
-rw-r--r--mkid.c1091
-rw-r--r--mkid.info1094
-rw-r--r--mkid.texinfo953
-rw-r--r--scanners.c1209
-rw-r--r--scanners.h30
-rw-r--r--stamp-h.in1
-rw-r--r--strcasecmp.c77
-rw-r--r--strxtra.h35
-rw-r--r--token.c50
-rw-r--r--token.h40
49 files changed, 16802 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..16f85e6
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,118 @@
+ This is a generic INSTALL file for utilities distributions.
+If this package does not come with, e.g., installable documentation or
+data files, please ignore the references to them below.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation, and
+creates the Makefile(s) (one in each subdirectory of the source
+directory). In some packages it creates a C header file containing
+system-dependent definitions. It also creates a file `config.status'
+that you can run in the future to recreate the current configuration.
+
+To compile this package:
+
+1. Configure the package for your system.
+
+ Normally, you just `cd' to the directory containing the package's
+source code and type `./configure'. If you're using `csh' on an old
+version of System V, you might need to type `sh configure' instead to
+prevent `csh' from trying to execute `configure' itself.
+
+ Running `configure' takes a minute or two. While it is running, it
+prints some messages that tell what it is doing. If you don't want to
+see the messages, run `configure' with its standard output redirected
+to `/dev/null'; for example, `./configure >/dev/null'.
+
+ To compile the package in a different directory from the one
+containing the source code, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'. If
+for some reason `configure' is not in the source code directory that
+you are configuring, then it will report that it can't find the source
+code. In that case, run `configure' with the option `--srcdir=DIR',
+where DIR is the directory that contains the source code.
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'. Alternately, you can do so by consistently
+giving a value for the `prefix' variable when you run `make', e.g.,
+ make prefix=/usr/gnu
+ make prefix=/usr/gnu install
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH' or set the `make'
+variable `exec_prefix' to PATH, the package will use PATH as the prefix
+for installing programs and libraries. Data files and documentation
+will still use the regular prefix. Normally, all files are installed
+using the same prefix.
+
+ Some packages pay attention to `--with-PACKAGE' options to
+`configure', where PACKAGE is something like `gnu-as' or `x' (for the X
+Window System). The README should mention any `--with-' options that
+the package recognizes.
+
+ `configure' ignores any other arguments that you give it.
+
+ On systems that require unusual options for compilation or linking
+that the package's `configure' script does not know about, you can give
+`configure' initial values for variables by setting them in the
+environment. In Bourne-compatible shells, you can do that on the
+command line like this:
+
+ CC='gcc -traditional' LIBS=-lposix ./configure
+
+ Here are the `make' variables that you might want to override with
+environment variables when running `configure'.
+
+ For these variables, any value given in the environment overrides the
+value that `configure' would choose:
+
+ - Variable: CC
+ C compiler program. The default is `cc'.
+
+ - Variable: INSTALL
+ Program to use to install files. The default is `install' if you
+ have it, `cp' otherwise.
+
+ For these variables, any value given in the environment is added to
+the value that `configure' chooses:
+
+ - Variable: DEFS
+ Configuration options, in the form `-Dfoo -Dbar...'. Do not use
+ this variable in packages that create a configuration header file.
+
+ - Variable: LIBS
+ Libraries to link with, in the form `-lfoo -lbar...'.
+
+ If you need to do unusual things to compile the package, we encourage
+you to figure out how `configure' could check whether to do them, and
+mail diffs or instructions to the address given in the README so we
+can include them in the next release.
+
+2. Type `make' to compile the package. If you want, you can override
+the `make' variables CFLAGS and LDFLAGS like this:
+
+ make CFLAGS=-O2 LDFLAGS=-s
+
+3. If the package comes with self-tests and you want to run them,
+type `make check'. If you're not sure whether there are any, try it;
+if `make' responds with something like
+ make: *** No way to make target `check'. Stop.
+then the package does not come with self-tests.
+
+4. Type `make install' to install programs, data files, and
+documentation.
+
+5. You can remove the program binaries and object files from the
+source directory by typing `make clean'. To also remove the
+Makefile(s), the header file containing system-dependent definitions
+(if the package uses one), and `config.status' (all the files that
+`configure' created), type `make distclean'.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need it if you want to regenerate
+`configure' using a newer version of `autoconf'.
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..b256166
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,264 @@
+# Main Makefile for `mkid'.
+
+# Copyright (C) 1986, 1995 Greg McGary
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to the
+# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+##############################################################################
+
+PRODUCT = @PRODUCT@
+MAJOR_VERSION = @MAJOR_VERSION@
+MINOR_VERSION = @MINOR_VERSION@
+PATCH_LEVEL = @PATCH_LEVEL@
+VERISON = @VERSION@
+FULL_VERSION = @FULL_VERSION@
+
+SHELL = /bin/sh
+VPATH = @srcdir@
+@SET_MAKE@
+
+prefix = @prefix@
+srcdir = @srcdir@
+exec_prefix = @exec_prefix@
+bindir = $(exec_prefix)/bin
+infodir = $(prefix)/info
+libdir = @libdir@
+
+##############################################################################
+
+CC = @CC@
+YACC = @YACC@
+LN_S = @LN_S@
+MAKEINFO = makeinfo
+TEXI2DVI = texi2dvi
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+IID_HELP_FILE = @IID_HELP_FILE@
+DEPEND = @DEPEND@
+
+REGEXP = @REGEXP@
+STRCASECMP = @STRCASECMP@
+LIB_OFILES = @ALLOCA@ @GETOPT@ @STRERROR@
+
+##############################################################################
+
+TARGETS = $(PROGRAMS) $(LID_LINKS)
+PROGRAMS = mkid lid fid idx iid
+LID_LINKS = eid aid gid pid
+
+MKID_OFILES = mkid.o misc.o scanners.o idfile.o filenames.o bitops.o $(LIB_OFILES)
+LID_OFILES = lid.o misc.o idfile.o filenames.o bitops.o token.o $(REGEXP) $(LIB_OFILES)
+FID_OFILES = fid.o misc.o idfile.o filenames.o bitops.o token.o $(LIB_OFILES)
+IDX_OFILES = idx.o scanners.o filenames.o misc.o $(LIB_OFILES)
+IID_OFILES = iid.o $(STRCASECMP) $(LIB_OFILES)
+
+SRC_YFILES = iid.y
+
+SRC_CFILES = mkid.c lid.c fid.c idx.c misc.c scanners.c idfile.c \
+ filenames.c bitops.c token.c
+GEN_CFILES = $(SRC_YFILES:.y=.c)
+LIB_CFILES = regex.c alloca.c getopt.c getopt1.c strcasecmp.c strerror.c
+ALL_CFILES = $(SRC_CFILES) $(LIB_CFILES) $(GEN_CFILES)
+
+SRC_HFILES = alloc.h bitops.h filenames.h idarg.h \
+ idfile.h misc.h scanners.h strxtra.h token.h
+LIB_HFILES = regex.h getopt.h
+GEN_HFILES = config.h
+
+ID_SRC_FILES = $(SRC_HFILES) $(GEN_HFILES) $(LIB_HFILES) $(SRC_CFILES) $(LIB_CFILES) $(SRC_YFILES)
+DIST_SRC_FILES =$(ALL_CFILES) $(SRC_HFILES) $(LIB_HFILES) $(SRC_YFILES)
+
+DIST_DOC_FILES =mkid.texinfo mkid.info fid.1 iid.1 lid.1 mkid.1
+DIST_CONF_FILES=aclocal.m4 acconfig.h configure.in Makefile.in mkdirhier \
+ stamp-h.in config.h.in configure $(DEPEND)
+DIST_MISC_FILES=COPYING README INSTALL NEWS TODO THANKS idtest gid.el install-sh iid.help
+
+DIST_FILES = $(DIST_CONF_FILES) $(DIST_SRC_FILES) $(DIST_DOC_FILES) $(DIST_MISC_FILES)
+
+DIST_DIR = $(PRODUCT)-$(FULL_VERSION)
+DIST_TGZ = $(DIST_DIR).tar.gz
+DIST_UU = $(DIST_TGZ).uu
+
+# Note: OLD_DIST_DIR must be passed in through the environment
+# do "OLD_DIST_DIR=mkid-X.X.X make patch"
+PATCH_GZ = $(OLD_DIST_DIR)-$(FULL_VERSION).diff.gz
+PATCH_UU = $(PATCH_GZ).uu
+
+##############################################################################
+
+.SUFFIXES:
+.SUFFIXES: .c .i .o
+
+INCLUDES = -I$(srcdir)
+CC_FLAGS = $(DEFS) $(CPPFLAGS) $(INCLUDES) $(CFLAGS)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+# This is a convenient diagnostic aid: get a cpp'ed foo.c with "make foo.i"
+.c.i:
+ $(CPP) $(CC_FLAGS) $< >$@
+.c.o:
+ $(CC) $(CC_FLAGS) -c $<
+
+##############################################################################
+
+all: $(TARGETS)
+
+mkid: $(MKID_OFILES)
+ $(LINK) $(MKID_OFILES) $(LIBS)
+
+lid: $(LID_OFILES)
+ $(LINK) $(LID_OFILES) $(LIBS)
+
+fid: $(FID_OFILES)
+ $(LINK) $(FID_OFILES) $(LIBS)
+
+idx: $(IDX_OFILES)
+ $(LINK) $(IDX_OFILES) $(LIBS)
+
+iid: $(IID_OFILES)
+ $(LINK) $(IID_OFILES) $(LIBS)
+
+$(LID_LINKS): lid
+ rm -f $@; $(LN_S) lid $@
+
+iid.c: iid.y
+ $(YACC) $(srcdir)/iid.y && mv -f y.tab.c $@
+
+##############################################################################
+
+info: mkid.info
+
+mkid.info: mkid.texinfo version.texi
+ cd $(srcdir) && $(MAKEINFO) mkid.texinfo
+
+dvi: mkid.dvi
+
+mkid.dvi: mkid.texinfo version.texi
+ $(TEXI2DVI) $(srcdir)/mkid.texinfo
+
+version.texi: configure.in
+ echo "@set VERSION $(VERSION)" >version.tmp
+ if cmp -s version.tmp $(srcdir)/version.texi; then rm version.tmp; \
+ else mv version.tmp $(srcdir)/version.texi; fi
+
+##############################################################################
+
+install: all
+ $(srcdir)/mkdirhier $(bindir) $(libdir) $(infodir)
+ @for file in $(PROGRAMS); do \
+ cmd="$(INSTALL_PROGRAM) $$file $(bindir)/`echo $$file | sed '$(transform)'`"; \
+ echo $$cmd; eval $$cmd; \
+ done
+ @for file in $(LID_LINKS); do \
+ lid=`echo lid |sed '$(transform)'`; \
+ file=`echo $$file |sed '$(transform)'`; \
+ cmd="rm -f $(bindir)/$$file; $(LN_S) $(bindir)/$$lid $(bindir)/$$file"; \
+ echo $$cmd; eval $$cmd; \
+ done
+ $(INSTALL_DATA) $(srcdir)/iid.help $(IID_HELP_FILE)
+ @for file in `cd $(srcdir) && echo mkid.info*`; do \
+ cmd="$(INSTALL_DATA) $(srcdir)/$$file $(infodir)/$$file"; \
+ echo $$cmd; eval $$cmd; \
+ done
+
+uninstall:
+ @for file in $(TARGETS); do \
+ cmd="rm -f $(bindir)/`echo $$file |sed '$(transform)'`"; \
+ echo $$cmd; eval $$cmd; \
+ done
+ @for file in `cd $(infodir) && echo mkid.info*`; do \
+ cmd="rm -f $(infodir)/$$file"; \
+ echo $$cmd; eval $$cmd; \
+ done
+
+##############################################################################
+
+check: mkid lid fid idx
+ here=`pwd`; PATH=$$here:$$PATH; cd $(srcdir) \
+ && $$here/mkid -v -f$$here/ID $(SRC_FILES) \
+ && $(srcdir)/idtest -f$$here/ID $(SRC_FILES)
+
+ID: $(SRC_FILES) mkid
+ ./mkid $(SRC_FILES)
+
+tags: TAGS
+
+TAGS: $(SRC_FILES)
+ cd $(srcdir) && etags -t $(SRC_FILES)
+
+##############################################################################
+
+mostlyclean:
+ rm -f $(TARGETS) a.out
+ rm -f *.o core *.core core.* *.aux *.cp *.cps *.dvi *.fn *.fns
+ rm -f *.ky *.kys *.log *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs
+
+clean: mostlyclean
+ rm -f iid.c
+
+distclean: clean
+ rm -f Makefile config.h config.status config.cache stamp-h
+
+realclean: distclean
+ rm -f *.info* TAGS ID
+
+##############################################################################
+
+dist-dir: $(DIST_DIR)
+$(DIST_DIR): $(DIST_FILES)
+ rm -fr $@
+ mkdir $@
+ chmod 777 $@
+ distdir=`pwd`/$@; cd $(srcdir); ln $(DIST_FILES) $$distdir
+ chmod -R a+r $@
+
+dist dist-tgz: $(DIST_TGZ)
+$(DIST_TGZ): $(DIST_DIR)
+ tar cfzho $@ $(DIST_DIR)
+
+dist-uu: $(DIST_UU)
+$(DIST_UU): $(DIST_TGZ)
+ uuencode $(DIST_TGZ) <$(DIST_TGZ) >$@
+
+patch patch-gz: $(PATCH_GZ)
+$(PATCH_GZ): $(OLD_DIST_DIR) $(DIST_DIR)
+ diff -r -c --show-c-function --new-file $(OLD_DIST_DIR) $(DIST_DIR) |gzip >$@
+
+patch-uu: $(PATCH_UU)
+$(PATCH_UU): $(PATCH_GZ)
+ uuencode $(PATCH_GZ) <$(PATCH_GZ) >$@
+
+##############################################################################
+
+Makefile: Makefile.in config.status $(DEPEND)
+ CONFIG_FILES=$@ CONFIG_HEADERS= ./config.status
+config.status: configure
+ ./config.status --recheck
+$(srcdir)/configure: configure.in aclocal.m4
+ cd $(srcdir) && autoconf
+
+config.h: stamp-h
+stamp-h: $(srcdir)/config.h.in config.status
+ CONFIG_FILES= CONFIG_HEADERS=config.h ./config.status
+ date >stamp-h
+$(srcdir)/config.h.in: stamp-h.in
+$(srcdir)/stamp-h.in: configure.in aclocal.m4 acconfig.h
+ cd $(srcdir) && autoheader
+ date >$(srcdir)/stamp-h.in
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..35f1200
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,25 @@
+mkid NEWS - User visible changes.
+
+Version 3.0 - February 1995, by Greg McGary
+
+* mkid uses a new efficient algorithm for building the database. The old
+algorithm was O(n^2) for space and would exhaust memory or lead to page
+thrashing on very large programs. The new algorithm is linear with the
+size of the input for both time and space. It is now possible to build
+very large mkid databases covering many thousands of files totalling tens
+of megabytes and containing hundreds of thousands of unique tokens.
+
+* The database header is now byte-order and word-size independent. ID
+files may be shared across networks of dissimilar architectures.
+
+Version 2.0 - February 1991, by Tom Horsley
+
+
+Version 1.0 - September 1987, by Greg McGary
+
+* Initial release to comp.sources.unix
+
+Local Variables:
+mode: text
+fill-column: 75
+End:
diff --git a/README b/README
new file mode 100644
index 0000000..09acbd7
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+`mkid' is a simple, fast, high-capacity, language-independent
+identifier database tool. Actually, the term `identifier' is too
+limiting--`mkid' stores tokens, be they program identifiers of any
+form, literal numbers, or words of human-readable text.
+
+`mkid' was originally written by Greg McGary <gkm@magilla.cichlid.com>
+and posted to comp.sources.unix in September 1987. It was then
+maintained and enhanced by a loose knit group of programmers on the
+Internet led by Tom Horsley <Tom.Horsley@mail.hcsc.com>. Tom released
+`mkid2' on comp.sources.unix in February 1991. Since then, Greg
+McGary has resumed maintenance and is working on a greatly enhanced
+version 3 under GPL.
+
+See file `COPYING' for copying conditions.
+See file `INSTALL' for compilation and installation instructions.
+See file `NEWS' for a list of major changes in the current release.
+See file `THANKS' for a list of contributors.
+See file `TODO' for planned enhancements.
+
+Send bug reports to <gkm@magilla.cichlid.com>. A bug report is an
+adequate description of the problem: your input, what you expected,
+what you got, and why this is wrong. Diffs are welcome, but they only
+describe a solution, from which the problem might be uneasy to infer.
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..3e3d274
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,17 @@
+mkid THANKS file
+
+mkid and lid were originally written by Greg McGary. iid was written
+by Tom Horsley, who also maintained the programs for a time and
+released mkid2 to comp.sources.unix.
+
+Many people further contributed to GNU m4 by reporting problems,
+suggesting improvements, or submitting actual code. Here is a list of
+these people. If you contributed, but don't see your name here,
+please notify me at the address in the README file. Help me keep this
+list complete and error-free.
+
+Wolfgang Rupprecht wolfgang@wsrcc.com
+Tom Horsley Tom.Horsley@mail.hcsc.com
+Marty Leisner leisner@sdsp.mc.xerox.com
+
+... (I'm not done with this yet --gkm) ...
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..eca6c82
--- /dev/null
+++ b/TODO
@@ -0,0 +1,51 @@
+mkid TODO - Future directions
+
+* Bring all code into conformance with GNU project coding standards.
+ - Use command-line arguments, not the name of the program in argv[0]
+ to determine lid's behavior
+ - Use getopt_long () to parse command-line arguments.
+
+* Portability
+ - Fix filenames.c to handle non-UNIX file names properly.
+ - Fix sizeof (int) == sizeof (long) assumptions.
+ - The configure script tests for lots of things, but the code doesn't
+ use all of the results that it should.
+
+* lid
+ - change the name of the program to something a bit more
+ descriptive, e.g., "findid".
+ - use "long long" (if the compiler supports it) integer comparisons
+ in find_number().
+ - generalize the command-line interface for different behaviors.
+ User passes a command-format string for operating on the results
+ of a query:
+ %t = token, %f = crunched file list, %F = uncrunched file list
+ Recommend the following aliases to emulate old behavior:
+ alias lid="findid -e 'echo %t %f'"
+ alias eid="findid -e 'vi +/\\<%t\\>/ %F'" # for vi users
+ alias gid="findid -e 'egrep \\<%t\\> %F'"
+
+* mkid & lid
+ - store & retrieve floating point literals
+
+* mkid
+ - add a recursive file tree walker, so a complete list of file names
+ need not be supplied.
+ - Store attributes with symbols: type, func, variable struct-tag
+ enum-tag, keyword, etc. Store caller/callee relationships between
+ identifiers. Indicate which files have defns vs. uses. This info
+ is needed to support a cscope interface.
+
+* cscope
+ - add a cscope work-alike query interface (this is being done by
+ Maureen Lecuona <lecuona@paul.rutgers.edu>)
+
+* grep
+ - Hack GNU grep to use a mkid database for hints about which files to
+ search.
+
+
+Local Variables:
+mode: text
+fill-column: 75
+End:
diff --git a/acconfig.h b/acconfig.h
new file mode 100644
index 0000000..1700c07
--- /dev/null
+++ b/acconfig.h
@@ -0,0 +1,27 @@
+/* Special definitions for `mkid', processed by autoheader.
+ This file is in the public domain.
+*/
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to the name of the distribution. */
+#undef PRODUCT
+
+/* Define to 1 if ANSI function prototypes are usable. */
+#undef PROTOTYPES
+
+/* Define to the major version # of the distribution. */
+#undef MAJOR_VERSION
+
+/* Define to the minor version # of the distribution. */
+#undef MINOR_VERSION
+
+/* Define to the minor.minor version # of the distribution. */
+#undef VERSION
+
+/* Define to the minor.minor.patch_level # of the distribution. */
+#undef FULL_VERSION
+
+/* Define to filename of iid help text. */
+#undef IID_HELP_FILE
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..a4aeb99
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,68 @@
+# All fp_* macros are:
+# Copyright (C) 1992, 1994 Free Software Foundation, Inc.
+# Francois Pinard <pinard@iro.umontreal.ca>, 1992.
+
+# @defmac AC_PROG_CC_STDC
+# @maindex PROG_CC_STDC
+# @ovindex CC
+# If the C compiler in not in ANSI C mode by default, try to add an option
+# to output variable @code{CC} to make it so. This macro tries various
+# options that select ANSI C on some system or another. It considers the
+# compiler to be in ANSI C mode if it defines @code{__STDC__} to 1 and
+# handles function prototypes correctly.
+#
+# If you use this macro, you should check after calling it whether the C
+# compiler has been set to accept ANSI C; if not, the shell variable
+# @code{ac_cv_prog_cc_stdc} is set to @samp{no}. If you wrote your source
+# code in ANSI C, you can make an un-ANSIfied copy of it by using the
+# program @code{ansi2knr}, which comes with Ghostscript.
+# @end defmac
+
+define(fp_PROG_CC_STDC,
+[AC_MSG_CHECKING(for ${CC-cc} option to accept ANSI C)
+AC_CACHE_VAL(ac_cv_prog_cc_stdc,
+[ac_cv_prog_cc_stdc=no
+ac_save_CFLAGS="$CFLAGS"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX -Aa -D_HPUX_SOURCE
+# SVR4 -Xc
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" -Xc
+do
+ CFLAGS="$ac_save_CFLAGS $ac_arg"
+ AC_TRY_COMPILE(
+[#if !defined(__STDC__) || __STDC__ != 1
+choke me
+#endif
+], [int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};],
+[ac_cv_prog_cc_stdc="$ac_arg"; break])
+done
+CFLAGS="$ac_save_CFLAGS"
+])
+AC_MSG_RESULT($ac_cv_prog_cc_stdc)
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno) ;;
+ *) CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+])
+
+# Check for function prototypes.
+
+AC_DEFUN(fp_C_PROTOTYPES,
+[AC_REQUIRE([fp_PROG_CC_STDC])
+AC_MSG_CHECKING([for function prototypes])
+if test "$ac_cv_prog_cc_stdc" != no; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(PROTOTYPES)
+ U= ANSI2KNR=
+else
+ AC_MSG_RESULT(no)
+ U=_ ANSI2KNR=ansi2knr
+fi
+AC_SUBST(U)dnl
+AC_SUBST(ANSI2KNR)dnl
+])
diff --git a/alloc.h b/alloc.h
new file mode 100644
index 0000000..70122ce
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,28 @@
+/* alloc.h -- convenient interface macros for malloc(3) & friends
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _alloc_h_
+#define _alloc_h_
+
+#include <malloc.h>
+
+#define CALLOC(type, n) ((type *)calloc(sizeof(type), (n)))
+#define MALLOC(type, n) ((type *)malloc(sizeof(type) * (n)))
+#define REALLOC(old, type, n) ((type *)realloc((old), sizeof(type) * (n)))
+
+#endif /* not _alloc_h_ */
diff --git a/alloca.c b/alloca.c
new file mode 100644
index 0000000..7020f32
--- /dev/null
+++ b/alloca.c
@@ -0,0 +1,492 @@
+/* alloca.c -- allocate automatically reclaimed memory
+ (Mostly) portable public-domain implementation -- D A Gwyn
+
+ This implementation of the PWB library alloca function,
+ which is used to allocate space off the run-time stack so
+ that it is automatically reclaimed upon procedure exit,
+ was inspired by discussions with J. Q. Johnson of Cornell.
+ J.Otto Tennant <jot@cray.com> contributed the Cray support.
+
+ There are some preprocessor constants that can
+ be defined when compiling for your specific system, for
+ improved efficiency; however, the defaults should be okay.
+
+ The general concept of this implementation is to keep
+ track of all alloca-allocated blocks, and reclaim any
+ that are found to be deeper in the stack than the current
+ invocation. This heuristic does not reclaim storage as
+ soon as it becomes invalid, but it will do so eventually.
+
+ As a special case, alloca(0) reclaims storage without
+ allocating any. It is a good idea to use alloca(0) in
+ your main control loop, etc. to force garbage collection. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef emacs
+#include "blockinput.h"
+#endif
+
+/* If compiling with GCC 2, this file's not needed. */
+#if !defined (__GNUC__) || __GNUC__ < 2
+
+/* If someone has defined alloca as a macro,
+ there must be some other way alloca is supposed to work. */
+#ifndef alloca
+
+#ifdef emacs
+#ifdef static
+/* actually, only want this if static is defined as ""
+ -- this is for usg, in which emacs must undefine static
+ in order to make unexec workable
+ */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+/* If your stack is a linked list of frames, you have to
+ provide an "address metric" ADDRESS_FUNCTION macro. */
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+long i00afunc ();
+#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
+#else
+#define ADDRESS_FUNCTION(arg) &(arg)
+#endif
+
+#if __STDC__
+typedef void *pointer;
+#else
+typedef char *pointer;
+#endif
+
+#define NULL 0
+
+/* Different portions of Emacs need to call different versions of
+ malloc. The Emacs executable needs alloca to call xmalloc, because
+ ordinary malloc isn't protected from input signals. On the other
+ hand, the utilities in lib-src need alloca to call malloc; some of
+ them are very simple, and don't have an xmalloc routine.
+
+ Non-Emacs programs expect this to call use xmalloc.
+
+ Callers below should use malloc. */
+
+#ifndef emacs
+#define malloc xmalloc
+#endif
+extern pointer malloc ();
+
+/* Define STACK_DIRECTION if you know the direction of stack
+ growth for your system; otherwise it will be automatically
+ deduced at run-time.
+
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+
+#ifndef STACK_DIRECTION
+#define STACK_DIRECTION 0 /* Direction unknown. */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */
+
+#else /* STACK_DIRECTION == 0; need run-time code. */
+
+static int stack_dir; /* 1 or -1 once known. */
+#define STACK_DIR stack_dir
+
+static void
+find_stack_direction ()
+{
+ static char *addr = NULL; /* Address of first `dummy', once known. */
+ auto char dummy; /* To get stack address. */
+
+ if (addr == NULL)
+ { /* Initial entry. */
+ addr = ADDRESS_FUNCTION (dummy);
+
+ find_stack_direction (); /* Recurse once. */
+ }
+ else
+ {
+ /* Second entry. */
+ if (ADDRESS_FUNCTION (dummy) > addr)
+ stack_dir = 1; /* Stack grew upward. */
+ else
+ stack_dir = -1; /* Stack grew downward. */
+ }
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/* An "alloca header" is used to:
+ (a) chain together all alloca'ed blocks;
+ (b) keep track of stack depth.
+
+ It is very important that sizeof(header) agree with malloc
+ alignment chunk size. The following default should work okay. */
+
+#ifndef ALIGN_SIZE
+#define ALIGN_SIZE sizeof(double)
+#endif
+
+typedef union hdr
+{
+ char align[ALIGN_SIZE]; /* To force sizeof(header). */
+ struct
+ {
+ union hdr *next; /* For chaining headers. */
+ char *deep; /* For stack depth measure. */
+ } h;
+} header;
+
+static header *last_alloca_header = NULL; /* -> last alloca header. */
+
+/* Return a pointer to at least SIZE bytes of storage,
+ which will be automatically reclaimed upon exit from
+ the procedure that called alloca. Originally, this space
+ was supposed to be taken from the current stack frame of the
+ caller, but that method cannot be made to work for some
+ implementations of C, for example under Gould's UTX/32. */
+
+pointer
+alloca (size)
+ unsigned size;
+{
+ auto char probe; /* Probes stack depth: */
+ register char *depth = ADDRESS_FUNCTION (probe);
+
+#if STACK_DIRECTION == 0
+ if (STACK_DIR == 0) /* Unknown growth direction. */
+ find_stack_direction ();
+#endif
+
+ /* Reclaim garbage, defined as all alloca'd storage that
+ was allocated from deeper in the stack than currently. */
+
+ {
+ register header *hp; /* Traverses linked list. */
+
+#ifdef emacs
+ BLOCK_INPUT;
+#endif
+
+ for (hp = last_alloca_header; hp != NULL;)
+ if ((STACK_DIR > 0 && hp->h.deep > depth)
+ || (STACK_DIR < 0 && hp->h.deep < depth))
+ {
+ register header *np = hp->h.next;
+
+ free ((pointer) hp); /* Collect garbage. */
+
+ hp = np; /* -> next header. */
+ }
+ else
+ break; /* Rest are not deeper. */
+
+ last_alloca_header = hp; /* -> last valid storage. */
+
+#ifdef emacs
+ UNBLOCK_INPUT;
+#endif
+ }
+
+ if (size == 0)
+ return NULL; /* No allocation required. */
+
+ /* Allocate combined header + user data storage. */
+
+ {
+ register pointer new = malloc (sizeof (header) + size);
+ /* Address of header. */
+
+ ((header *) new)->h.next = last_alloca_header;
+ ((header *) new)->h.deep = depth;
+
+ last_alloca_header = (header *) new;
+
+ /* User storage begins just after header. */
+
+ return (pointer) ((char *) new + sizeof (header));
+ }
+}
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+
+#ifdef DEBUG_I00AFUNC
+#include <stdio.h>
+#endif
+
+#ifndef CRAY_STACK
+#define CRAY_STACK
+#ifndef CRAY2
+/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
+struct stack_control_header
+ {
+ long shgrow:32; /* Number of times stack has grown. */
+ long shaseg:32; /* Size of increments to stack. */
+ long shhwm:32; /* High water mark of stack. */
+ long shsize:32; /* Current size of stack (all segments). */
+ };
+
+/* The stack segment linkage control information occurs at
+ the high-address end of a stack segment. (The stack
+ grows from low addresses to high addresses.) The initial
+ part of the stack segment linkage control information is
+ 0200 (octal) words. This provides for register storage
+ for the routine which overflows the stack. */
+
+struct stack_segment_linkage
+ {
+ long ss[0200]; /* 0200 overflow words. */
+ long sssize:32; /* Number of words in this segment. */
+ long ssbase:32; /* Offset to stack base. */
+ long:32;
+ long sspseg:32; /* Offset to linkage control of previous
+ segment of stack. */
+ long:32;
+ long sstcpt:32; /* Pointer to task common address block. */
+ long sscsnm; /* Private control structure number for
+ microtasking. */
+ long ssusr1; /* Reserved for user. */
+ long ssusr2; /* Reserved for user. */
+ long sstpid; /* Process ID for pid based multi-tasking. */
+ long ssgvup; /* Pointer to multitasking thread giveup. */
+ long sscray[7]; /* Reserved for Cray Research. */
+ long ssa0;
+ long ssa1;
+ long ssa2;
+ long ssa3;
+ long ssa4;
+ long ssa5;
+ long ssa6;
+ long ssa7;
+ long sss0;
+ long sss1;
+ long sss2;
+ long sss3;
+ long sss4;
+ long sss5;
+ long sss6;
+ long sss7;
+ };
+
+#else /* CRAY2 */
+/* The following structure defines the vector of words
+ returned by the STKSTAT library routine. */
+struct stk_stat
+ {
+ long now; /* Current total stack size. */
+ long maxc; /* Amount of contiguous space which would
+ be required to satisfy the maximum
+ stack demand to date. */
+ long high_water; /* Stack high-water mark. */
+ long overflows; /* Number of stack overflow ($STKOFEN) calls. */
+ long hits; /* Number of internal buffer hits. */
+ long extends; /* Number of block extensions. */
+ long stko_mallocs; /* Block allocations by $STKOFEN. */
+ long underflows; /* Number of stack underflow calls ($STKRETN). */
+ long stko_free; /* Number of deallocations by $STKRETN. */
+ long stkm_free; /* Number of deallocations by $STKMRET. */
+ long segments; /* Current number of stack segments. */
+ long maxs; /* Maximum number of stack segments so far. */
+ long pad_size; /* Stack pad size. */
+ long current_address; /* Current stack segment address. */
+ long current_size; /* Current stack segment size. This
+ number is actually corrupted by STKSTAT to
+ include the fifteen word trailer area. */
+ long initial_address; /* Address of initial segment. */
+ long initial_size; /* Size of initial segment. */
+ };
+
+/* The following structure describes the data structure which trails
+ any stack segment. I think that the description in 'asdef' is
+ out of date. I only describe the parts that I am sure about. */
+
+struct stk_trailer
+ {
+ long this_address; /* Address of this block. */
+ long this_size; /* Size of this block (does not include
+ this trailer). */
+ long unknown2;
+ long unknown3;
+ long link; /* Address of trailer block of previous
+ segment. */
+ long unknown5;
+ long unknown6;
+ long unknown7;
+ long unknown8;
+ long unknown9;
+ long unknown10;
+ long unknown11;
+ long unknown12;
+ long unknown13;
+ long unknown14;
+ };
+
+#endif /* CRAY2 */
+#endif /* not CRAY_STACK */
+
+#ifdef CRAY2
+/* Determine a "stack measure" for an arbitrary ADDRESS.
+ I doubt that "lint" will like this much. */
+
+static long
+i00afunc (long *address)
+{
+ struct stk_stat status;
+ struct stk_trailer *trailer;
+ long *block, size;
+ long result = 0;
+
+ /* We want to iterate through all of the segments. The first
+ step is to get the stack status structure. We could do this
+ more quickly and more directly, perhaps, by referencing the
+ $LM00 common block, but I know that this works. */
+
+ STKSTAT (&status);
+
+ /* Set up the iteration. */
+
+ trailer = (struct stk_trailer *) (status.current_address
+ + status.current_size
+ - 15);
+
+ /* There must be at least one stack segment. Therefore it is
+ a fatal error if "trailer" is null. */
+
+ if (trailer == 0)
+ abort ();
+
+ /* Discard segments that do not contain our argument address. */
+
+ while (trailer != 0)
+ {
+ block = (long *) trailer->this_address;
+ size = trailer->this_size;
+ if (block == 0 || size == 0)
+ abort ();
+ trailer = (struct stk_trailer *) trailer->link;
+ if ((block <= address) && (address < (block + size)))
+ break;
+ }
+
+ /* Set the result to the offset in this segment and add the sizes
+ of all predecessor segments. */
+
+ result = address - block;
+
+ if (trailer == 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ if (trailer->this_size <= 0)
+ abort ();
+ result += trailer->this_size;
+ trailer = (struct stk_trailer *) trailer->link;
+ }
+ while (trailer != 0);
+
+ /* We are done. Note that if you present a bogus address (one
+ not in any segment), you will get a different number back, formed
+ from subtracting the address of the first block. This is probably
+ not what you want. */
+
+ return (result);
+}
+
+#else /* not CRAY2 */
+/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
+ Determine the number of the cell within the stack,
+ given the address of the cell. The purpose of this
+ routine is to linearize, in some sense, stack addresses
+ for alloca. */
+
+static long
+i00afunc (long address)
+{
+ long stkl = 0;
+
+ long size, pseg, this_segment, stack;
+ long result = 0;
+
+ struct stack_segment_linkage *ssptr;
+
+ /* Register B67 contains the address of the end of the
+ current stack segment. If you (as a subprogram) store
+ your registers on the stack and find that you are past
+ the contents of B67, you have overflowed the segment.
+
+ B67 also points to the stack segment linkage control
+ area, which is what we are really interested in. */
+
+ stkl = CRAY_STACKSEG_END ();
+ ssptr = (struct stack_segment_linkage *) stkl;
+
+ /* If one subtracts 'size' from the end of the segment,
+ one has the address of the first word of the segment.
+
+ If this is not the first segment, 'pseg' will be
+ nonzero. */
+
+ pseg = ssptr->sspseg;
+ size = ssptr->sssize;
+
+ this_segment = stkl - size;
+
+ /* It is possible that calling this routine itself caused
+ a stack overflow. Discard stack segments which do not
+ contain the target address. */
+
+ while (!(this_segment <= address && address <= stkl))
+ {
+#ifdef DEBUG_I00AFUNC
+ fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
+#endif
+ if (pseg == 0)
+ break;
+ stkl = stkl - pseg;
+ ssptr = (struct stack_segment_linkage *) stkl;
+ size = ssptr->sssize;
+ pseg = ssptr->sspseg;
+ this_segment = stkl - size;
+ }
+
+ result = address - this_segment;
+
+ /* If you subtract pseg from the current end of the stack,
+ you get the address of the previous stack segment's end.
+ This seems a little convoluted to me, but I'll bet you save
+ a cycle somewhere. */
+
+ while (pseg != 0)
+ {
+#ifdef DEBUG_I00AFUNC
+ fprintf (stderr, "%011o %011o\n", pseg, size);
+#endif
+ stkl = stkl - pseg;
+ ssptr = (struct stack_segment_linkage *) stkl;
+ size = ssptr->sssize;
+ pseg = ssptr->sspseg;
+ result += size;
+ }
+ return (result);
+}
+
+#endif /* not CRAY2 */
+#endif /* CRAY */
+
+#endif /* no alloca */
+#endif /* not GCC version 2 */
diff --git a/bitops.c b/bitops.c
new file mode 100644
index 0000000..4d49572
--- /dev/null
+++ b/bitops.c
@@ -0,0 +1,116 @@
+/* bitops.c -- Bit-vector manipulation for mkid
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "bitops.h"
+
+static int str_to_int (char *bufp, int size);
+static char *int_to_str (int i, int size);
+
+int
+vec_to_bits (char *bit_array, char *vec, int size)
+{
+ int i;
+ int count;
+
+ for (count = 0; (*vec & 0xff) != 0xff; count++)
+ {
+ i = str_to_int (vec, size);
+ BITSET (bit_array, i);
+ vec += size;
+ }
+ return count;
+}
+
+int
+bits_to_vec (char *vec, char *bit_array, int bit_count, int size)
+{
+ char *element;
+ int i;
+ int count;
+
+ for (count = i = 0; i < bit_count; i++)
+ {
+ if (!BITTST (bit_array, i))
+ continue;
+ element = int_to_str (i, size);
+ switch (size)
+ {
+ case 4:
+ *vec++ = *element++;
+ case 3:
+ *vec++ = *element++;
+ case 2:
+ *vec++ = *element++;
+ case 1:
+ *vec++ = *element++;
+ }
+ count++;
+ }
+ *vec++ = 0xff;
+
+ return count;
+}
+
+/* NEEDSWORK: ENDIAN */
+
+static char *
+int_to_str (int i, int size)
+{
+ static char buf0[4];
+ char *bufp = &buf0[size];
+
+ switch (size)
+ {
+ case 4:
+ *--bufp = (i & 0xff);
+ i >>= 8;
+ case 3:
+ *--bufp = (i & 0xff);
+ i >>= 8;
+ case 2:
+ *--bufp = (i & 0xff);
+ i >>= 8;
+ case 1:
+ *--bufp = (i & 0xff);
+ }
+ return buf0;
+}
+
+static int
+str_to_int (char *bufp, int size)
+{
+ int i = 0;
+
+ bufp--;
+ switch (size)
+ {
+ case 4:
+ i |= (*++bufp & 0xff);
+ i <<= 8;
+ case 3:
+ i |= (*++bufp & 0xff);
+ i <<= 8;
+ case 2:
+ i |= (*++bufp & 0xff);
+ i <<= 8;
+ case 1:
+ i |= (*++bufp & 0xff);
+ }
+ return i;
+}
diff --git a/bitops.h b/bitops.h
new file mode 100644
index 0000000..7e0f0a7
--- /dev/null
+++ b/bitops.h
@@ -0,0 +1,31 @@
+/* bitops.h -- defs for interface to bitops.c, plus bit-vector macros
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _bitops_h_
+#define _bitops_h_
+
+#define BITTST(ba, bn) ((ba)[(bn) >> 3] & (1 << ((bn) & 0x07)))
+#define BITSET(ba, bn) ((ba)[(bn) >> 3] |= (1 << ((bn) & 0x07)))
+#define BITCLR(ba, bn) ((ba)[(bn) >> 3] &=~(1 << ((bn) & 0x07)))
+#define BITAND(ba, bn) ((ba)[(bn) >> 3] &= (1 << ((bn) & 0x07)))
+#define BITXOR(ba, bn) ((ba)[(bn) >> 3] ^= (1 << ((bn) & 0x07)))
+
+int vec_to_bits (char *bit_array, char *vec, int size);
+int bits_to_vec (char *vec, char *bit_array, int bit_count, int size);
+
+#endif /* _bitops_h_ */
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..ae5350f
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,140 @@
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if using alloca.c. */
+#undef C_ALLOCA
+
+/* Define to empty if the keyword does not work. */
+#undef const
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+#undef CRAY_STACKSEG_END
+
+/* Define if you have alloca, as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+#undef HAVE_ALLOCA_H
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+#undef off_t
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+#undef size_t
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#undef STACK_DIRECTION
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to the name of the distribution. */
+#undef PRODUCT
+
+/* Define to 1 if ANSI function prototypes are usable. */
+#undef PROTOTYPES
+
+/* Define to the version of the distribution. */
+#undef VERSION
+
+/* Define to filename of iid help text. */
+#undef IID_HELP_FILE
+
+/* Define if you have the bcopy function. */
+#undef HAVE_BCOPY
+
+/* Define if you have the bzero function. */
+#undef HAVE_BZERO
+
+/* Define if you have the getcwd function. */
+#undef HAVE_GETCWD
+
+/* Define if you have the getopt_long function. */
+#undef HAVE_GETOPT_LONG
+
+/* Define if you have the getwd function. */
+#undef HAVE_GETWD
+
+/* Define if you have the memcpy function. */
+#undef HAVE_MEMCPY
+
+/* Define if you have the memset function. */
+#undef HAVE_MEMSET
+
+/* Define if you have the re_comp function. */
+#undef HAVE_RE_COMP
+
+/* Define if you have the strcasecmp function. */
+#undef HAVE_STRCASECMP
+
+/* Define if you have the strchr function. */
+#undef HAVE_STRCHR
+
+/* Define if you have the strdup function. */
+#undef HAVE_STRDUP
+
+/* Define if you have the strrchr function. */
+#undef HAVE_STRRCHR
+
+/* Define if you have the <assert.h> header file. */
+#undef HAVE_ASSERT_H
+
+/* Define if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <sgttyb.h> header file. */
+#undef HAVE_SGTTYB_H
+
+/* Define if you have the <stddef.h> header file. */
+#undef HAVE_STDDEF_H
+
+/* Define if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define if you have the <termio.h> header file. */
+#undef HAVE_TERMIO_H
+
+/* Define if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
diff --git a/configure b/configure
new file mode 100755
index 0000000..f95fec0
--- /dev/null
+++ b/configure
@@ -0,0 +1,2013 @@
+#!/bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.1
+# Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Initialize some other variables.
+subdirs=
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -build | --build | --buil | --bui | --bu | --b)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=* | --b=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=PREFIX install architecture-dependent files in PREFIX
+ [same as prefix]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+--enable and --with options recognized:$ac_help
+EOF
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.1"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 unused; standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 unused; some systems may open it to /dev/tty
+# 4 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 4>/dev/null
+else
+ exec 4>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=mkid.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} $CFLAGS $CPPFLAGS conftest.$ac_ext -c 1>&5 2>&5'
+ac_link='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext -o conftest $LIBS 1>&5 2>&5'
+
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+PRODUCT=mkid
+MAJOR_VERSION=3
+MINOR_VERSION=0
+PATCH_LEVEL=2
+VERSION=$MAJOR_VERSION.$MINOR_VERSION
+FULL_VERSION=$VERSION.$PATCH_LEVEL
+cat >> confdefs.h <<EOF
+#define PRODUCT "$PRODUCT"
+EOF
+
+cat >> confdefs.h <<EOF
+#define MAJOR_VERSION "$MAJOR_VERSION"
+EOF
+
+cat >> confdefs.h <<EOF
+#define MINOR_VERSION "$MINOR_VERSION"
+EOF
+
+cat >> confdefs.h <<EOF
+#define PATCH_LEVEL "$PATCH_LEVEL"
+EOF
+
+cat >> confdefs.h <<EOF
+#define VERSION "$VERSION"
+EOF
+
+cat >> confdefs.h <<EOF
+#define FULL_VERSION "$FULL_VERSION"
+EOF
+
+
+
+
+
+
+
+
+for ac_prog in 'bison -y' byacc
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_YACC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ if test -n "$YACC"; then
+ ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_YACC="$ac_prog"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+YACC="$ac_cv_prog_YACC"
+if test -n "$YACC"; then
+ echo "$ac_t""$YACC" 1>&4
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="cc"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&4
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if ${CC-cc} -E conftest.c 2>&5 | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+echo "$ac_t""$ac_cv_prog_gcc" 1>&4
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+ if test "${CFLAGS+set}" != set; then
+ echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_gcc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_gcc_g=yes
+else
+ ac_cv_prog_gcc_g=no
+fi
+rm -f conftest*
+
+fi
+ echo "$ac_t""$ac_cv_prog_gcc_g" 1>&4
+ if test $ac_cv_prog_gcc_g = yes; then
+ CFLAGS="-g -O"
+ else
+ CFLAGS="-O"
+ fi
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+echo $ac_n "checking whether ln -s works""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_LN_S'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ rm -f conftestdata
+if ln -s X conftestdata 2>/dev/null
+then
+ rm -f conftestdata
+ ac_cv_prog_LN_S="ln -s"
+else
+ ac_cv_prog_LN_S=ln
+fi
+fi
+LN_S="$ac_cv_prog_LN_S"
+if test "$ac_cv_prog_LN_S" = "ln -s"; then
+ echo "$ac_t""yes" 1>&4
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&4
+if test -z "$INSTALL"; then
+if eval "test \"`echo '${'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+ for ac_dir in $PATH; do
+ case "$ac_dir" in
+ ''|.|/etc|/usr/sbin|/usr/etc|/sbin|/usr/afsws/bin|/usr/ucb) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ for ac_prog in ginstall installbsd scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ # OSF/1 installbsd also uses dspmsg, but is usable.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_ifs"
+ # As a last resort, use the slow shell script.
+ test -z "$ac_cv_path_install" && ac_cv_path_install="$ac_install_sh"
+fi
+ INSTALL="$ac_cv_path_install"
+fi
+echo "$ac_t""$INSTALL" 1>&4
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking whether ${MAKE-make} sets \$MAKE""... $ac_c" 1>&4
+set dummy ${MAKE-make}; ac_make=$2
+if eval "test \"`echo '${'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&4
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&4
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '${'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 680 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 694 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+fi
+CPP="$ac_cv_prog_CPP"
+echo "$ac_t""$CPP" 1>&4
+
+# If we cannot run a trivial program, we must be cross compiling.
+echo $ac_n "checking whether cross-compiling""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_c_cross'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_cross=yes
+else
+cat > conftest.$ac_ext <<EOF
+#line 726 "configure"
+#include "confdefs.h"
+main(){return(0);}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ ac_cv_c_cross=no
+else
+ ac_cv_c_cross=yes
+fi
+fi
+rm -fr conftest*
+fi
+cross_compiling=$ac_cv_c_cross
+echo "$ac_t""$ac_cv_c_cross" 1>&4
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 747 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 769 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 787 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ ac_cv_header_stdc=no
+else
+cat > conftest.$ac_ext <<EOF
+#line 808 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+echo "$ac_t""$ac_cv_header_stdc" 1>&4
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_sys_wait_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 842 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+int main() { return 0; }
+int t() {
+int s;
+wait (&s);
+s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=yes
+else
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&4
+if test $ac_cv_header_sys_wait_h = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS_WAIT_H 1
+EOF
+
+fi
+
+for ac_hdr in limits.h stddef.h sys/types.h sys/stat.h sys/param.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 885 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./\055' '[A-Z]___'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+for ac_hdr in stdlib.h unistd.h string.h malloc.h memory.h assert.h ctype.h errno.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 921 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./\055' '[A-Z]___'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+for ac_hdr in sys/ioctl.h termios.h termio.h sgttyb.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 957 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./\055' '[A-Z]___'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_c_const'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 991 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+
+/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this. */
+struct point {int x, y;};
+static struct point const zero;
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+}
+
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_c_const=yes
+else
+ rm -rf conftest*
+ ac_cv_c_const=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_c_const" 1>&4
+if test $ac_cv_c_const = no; then
+ cat >> confdefs.h <<\EOF
+#define const
+EOF
+
+fi
+
+echo $ac_n "checking for ${CC-cc} option to accept ANSI C""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_cc_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_cv_prog_cc_stdc=no
+ac_save_CFLAGS="$CFLAGS"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX -Aa -D_HPUX_SOURCE
+# CX/UX -Xa (-Xc is strict ANSI)
+# SVR4 -Xc
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" -Xa -Xc
+do
+ CFLAGS="$ac_save_CFLAGS $ac_arg"
+ cat > conftest.$ac_ext <<EOF
+#line 1076 "configure"
+#include "confdefs.h"
+#if !defined(__STDC__) || __STDC__ != 1
+choke me
+#endif
+
+int main() { return 0; }
+int t() {
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_prog_cc_stdc="$ac_arg"; break
+fi
+rm -f conftest*
+
+done
+CFLAGS="$ac_save_CFLAGS"
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_stdc" 1>&4
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno) ;;
+ *) CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+
+echo $ac_n "checking for function prototypes""... $ac_c" 1>&4
+if test "$ac_cv_prog_cc_stdc" != no; then
+ echo "$ac_t""yes" 1>&4
+ cat >> confdefs.h <<\EOF
+#define PROTOTYPES 1
+EOF
+
+ U= ANSI2KNR=
+else
+ echo "$ac_t""no" 1>&4
+ U=_ ANSI2KNR=ansi2knr
+fi
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1125 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+extern void (*signal ()) ();
+int main() { return 0; }
+int t() {
+int i;
+; return 0; }
+EOF
+if eval $ac_compile; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_signal" 1>&4
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for off_t""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_type_off_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1159 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "off_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_off_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_off_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_off_t" 1>&4
+if test $ac_cv_type_off_t = no; then
+ cat >> confdefs.h <<\EOF
+#define off_t long
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_type_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1190 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "size_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_size_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&4
+if test $ac_cv_type_size_t = no; then
+ cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+
+CFLAGS=${CFLAGS--g}
+LDFLAGS=${LDFLAGS--g}
+
+
+
+if test $ac_cv_prog_gcc = yes; then
+ echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ ac_pattern="Autoconf.*'x'"
+ cat > conftest.$ac_ext <<EOF
+#line 1229 "configure"
+#include "confdefs.h"
+#include <sgtty.h>
+Autoconf TIOCGETP
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+else
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat > conftest.$ac_ext <<EOF
+#line 1247 "configure"
+#include "confdefs.h"
+#include <termio.h>
+Autoconf TCGETA
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+ echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&4
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+for ac_func in getwd getcwd getopt_long re_comp
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1274 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+for ac_func in strdup strchr strrchr strcasecmp strerror
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1323 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+for ac_func in memcpy bcopy memset bzero
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1372 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+done
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments. Useless!
+echo $ac_n "checking for working alloca.h""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_header_alloca_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1421 "configure"
+#include "confdefs.h"
+#include <alloca.h>
+int main() { return 0; }
+int t() {
+char *p = alloca(2 * sizeof(int));
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ ac_cv_header_alloca_h=yes
+else
+ rm -rf conftest*
+ ac_cv_header_alloca_h=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_header_alloca_h" 1>&4
+if test $ac_cv_header_alloca_h = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ALLOCA_H 1
+EOF
+
+fi
+
+echo $ac_n "checking for alloca""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_alloca'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1452 "configure"
+#include "confdefs.h"
+
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif
+# endif
+# endif
+#endif
+
+int main() { return 0; }
+int t() {
+char *p = (char *) alloca(1);
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ ac_cv_func_alloca=yes
+else
+ rm -rf conftest*
+ ac_cv_func_alloca=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_func_alloca" 1>&4
+if test $ac_cv_func_alloca = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ALLOCA 1
+EOF
+
+fi
+
+if test $ac_cv_func_alloca = no; then
+ # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+ # that cause trouble. Some versions do not even contain alloca or
+ # contain a buggy version. If you still want to use their alloca,
+ # use ar to extract alloca.o from them instead of compiling alloca.c.
+ ALLOCA=alloca.o
+ cat >> confdefs.h <<\EOF
+#define C_ALLOCA 1
+EOF
+
+
+echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_os_cray'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1510 "configure"
+#include "confdefs.h"
+#if defined(CRAY) && ! defined(CRAY2)
+webecray
+#else
+wenotbecray
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "webecray" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_os_cray=yes
+else
+ rm -rf conftest*
+ ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_os_cray" 1>&4
+if test $ac_cv_os_cray = yes; then
+echo $ac_n "checking for _getb67""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func__getb67'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1537 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char _getb67();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub__getb67) || defined (__stub____getb67)
+choke me
+#else
+_getb67();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func__getb67=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func__getb67=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'_getb67`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ cat >> confdefs.h <<\EOF
+#define CRAY_STACKSEG_END _getb67
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+echo $ac_n "checking for GETB67""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_GETB67'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1580 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char GETB67();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_GETB67) || defined (__stub___GETB67)
+choke me
+#else
+GETB67();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_GETB67=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_GETB67=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'GETB67`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ cat >> confdefs.h <<\EOF
+#define CRAY_STACKSEG_END GETB67
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+echo $ac_n "checking for getb67""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_func_getb67'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1623 "configure"
+#include "confdefs.h"
+#include <ctype.h> /* Arbitrary system header to define __stub macros. */
+/* Override any gcc2 internal prototype to avoid an error. */
+char getb67();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_getb67) || defined (__stub___getb67)
+choke me
+#else
+getb67();
+#endif
+
+; return 0; }
+EOF
+if eval $ac_link; then
+ rm -rf conftest*
+ eval "ac_cv_func_getb67=yes"
+else
+ rm -rf conftest*
+ eval "ac_cv_func_getb67=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'getb67`\" = yes"; then
+ echo "$ac_t""yes" 1>&4
+ cat >> confdefs.h <<\EOF
+#define CRAY_STACKSEG_END getb67
+EOF
+
+else
+ echo "$ac_t""no" 1>&4
+fi
+
+fi
+
+fi
+
+fi
+
+echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&4
+if eval "test \"`echo '${'ac_cv_c_stack_direction'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&4
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_c_stack_direction=0
+else
+cat > conftest.$ac_ext <<EOF
+#line 1677 "configure"
+#include "confdefs.h"
+find_stack_direction ()
+{
+ static char *addr = 0;
+ auto char dummy;
+ if (addr == 0)
+ {
+ addr = &dummy;
+ return find_stack_direction ();
+ }
+ else
+ return (&dummy > addr) ? 1 : -1;
+}
+main ()
+{
+ exit (find_stack_direction() < 0);
+}
+EOF
+eval $ac_link
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ ac_cv_c_stack_direction=1
+else
+ ac_cv_c_stack_direction=-1
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_c_stack_direction" 1>&4
+cat >> confdefs.h <<EOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+EOF
+
+fi
+
+
+if test "$ac_cv_func_re_comp" = no; then REGEXP=rx.o; fi
+
+if test "$ac_cv_func_getopt_long" = no; then GETOPT="getopt.o getopt1.o"; fi
+
+if test "$ac_cv_func_strcasecmp" = no; then STRCASECMP="strcasecmp.o"; fi
+
+
+if test "x$prefix" != xNONE; then
+ libdir=$prefix/lib
+else
+ libdir=$ac_default_prefix/lib
+fi
+IID_HELP_FILE=$libdir/iid.help
+
+
+cat >> confdefs.h <<EOF
+#define IID_HELP_FILE "$libdir/iid.help"
+EOF
+
+
+trap '' 1 2 15
+if test -w $cache_file; then
+echo "updating cache $cache_file"
+cat > $cache_file <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# Ultrix sh set writes to stderr and can't be redirected directly.
+(set) 2>&1 |
+ sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/: \${\1='\2'}/p" \
+ >> $cache_file
+else
+echo "not updating unwritable cache $cache_file"
+fi
+
+trap 'rm -fr conftest* confdefs* core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#!/bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.1"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr Makefile config.h conftest*; exit 1' 1 2 15
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF
+$ac_vpsub
+$extrasub
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@PRODUCT@%$PRODUCT%g
+s%@MAJOR_VERSION@%$MAJOR_VERSION%g
+s%@MINOR_VERSION@%$MINOR_VERSION%g
+s%@PATCH_LEVEL@%$PATCH_LEVEL%g
+s%@VERSION@%$VERSION%g
+s%@FULL_VERSION@%$FULL_VERSION%g
+s%@YACC@%$YACC%g
+s%@CC@%$CC%g
+s%@LN_S@%$LN_S%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@CPP@%$CPP%g
+s%@U@%$U%g
+s%@ANSI2KNR@%$ANSI2KNR%g
+s%@ALLOCA@%$ALLOCA%g
+s%@REGEXP@%$REGEXP%g
+s%@GETOPT@%$GETOPT%g
+s%@STRCASECMP@%$STRCASECMP%g
+s%@libdir@%$libdir%g
+s%@IID_HELP_FILE@%$IID_HELP_FILE%g
+
+CEOF
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust relative srcdir, etc. for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/$ac_dir"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file
+fi; done
+rm -f conftest.subs
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+CONFIG_HEADERS=${CONFIG_HEADERS-"config.h"}
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ cp $ac_given_srcdir/$ac_file_in conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) \(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+# Maximum number of lines to put in a single here document.
+ac_max_here_lines=12
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..830f227
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,77 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(mkid.c)
+AC_CONFIG_HEADER(config.h)
+
+PRODUCT=mkid
+MAJOR_VERSION=3
+MINOR_VERSION=0
+PATCH_LEVEL=2
+VERSION=$MAJOR_VERSION.$MINOR_VERSION
+FULL_VERSION=$VERSION.$PATCH_LEVEL
+AC_DEFINE_UNQUOTED(PRODUCT, "$PRODUCT")
+AC_DEFINE_UNQUOTED(MAJOR_VERSION, "$MAJOR_VERSION")
+AC_DEFINE_UNQUOTED(MINOR_VERSION, "$MINOR_VERSION")
+AC_DEFINE_UNQUOTED(PATCH_LEVEL, "$PATCH_LEVEL")
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
+AC_DEFINE_UNQUOTED(FULL_VERSION, "$FULL_VERSION")
+AC_SUBST(PRODUCT)
+AC_SUBST(MAJOR_VERSION)
+AC_SUBST(MINOR_VERSION)
+AC_SUBST(PATCH_LEVEL)
+AC_SUBST(VERSION)
+AC_SUBST(FULL_VERSION)
+
+dnl Checks for programs.
+AC_PROG_YACC
+AC_SUBST(YACC)
+AC_PROG_CC
+AC_PROG_LN_S
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+dnl AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(limits.h stddef.h sys/types.h sys/stat.h sys/param.h)
+AC_CHECK_HEADERS(stdlib.h unistd.h string.h malloc.h memory.h assert.h ctype.h errno.h)
+AC_CHECK_HEADERS(sys/ioctl.h termios.h termio.h sgttyb.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+fp_C_PROTOTYPES
+AC_TYPE_SIGNAL
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+
+CFLAGS=${CFLAGS--g}
+LDFLAGS=${LDFLAGS--g}
+AC_SUBST(CFLAGS)
+AC_SUBST(LDFLAGS)
+
+dnl Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_CHECK_FUNCS(getwd getcwd getopt_long re_comp strdup strchr strrchr strcasecmp)
+AC_CHECK_FUNCS(memcpy bcopy memset bzero)
+AC_FUNC_ALLOCA
+AC_SUBST(ALLOCA)
+if test "$ac_cv_func_re_comp" = no; then REGEXP=rx.o; fi
+AC_SUBST(REGEXP)
+if test "$ac_cv_func_getopt_long" = no; then GETOPT="getopt.o getopt1.o"; fi
+AC_SUBST(GETOPT)
+if test "$ac_cv_func_strcasecmp" = no; then STRCASECMP="strcasecmp.o"; fi
+AC_SUBST(STRCASECMP)
+
+if test "x$prefix" != xNONE; then
+ libdir=$prefix/lib
+else
+ libdir=$ac_default_prefix/lib
+fi
+IID_HELP_FILE=$libdir/iid.help
+AC_SUBST(libdir)
+AC_SUBST(IID_HELP_FILE)
+AC_DEFINE_UNQUOTED(IID_HELP_FILE, "$libdir/iid.help")
+
+AC_OUTPUT(Makefile)
diff --git a/depend.out b/depend.out
new file mode 100644
index 0000000..f712130
--- /dev/null
+++ b/depend.out
@@ -0,0 +1,40 @@
+mkid.o: mkid.c config.h strxtra.h alloc.h idfile.h idarg.h token.h bitops.h misc.h \
+ filenames.h scanners.h
+lid.o: lid.c config.h alloc.h idfile.h idarg.h token.h bitops.h strxtra.h misc.h \
+ filenames.h
+fid.o: fid.c config.h idfile.h idarg.h bitops.h filenames.h misc.h strxtra.h \
+ alloc.h token.h
+idx.o: idx.c config.h misc.h filenames.h scanners.h
+misc.o: misc.c config.h strxtra.h misc.h
+scanners.o: scanners.c config.h strxtra.h token.h alloc.h scanners.h
+idfile.o: idfile.c config.h alloc.h idfile.h idarg.h strxtra.h
+filenames.o: filenames.c config.h strxtra.h filenames.h misc.h
+bitops.o: bitops.c config.h bitops.h
+token.o: token.c config.h token.h
+regex.o: regex.c regex.h
+alloca.o: alloca.c
+getopt.o: getopt.c
+getopt1.o: getopt1.c getopt.h
+strcasecmp.o: strcasecmp.c
+strerror.o: strerror.c
+iid.o: iid.c config.h strxtra.h
+mkid.o: mkid.c config.h strxtra.h alloc.h idfile.h idarg.h token.h bitops.h misc.h \
+ filenames.h scanners.h
+lid.o: lid.c config.h alloc.h idfile.h idarg.h token.h bitops.h strxtra.h misc.h \
+ filenames.h
+fid.o: fid.c config.h idfile.h idarg.h bitops.h filenames.h misc.h strxtra.h \
+ alloc.h token.h
+idx.o: idx.c config.h misc.h filenames.h scanners.h
+misc.o: misc.c config.h strxtra.h misc.h
+scanners.o: scanners.c config.h strxtra.h token.h alloc.h scanners.h
+idfile.o: idfile.c config.h alloc.h idfile.h idarg.h strxtra.h
+filenames.o: filenames.c config.h strxtra.h filenames.h misc.h
+bitops.o: bitops.c config.h bitops.h
+token.o: token.c config.h token.h
+regex.o: regex.c regex.h
+alloca.o: alloca.c
+getopt.o: getopt.c
+getopt1.o: getopt1.c getopt.h
+strcasecmp.o: strcasecmp.c
+strerror.o: strerror.c
+iid.o: iid.c ./config.h ./strxtra.h
diff --git a/fid.1 b/fid.1
new file mode 100644
index 0000000..d504e80
--- /dev/null
+++ b/fid.1
@@ -0,0 +1,26 @@
+.TH FID 1
+.SH NAME
+fid \- query id database for specific files
+.SH SYNOPSIS
+.B fid
+.RB [ \-f \^file]
+file1 [ file2 ]
+.SH DESCRIPTION
+.I Fid
+is a query tool for the id database. If you specify a single file
+name as an argument, it prints a list of all the identifiers that
+occur in that file.
+.PP
+When you give it two file names it takes the intersection. It prints
+only the list of identifiers that occur in both files.
+.PP
+The following options are recognized:
+.TP 10
+.BR \-f file\^
+Use
+.I file\^
+as the database instead of the default
+.BR ID .
+.SH SEE ALSO
+mkid(1),
+lid(1).
diff --git a/fid.c b/fid.c
new file mode 100644
index 0000000..a2cb1a2
--- /dev/null
+++ b/fid.c
@@ -0,0 +1,191 @@
+/* fid.c -- list all tokens in the given file(s)
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "idfile.h"
+#include "idarg.h"
+#include "bitops.h"
+#include "filenames.h"
+#include "misc.h"
+#include "strxtra.h"
+#include "alloc.h"
+#include "token.h"
+
+int get_idarg_index (char const *file_name);
+int is_hit (unsigned char const *hits, int file_number);
+int is_hit_1 (unsigned char const **hits, int level, int file_number);
+void skip_hits (unsigned char const **hits, int level);
+
+FILE *id_FILE;
+struct idhead idh;
+struct idarg *idarg_0;
+int tree8_levels;
+char const *program_name;
+
+static void
+usage (void)
+{
+ fprintf (stderr, "Usage: %s [-f<file>] file1 file2\n", program_name);
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ char const *id_file = IDFILE;
+ char *buf;
+ int op;
+ int i;
+ int index_1 = -1;
+ int index_2 = -1;
+
+ program_name = basename (GETARG (argc, argv));
+
+ while (argc)
+ {
+ char const *arg = GETARG (argc, argv);
+ switch (op = *arg++)
+ {
+ case '-':
+ case '+':
+ break;
+ default:
+ UNGETARG (argc, argv);
+ goto argsdone;
+ }
+ while (*arg)
+ switch (*arg++)
+ {
+ case 'f':
+ id_file = arg;
+ goto nextarg;
+ default:
+ usage ();
+ }
+ nextarg:;
+ }
+argsdone:
+
+ id_file = look_up (id_file);
+ if (id_file == NULL)
+ {
+ filerr ("open", id_file);
+ return 1;
+ }
+ id_FILE = init_idfile (id_file, &idh, &idarg_0);
+ if (id_FILE == NULL)
+ {
+ filerr ("open", id_file);
+ return 1;
+ }
+ switch (argc)
+ {
+ case 2:
+ index_2 = get_idarg_index (argv[1]);
+ /* fall through */
+ case 1:
+ index_1 = get_idarg_index (argv[0]);
+ break;
+ default:
+ usage ();
+ }
+
+ if (index_1 < 0)
+ return 1;
+
+ buf = MALLOC (char, idh.idh_buf_size);
+ fseek (id_FILE, idh.idh_tokens_offset, 0);
+ tree8_levels = tree8_count_levels (idh.idh_paths);
+
+ for (i = 0; i < idh.idh_tokens; i++)
+ {
+ unsigned char const *hits;
+
+ gets_past_00 (buf, id_FILE);
+ hits = tok_hits_addr (buf);
+ if (is_hit (hits, index_1) && (index_2 < 0 || is_hit (hits, index_2)))
+ printf ("%s\n", tok_string (buf));
+ }
+
+ return 0;
+}
+
+int
+get_idarg_index (char const *file_name)
+{
+ struct idarg *idarg;
+ int file_name_length = strlen (file_name);
+ struct idarg *end = &idarg_0[idh.idh_paths];
+
+ for (idarg = idarg_0; idarg < end; ++idarg)
+ {
+ int arg_length = strlen (idarg->ida_arg);
+ int prefix_length = arg_length - file_name_length;
+ if (prefix_length < 0
+ || (prefix_length > 0 && idarg->ida_arg[prefix_length - 1] != '/'))
+ continue;
+ if (strequ (&idarg->ida_arg[prefix_length], file_name))
+ return idarg->ida_index;
+ }
+ fprintf (stderr, "%s: not found\n", file_name);
+ return -1;
+}
+
+int
+is_hit (unsigned char const *hits, int file_number)
+{
+ return is_hit_1 (&hits, tree8_levels, file_number);
+}
+
+int
+is_hit_1 (unsigned char const **hits, int level, int file_number)
+{
+ int file_hit = 1 << ((file_number >> (3 * --level)) & 7);
+ int hit = *(*hits)++;
+ int bit;
+
+ if (!(file_hit & hit))
+ return 0;
+ if (level == 0)
+ return 1;
+
+ for (bit = 1; (bit < file_hit) && (bit & 0xff); bit <<= 1)
+ {
+ if (hit & bit)
+ skip_hits (hits, level);
+ }
+ return is_hit_1 (hits, level, file_number);
+}
+
+void
+skip_hits (unsigned char const **hits, int level)
+{
+ int hit = *(*hits)++;
+ int bit;
+
+ if (--level == 0)
+ return;
+ for (bit = 1; bit & 0xff; bit <<= 1)
+ {
+ if (hit & bit)
+ skip_hits (hits, level);
+ }
+}
diff --git a/filenames.c b/filenames.c
new file mode 100644
index 0000000..9ccd4ad
--- /dev/null
+++ b/filenames.c
@@ -0,0 +1,603 @@
+/* filenames.c -- file & directory name manipulations
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "strxtra.h"
+#include "filenames.h"
+#include "misc.h"
+
+/* relative_file_name takes two arguments:
+ * 1) an absolute path name for a directory.
+ * (This name MUST have a trailing /).
+ * 2) an absolute path name for a file.
+ *
+ * It looks for common components at the front of the file and
+ * directory names and generates a relative path name for the file
+ * (relative to the specified directory).
+ *
+ * This may result in a huge number of ../s if the names
+ * have no components in common.
+ *
+ * The output from this concatenated with the input directory name
+ * and run through span_file_name should result in the original input
+ * absolute path name of the file.
+ *
+ * Examples:
+ * dir arg return value
+ * /x/y/z/ /x/y/q/file -> ../q/file
+ * /x/y/z/ /q/t/p/file -> ../../../q/t/p/file
+ * /x/y/z/ /x/y/z/file -> file
+ */
+char const *
+relative_file_name (char const *dir, char const *arg)
+{
+ char const *a;
+ char const *d;
+ char const *lasta;
+ char const *lastd;
+ static char file_name_buffer[BUFSIZ];
+ char *buf = file_name_buffer;
+
+ lasta = a = arg;
+ lastd = d = dir;
+ while (*a == *d)
+ {
+ if (*a == '/')
+ {
+ lasta = a;
+ lastd = d;
+ }
+ ++a;
+ ++d;
+ }
+ /* lasta and lastd now point to the last / in each
+ * file name where the leading file components were
+ * identical.
+ */
+ ++lasta;
+ ++lastd;
+ /* copy a ../ into the buffer for each component of
+ * the directory that remains.
+ */
+
+ while (*lastd != '\0')
+ {
+ if (*lastd == '/')
+ {
+ strcpy (buf, "../");
+ buf += 3;
+ }
+ ++lastd;
+ }
+ /* now tack on remainder of arg */
+ strcpy (buf, lasta);
+ return file_name_buffer;
+}
+
+/* span_file_name accepts a directory name and a file name and returns
+ a cannonical form of the full file name within that directory. It
+ gets rid of ./ and things like that. If the file is an absolute
+ name then the directory is ignored. */
+char const *
+span_file_name (char const *dir, char const *arg)
+{
+ char *argptr;
+ static char file_name_buffer[BUFSIZ];
+
+ /* reduce directory to cannonical form */
+ strcpy (file_name_buffer, dir);
+ cannoname (file_name_buffer);
+ /* tack the obilgatory / on the end */
+ strcat (file_name_buffer, "/");
+ /* stick file name in buffer after directory */
+ argptr = file_name_buffer + strlen (file_name_buffer);
+ strcpy (argptr, arg);
+ /* and reduce it to cannonical form also */
+ cannoname (argptr);
+ /* If it is an absolute name, just return it */
+ if (*argptr == '/')
+ return argptr;
+ /* otherwise, combine the names to cannonical form */
+ cannoname (file_name_buffer);
+ return file_name_buffer;
+}
+
+/* root_name returns the base name of the file with any leading
+ * directory information or trailing suffix stripped off. Examples:
+ *
+ * /usr/include/stdio.h -> stdio
+ * fred -> fred
+ * barney.c -> barney
+ * bill/bob -> bob
+ * / -> < null string >
+ */
+char const *
+root_name (char const *path)
+{
+ static char file_name_buffer[BUFSIZ];
+ char const *root;
+ char const *dot;
+
+ root = strrchr (path, '/');
+ if (root == NULL)
+ root = path;
+ else
+ root++;
+
+ dot = strrchr (root, '.');
+ if (dot == NULL)
+ strcpy (file_name_buffer, root);
+ else
+ {
+ strncpy (file_name_buffer, root, dot - root);
+ file_name_buffer[dot - root] = '\0';
+ }
+ return file_name_buffer;
+}
+
+/* suff_name returns the suffix (including the dot) or a null string
+ * if there is no suffix. Examples:
+ *
+ * /usr/include/stdio.h -> .h
+ * fred -> < null string >
+ * barney.c -> .c
+ * bill/bob -> < null string >
+ * / -> < null string >
+ */
+char const *
+suff_name (char const *path)
+{
+ char const *dot;
+
+ dot = strrchr (path, '.');
+ if (dot == NULL)
+ return "";
+ return dot;
+}
+
+int
+can_crunch (char const *path1, char const *path2)
+{
+ char const *slash1;
+ char const *slash2;
+
+ slash1 = strrchr (path1, '/');
+ slash2 = strrchr (path2, '/');
+
+ if (slash1 == NULL && slash2 == NULL)
+ return strequ (suff_name (path1), suff_name (path2));
+ if ((slash1 - path1) != (slash2 - path2))
+ return 0;
+ if (!strnequ (path1, path2, slash1 - path1))
+ return 0;
+ return strequ (suff_name (slash1), suff_name (slash2));
+}
+
+/* look_up adds ../s to the beginning of a file name until it finds
+ * the one that really exists. Returns NULL if it gets all the way
+ * to / and never finds it.
+ *
+ * If the file name starts with /, just return it as is.
+ *
+ * This routine is used to locate the ID database file.
+ */
+char const *
+look_up (char const *arg)
+{
+ static char file_name_buffer[BUFSIZ];
+ char *buf = file_name_buffer;
+ struct stat rootb;
+ struct stat statb;
+
+ /* if we got absolute name, just use it. */
+ if (arg[0] == '/')
+ return arg;
+ /* if the name we were give exists, don't bother searching */
+ if (stat (arg, &statb) == 0)
+ return arg;
+ /* search up the tree until we find a directory where this
+ * relative file name is visible.
+ * (or we run out of tree to search by hitting root).
+ */
+
+ if (stat ("/", &rootb) != 0)
+ return NULL;
+ do
+ {
+ strcpy (buf, "../");
+ buf += 3;
+ strcpy (buf, arg);
+ if (stat (file_name_buffer, &statb) == 0)
+ return file_name_buffer;
+ *buf = '\0';
+ if (stat (file_name_buffer, &statb) != 0)
+ return NULL;
+ }
+ while (!((statb.st_ino == rootb.st_ino) ||
+ (statb.st_dev == rootb.st_dev)));
+ return NULL;
+}
+
+/* define special name components */
+
+static char slash[] = "/";
+static char dot[] = ".";
+static char dotdot[] = "..";
+
+/* nextc points to the next character to look at in the string or is
+ * null if the end of string was reached.
+ *
+ * namep points to buffer that holds the components.
+ */
+static char const *nextc = NULL;
+static char *namep;
+
+/* lexname - Return next name component. Uses global variables initialized
+ * by cannoname to figure out what it is scanning.
+ */
+static char const *
+lexname (void)
+{
+ char c;
+ char const *d;
+
+ if (nextc)
+ {
+ c = *nextc++;
+ if (c == '\0')
+ {
+ nextc = NULL;
+ return NULL;
+ }
+ if (c == '/')
+ {
+ return &slash[0];
+ }
+ if (c == '.')
+ {
+ if ((*nextc == '/') || (*nextc == '\0'))
+ return &dot[0];
+ if (*nextc == '.' && (*(nextc + 1) == '/' || *(nextc + 1) == '\0'))
+ {
+ ++nextc;
+ return &dotdot[0];
+ }
+ }
+ d = namep;
+ *namep++ = c;
+ while ((c = *nextc) != '/')
+ {
+ *namep++ = c;
+ if (c == '\0')
+ {
+ nextc = NULL;
+ return d;
+ }
+ ++nextc;
+ }
+ *namep++ = '\0';
+ return d;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/* cannoname - Put a file name in cannonical form. Looks for all the
+ * whacky wonderful things a demented *ni* programmer might put
+ * in a file name and reduces the name to cannonical form.
+ */
+void
+cannoname (char *n)
+{
+ char const *components[1024];
+ char const **cap = &components[0];
+ char const **cad;
+ char const *cp;
+ char namebuf[2048];
+ char const *s;
+
+ /* initialize scanner */
+ nextc = n;
+ namep = &namebuf[0];
+
+ /* break the file name into individual components */
+ while ((cp = lexname ()))
+ {
+ *cap++ = cp;
+ }
+
+ /* If name is empty, leave it that way */
+ if (cap == &components[0])
+ return;
+
+ /* flag end of component list */
+ *cap = NULL;
+
+ /* remove all trailing slashes and dots */
+ while ((--cap != &components[0]) &&
+ ((*cap == &slash[0]) || (*cap == &dot[0])))
+ *cap = NULL;
+
+ /* squeeze out all . / component sequences */
+ cap = &components[0];
+ cad = cap;
+ while (*cap)
+ {
+ if ((*cap == &dot[0]) && (*(cap + 1) == &slash[0]))
+ {
+ cap += 2;
+ }
+ else
+ {
+ *cad++ = *cap++;
+ }
+ }
+ *cad++ = NULL;
+
+ /* find multiple // and use last slash as root, except on apollo which
+ * apparently actually uses // in real file names (don't ask me why).
+ */
+#ifndef apollo
+ s = NULL;
+ cap = &components[0];
+ cad = cap;
+ while (*cap)
+ {
+ if ((s == &slash[0]) && (*cap == &slash[0]))
+ {
+ cad = &components[0];
+ }
+ s = *cap++;
+ *cad++ = s;
+ }
+ *cad = NULL;
+#endif
+
+ /* if this is absolute name get rid of any /.. at beginning */
+ if ((components[0] == &slash[0]) && (components[1] == &dotdot[0]))
+ {
+ cap = &components[1];
+ cad = cap;
+ while (*cap == &dotdot[0])
+ {
+ ++cap;
+ if (*cap == NULL)
+ break;
+ if (*cap == &slash[0])
+ ++cap;
+ }
+ while (*cap)
+ *cad++ = *cap++;
+ *cad = NULL;
+ }
+
+ /* squeeze out any name/.. sequences (but leave leading ../..) */
+ cap = &components[0];
+ cad = cap;
+ while (*cap)
+ {
+ if ((*cap == &dotdot[0]) &&
+ ((cad - 2) >= &components[0]) &&
+ ((*(cad - 2)) != &dotdot[0]))
+ {
+ cad -= 2;
+ ++cap;
+ if (*cap)
+ ++cap;
+ }
+ else
+ {
+ *cad++ = *cap++;
+ }
+ }
+ /* squeezing out a trailing /.. can leave unsightly trailing /s */
+ if ((cad >= &components[2]) && ((*(cad - 1)) == &slash[0]))
+ --cad;
+ *cad = NULL;
+ /* if it was just name/.. it now becomes . */
+ if (components[0] == NULL)
+ {
+ components[0] = &dot[0];
+ components[1] = NULL;
+ }
+
+ /* re-assemble components */
+ cap = &components[0];
+ while ((s = *cap++))
+ {
+ while (*s)
+ *n++ = *s++;
+ }
+ *n++ = '\0';
+}
+
+/* kshgetwd is a routine that acts just like getwd, but is optimized
+ * for ksh users, taking advantage of the fact that ksh maintains
+ * an environment variable named PWD holding path name of the
+ * current working directory.
+ *
+ * The primary motivation for this is not really that it is algorithmically
+ * simpler, but that it is much less likely to bother NFS if we can just
+ * guess the name of the current working directory using the hint that
+ * ksh maintains. Anything that avoids NFS gettar failed messages is
+ * worth doing.
+ */
+char const *
+kshgetwd (char *pathname)
+{
+ struct stat kshstat, dotstat;
+ char kshname[MAXPATHLEN];
+ char const *kshp;
+
+ kshp = getenv ("PWD");
+ if (kshp)
+ {
+ /* OK, there was a PWD environment variable */
+ strcpy (kshname, kshp);
+ if (unsymlink (kshname)
+ /* And we could resolve the symbolic links through it */
+ && kshname[0] == '/'
+ /* And the name we have is an absolute path name */
+ && stat (kshname, &kshstat) == 0
+ /* And we can stat the name */
+ && stat (".", &dotstat) == 0
+ /* And we can stat "." */
+ && (kshstat.st_dev == dotstat.st_dev)
+ && (kshstat.st_ino == dotstat.st_ino))
+ /* By golly, that name is the same file as "." ! */
+ return strcpy (pathname, kshname);
+ }
+ /* Oh well, something did not work out right, do it the hard way */
+ return getwd (pathname);
+}
+
+/* unsymlink is a routine that resolves all symbolic links in
+ * a file name, transforming a name to the "actual" file name
+ * instead of the name in terms of symbolic links.
+ *
+ * If it can resolve all links and discover an actual file
+ * it returns a pointer to its argument string and transforms
+ * the argument in place to the actual name.
+ *
+ * If no such actual file exists, or for some reason the links
+ * cannot be resolved, it returns a NULL pointer and leaves the
+ * name alone.
+ */
+char const *
+unsymlink (char *n)
+{
+ char newname[MAXPATHLEN];
+ char partname[MAXPATHLEN];
+ char linkname[MAXPATHLEN];
+ char const *s;
+ char *d;
+ char *lastcomp;
+ int linksize;
+ struct stat statb;
+
+ /* Just stat the file to automagically do all the symbolic
+ * link verification checks and make sure we have access to
+ * directories, etc.
+ */
+ if (stat (n, &statb) != 0)
+ return NULL;
+ strcpy (newname, n);
+ /* Now loop, lstating each component to see if it is a symbolic
+ * link. For symbolic link components, use readlink() to get
+ * the real name, put the read link name in place of the
+ * last component, and start again.
+ */
+ cannoname (newname);
+ s = &newname[0];
+ d = &partname[0];
+ if (*s == '/')
+ *d++ = *s++;
+ lastcomp = d;
+ for (;;)
+ {
+ if ((*s == '/') || (*s == '\0'))
+ {
+ /* we have a complete component name in partname, check it out */
+ *d = '\0';
+ if (lstat (partname, &statb) != 0)
+ return NULL;
+ if ((statb.st_mode & S_IFMT) == S_IFLNK)
+ {
+ /* This much of name is a symbolic link, do a readlink
+ * and tack the bits and pieces together
+ */
+ linksize = readlink (partname, linkname, MAXPATHLEN);
+ if (linksize < 0)
+ return NULL;
+ linkname[linksize] = '\0';
+ strcpy (lastcomp, linkname);
+ lastcomp += linksize;
+ strcpy (lastcomp, s);
+ strcpy (newname, partname);
+ cannoname (newname);
+ s = &newname[0];
+ d = &partname[0];
+ if (*s == '/')
+ {
+ *d++ = *s++;
+ }
+ lastcomp = d;
+ }
+ else
+ {
+ /* Not a symlink, just keep scanning to next component */
+ if (*s == '\0')
+ break;
+ *d++ = *s++;
+ lastcomp = d;
+ }
+ }
+ else
+ {
+ *d++ = *s++;
+ }
+ }
+ strcpy (n, newname);
+ return n;
+}
+
+long input_chars = 0;
+
+FILE *
+open_source_FILE (char *file_name, char const *filter)
+{
+ struct stat stat_buf;
+ FILE *source_FILE;
+
+ if (stat (file_name, &stat_buf) < 0)
+ {
+ filerr ("open", file_name);
+ return NULL;
+ }
+ input_chars += stat_buf.st_size;
+
+ if (filter)
+ {
+ char command[1024];
+ sprintf (command, filter, file_name);
+ source_FILE = popen (command, "r");
+ }
+ else
+ source_FILE = fopen (file_name, "r");
+ if (source_FILE == NULL)
+ filerr ("open", file_name);
+ return source_FILE;
+}
+
+void
+close_source_FILE (FILE *fp, char const *filter)
+{
+ if (filter)
+ pclose (fp);
+ else
+ fclose (fp);
+}
diff --git a/filenames.h b/filenames.h
new file mode 100644
index 0000000..d2b7a39
--- /dev/null
+++ b/filenames.h
@@ -0,0 +1,38 @@
+/* filenames.h -- defs for interface to filenames.c
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _filenames_h_
+#define _filenames_h_
+
+char const *relative_file_name (char const *dir, char const *arg);
+char const *span_file_name (char const *dir, char const *arg);
+char const *root_name (char const *path);
+char const *suff_name (char const *path);
+int can_crunch (char const *path1, char const *path2);
+char const *look_up (char const *arg);
+void cannoname (char *n);
+char const *kshgetwd (char *pathname);
+char const *unsymlink (char *n);
+FILE *open_source_FILE (char *file_name, char const *filter);
+void close_source_FILE (FILE *fp, char const *filter);
+char const *get_sccs (char const *dir, char const *base, char const *sccs_dir);
+char const *co_rcs (char const *dir, char const *base, char const *rcs_dir);
+
+extern long input_chars;
+
+#endif /* not _filenames_h_ */
diff --git a/getopt.c b/getopt.c
new file mode 100644
index 0000000..43c0a6a
--- /dev/null
+++ b/getopt.c
@@ -0,0 +1,748 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+static const char *
+_getopt_initialize (optstring)
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (optind == 0)
+ optstring = _getopt_initialize (optstring);
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0'))
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if (nameend - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+ else
+ fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/getopt1.c b/getopt1.c
new file mode 100644
index 0000000..4580211
--- /dev/null
+++ b/getopt1.c
@@ -0,0 +1,180 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993, 1994
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gid.el b/gid.el
new file mode 100644
index 0000000..c5e0e2f
--- /dev/null
+++ b/gid.el
@@ -0,0 +1,22 @@
+;;; put this in your GnuEmacs startup file '~/.emacs' .
+;;; or autoload it from some other file. -wsr
+
+(require 'symfunc)
+
+(defun gid (command)
+ "Run gid, with user-specified args, and collect output in a buffer.
+While gid runs asynchronously, you can use the \\[next-error] command
+to find the text that gid hits refer to."
+ (interactive (list (read-input "Run gid (with args): "
+ (symbol-around-point))))
+ (require 'compile)
+ (setq command (concat "gid " command))
+ (compile1 command "No more gid hits" command))
+
+(defun aid (command)
+ "Run aid, with user-specified args, and collect output in a buffer."
+ (interactive (list (read-input "Run aid (with args): "
+ (symbol-around-point))))
+ (require 'compile)
+ (setq command (concat "aid " command))
+ (compile1 command "No aid hits" command))
diff --git a/idarg.h b/idarg.h
new file mode 100644
index 0000000..4bcef8a
--- /dev/null
+++ b/idarg.h
@@ -0,0 +1,33 @@
+/* idarg.h -- defs for internal form of command-line arguments
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _idarg_h_
+#define _idarg_h_
+
+struct idarg
+{
+ struct idarg *ida_next;
+ char *ida_arg;
+ int ida_index;
+ char ida_flags;
+#define IDA_RELATIVE 0x01 /* file name is now relative (lid) */
+#define IDA_SCAN_ME 0x01 /* file should be scanned (mkid) */
+#define IDA_PREFIX_US 0x02 /* file has names with prefixed underscores */
+};
+
+#endif /* _idarg_h_ */
diff --git a/idfile.c b/idfile.c
new file mode 100644
index 0000000..00ad740
--- /dev/null
+++ b/idfile.c
@@ -0,0 +1,184 @@
+/* idfile.c -- read & write mkid database file header
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include "alloc.h"
+#include "idfile.h"
+#include "idarg.h"
+#include "strxtra.h"
+
+static int io_idhead (FILE *fp, int (*io) (FILE *, void *, int, int), struct idhead *idh);
+static int io_write (FILE *output_FILE, void *addr, int size, int is_int);
+static int io_read (FILE *input_FILE, void *addr, int size, int is_int);
+static int io_size (FILE *, void *, int size, int);
+
+extern char *program_name;
+
+/* init_id opens id_file, reads the header into idhp (and verifies the magic
+ * number), then builds the id_args list holding the names of all the
+ * files recorded in the database.
+ */
+FILE *
+init_idfile (char const *id_file, struct idhead *idh, struct idarg **id_args)
+{
+ FILE *id_FILE;
+ int i;
+ char *strings;
+ struct idarg *ida;
+
+ id_FILE = fopen (id_file, "r");
+ if (id_FILE == NULL)
+ return NULL;
+
+ read_idhead (id_FILE, idh);
+ if (!strnequ (idh->idh_magic, IDH_MAGIC, sizeof (idh->idh_magic)))
+ {
+ fprintf (stderr, "%s: Not an id file: `%s'\n", program_name, id_file);
+ exit (1);
+ }
+ if (idh->idh_version != IDH_VERSION)
+ {
+ fprintf (stderr, "%s: ID version mismatch (want: %d, got: %d)\n", program_name, IDH_VERSION, idh->idh_version);
+ exit (1);
+ }
+
+ fseek (id_FILE, idh->idh_args_offset, 0);
+ strings = malloc (i = idh->idh_tokens_offset - idh->idh_args_offset);
+ fread (strings, i, 1, id_FILE);
+ ida = *id_args = CALLOC (struct idarg, idh->idh_paths);
+ for (i = 0; i < idh->idh_args; i++)
+ {
+ if (*strings != '+' && *strings != '-')
+ {
+ ida->ida_flags = 0;
+ ida->ida_arg = strings;
+ ida->ida_next = ida + 1;
+ ida->ida_index = i;
+ ida++;
+ }
+ while (*strings++)
+ ;
+ }
+ (--ida)->ida_next = NULL;
+ return id_FILE;
+}
+
+int
+read_idhead (FILE *input_FILE, struct idhead *idh)
+{
+ return io_idhead (input_FILE, io_read, idh);
+}
+
+int
+write_idhead (FILE *input_FILE, struct idhead *idh)
+{
+ return io_idhead (input_FILE, io_write, idh);
+}
+
+int
+sizeof_idhead ()
+{
+ return io_idhead (0, io_size, 0);
+}
+
+static int
+io_size (FILE *ignore_FILE, void *ignore_addr, int size, int ignore_int)
+{
+ return size;
+}
+
+static int
+io_read (FILE *input_FILE, void *addr, int size, int is_int)
+{
+ if (is_int)
+ {
+ switch (size)
+ {
+ case sizeof (long):
+ *(long *)addr = getc (input_FILE);
+ *(long *)addr += getc (input_FILE) << 010;
+ *(long *)addr += getc (input_FILE) << 020;
+ *(long *)addr += getc (input_FILE) << 030;
+ break;
+ case sizeof (short):
+ *(short *)addr = getc (input_FILE);
+ *(short *)addr += getc (input_FILE) << 010;
+ break;
+ default:
+ fprintf (stderr, "Unsupported size in io_write (): %d\n", size);
+ abort ();
+ }
+ }
+ else if (size > 1)
+ fread (addr, size, 1, input_FILE);
+ else
+ *(char *)addr = getc (input_FILE);
+ return size;
+}
+
+static int
+io_write (FILE *output_FILE, void *addr, int size, int is_int)
+{
+ if (is_int)
+ {
+ switch (size)
+ {
+ case sizeof (long):
+ putc (*(long *)addr, output_FILE);
+ putc (*(long *)addr >> 010, output_FILE);
+ putc (*(long *)addr >> 020, output_FILE);
+ putc (*(long *)addr >> 030, output_FILE);
+ break;
+ case sizeof (short):
+ putc (*(short *)addr, output_FILE);
+ putc (*(short *)addr >> 010, output_FILE);
+ break;
+ default:
+ fprintf (stderr, "Unsupported size in io_write (): %d\n", size);
+ abort ();
+ }
+ }
+ else if (size > 1)
+ fwrite (addr, size, 1, output_FILE);
+ else
+ putc (*(char *)addr, output_FILE);
+ return size;
+}
+
+static int
+io_idhead (FILE *fp, int (*io) (FILE *, void *, int, int), struct idhead *idh)
+{
+ int size = 0;
+ fseek (fp, 0L, 0);
+ size += io (fp, idh->idh_magic, sizeof (idh->idh_magic), 0);
+ size += io (fp, &idh->idh_pad_1, sizeof (idh->idh_pad_1), 0);
+ size += io (fp, &idh->idh_version, sizeof (idh->idh_version), 0);
+ size += io (fp, &idh->idh_flags, sizeof (idh->idh_flags), 1);
+ size += io (fp, &idh->idh_args, sizeof (idh->idh_args), 1);
+ size += io (fp, &idh->idh_paths, sizeof (idh->idh_paths), 1);
+ size += io (fp, &idh->idh_tokens, sizeof (idh->idh_tokens), 1);
+ size += io (fp, &idh->idh_buf_size, sizeof (idh->idh_buf_size), 1);
+ size += io (fp, &idh->idh_vec_size, sizeof (idh->idh_vec_size), 1);
+ size += io (fp, &idh->idh_args_offset, sizeof (idh->idh_args_offset), 1);
+ size += io (fp, &idh->idh_tokens_offset, sizeof (idh->idh_tokens_offset), 1);
+ size += io (fp, &idh->idh_end_offset, sizeof (idh->idh_end_offset), 1);
+ return size;
+}
+
diff --git a/idfile.h b/idfile.h
new file mode 100644
index 0000000..a6637c3
--- /dev/null
+++ b/idfile.h
@@ -0,0 +1,55 @@
+/* idfile.h -- defs for mkid database file header & interface to idfile.c
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _idfile_h_
+#define _idfile_h_ 1
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#define IDFILE "ID"
+
+struct idhead
+{
+ char idh_magic[2];
+#define IDH_MAGIC "\311\304" /* ("ID" with hi bits set) */
+ char idh_pad_1;
+ char idh_version;
+#define IDH_VERSION 3
+ unsigned short idh_flags;
+#define IDH_COUNTS 0x0001 /* occurrence counts are included with each token */
+ long idh_args; /* total # of args for mkid update */
+ long idh_paths; /* total # of file names for mkid update */
+ long idh_tokens; /* total # of tokens */
+ long idh_buf_size; /* # of bytes in longest entry (bufsiz for lid) */
+ long idh_vec_size; /* # of hits in longest entry (max vector size for lid) */
+ off_t idh_args_offset; /* file offset of args */
+ off_t idh_tokens_offset; /* file offset of tokens section */
+ off_t idh_end_offset; /* file offset beyond tokens section */
+};
+
+#define GETARG(argc, argv) ((argc)--, *(argv)++)
+#define UNGETARG(argc, argv) ((argc)++, --(argv))
+
+struct idarg;
+FILE *init_idfile (char const *id_file, struct idhead *idhp, struct idarg **id_args);
+int read_idhead (FILE *input_FILE, struct idhead *idh);
+int write_idhead (FILE *input_FILE, struct idhead *idh);
+int sizeof_idhead (void);
+
+#endif /* _idfile_h_ */
diff --git a/idtest b/idtest
new file mode 100755
index 0000000..351bd52
--- /dev/null
+++ b/idtest
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -x
+
+case $# in
+0) 1>&2 echo Usage: $(basename $0) files...; exit 1;;
+esac
+case $1 in
+-f*) _fID=$1; shift;;
+esac
+
+idx "$@" |sort -u >|ids.idx
+lid $_fID |sed -e 's/[ ].*//' |sort -u >|ids.lid
+for file
+do
+ case x$file in
+ x-*) continue;;
+ esac
+ fid $_fID $file
+done |sort -u >|ids.fid
+
+if cmp ids.idx ids.lid
+then
+ if cmp ids.idx ids.fid
+ then
+ echo OK
+ rm -f ids.idx ids.fid ids.lid
+ exit 0
+ fi
+fi
+echo Oops!
+exit 1
diff --git a/idx.c b/idx.c
new file mode 100644
index 0000000..e62e2d9
--- /dev/null
+++ b/idx.c
@@ -0,0 +1,88 @@
+/* static char copyright[] = "@(#)Copyright (c) 1986, Greg McGary";
+ static char sccsid[] = "@(#)idx.c 1.2 86/10/17"; */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include "misc.h"
+#include "filenames.h"
+#include "scanners.h"
+
+void idxtract (char *path);
+
+char const *program_name;
+
+static void
+usage (void)
+{
+ fprintf (stderr, "Usage: %s [-u] [+/-a<ccc>] [-c<ccc>] files\n", program_name);
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ char *arg;
+ int op;
+
+ program_name = basename ((argc--, *argv++));
+
+ init_scanners ();
+
+ while (argc)
+ {
+ arg = (argc--, *argv++);
+ switch (op = *arg++)
+ {
+ case '-':
+ case '+':
+ break;
+ default:
+ (argc++, --argv);
+ goto argsdone;
+ }
+ switch (*arg++)
+ {
+ case 'S':
+ set_scan_args (op, arg);
+ break;
+ default:
+ usage ();
+ }
+ }
+argsdone:
+
+ if (argc == 0)
+ usage ();
+
+ while (argc)
+ idxtract ((argc--, *argv++));
+
+ return 0;
+}
+
+void
+idxtract (char *file_name)
+{
+ char const *key;
+ FILE *source_FILE;
+ int flags;
+ char const *suffix;
+ char const *filter;
+ char const *lang_name;
+ char const *(*scanner) (FILE*, int*);
+
+ suffix = strrchr (file_name, '.');
+ lang_name = get_lang_name (suffix);
+ scanner = get_scanner (lang_name);
+ if (scanner == NULL)
+ return;
+ source_FILE = open_source_FILE (file_name, filter = get_filter (suffix));
+ if (source_FILE == NULL)
+ return;
+
+ while ((key = (*scanner) (source_FILE, &flags)) != NULL)
+ puts (key);
+
+ close_source_FILE (source_FILE, filter);
+}
diff --git a/iid.1 b/iid.1
new file mode 100644
index 0000000..0cc256f
--- /dev/null
+++ b/iid.1
@@ -0,0 +1,235 @@
+.TH IID 1
+.SH NAME
+iid \- interactive query for ID database
+.SH SYNOPSIS
+.PP
+.B iid
+.RB [ \-a]
+.RB [ \-c \^command]
+.RB [ \-H]
+.SH DESCRIPTION
+This command provides an interactive query interface to the
+.I ID
+database.
+.I Iid\^
+allows you to query an
+.I ID
+database in a fashion similar to using \fIDIALOG\fP. Any individual
+query command results in a list of files that satisfy that query,
+each set of files is retained by
+.I iid
+and assigned a set number. The sets may be combined with
+.IR AND ,
+.I OR
+and
+.I NOT
+operators to produce additional sets. The primitive operators that
+produce sets are invocations of the
+.I lid
+or
+.I aid
+programs.
+.SH OPTIONS
+Normally
+.I iid
+runs interactively. Options may be used to run it in batch mode.
+.TP 8
+.B \-a
+Use the
+.I aid
+program as the default query program, normally
+.I lid
+is used.
+.TP 8
+.B \-c
+Accept a single command as an argument, run that command, and exit
+.IR Iid .
+.TP
+.B \-H
+Print a brief help message and exit.
+.SH SUBCOMMANDS
+The subcommands are used to carry on a dialog with
+.I iid
+after invoking the program.
+.PP
+Two basic query commands are available:
+.B SS
+and
+.BR FILES .
+The
+.B SS
+command shows the sets generated by a query, but does not display
+the actual file names that satisfy the query.
+The
+.B FILES
+command only displays the list of files, it does not show any
+of the sets created during the query.
+.PP
+Queries consist of keywords and identifier strings. The keywords are:
+.B and or not lid aid match
+and
+.B s<number>
+where
+.B s<number>
+is a set number consisting of the letter
+.B s
+followed (with no space) by a decimal set number.
+A clause of the form
+.B lid <identifier list>
+invokes
+.I lid
+with the
+.B <identifier list>
+as arguments and produces a set of files as a result.
+Substituting
+.B aid
+for
+.B lid
+runs the
+.I aid
+program to generate the list of files.
+As a shorthand notation for
+.B lid <identifier>
+you may simply use
+.B <identifier>.
+The
+.B match
+operator runs the standard system
+.I ls
+utility to produce a set of files. This allows sets to be
+constructed based on the names of files (using wild cards)
+rather than contents.
+The
+.B and or
+and
+.B not
+operators can be used to combine sets in the obvious fashion.
+If you need to pass any of the keywords as actual arguments to
+programs, or if the search strings contain any shell escape
+characters place the argument in quotes.
+.PP
+The
+.B NOT
+operator has highest precedence, followed by
+.B AND
+and
+.B OR
+in that order. Parenthesis may be used for grouping.
+.PP
+The remaining commands are:
+.PP
+.B BEGIN <directory>
+accepts a directory name and switches to that directory. By changing
+directories you control which
+.I ID
+database is searched. Changing directories automatically deletes
+all the sets constructed so far. The
+.B BEGIN
+command may be abbreviated as
+.BR B .
+.PP
+.B SETS
+shows the description of all the sets created so far. Each set
+description has the set number, the number of files in the set,
+and a symbolic description of the query that created the set.
+.PP
+.B SHOW <set number>
+runs a pager program, passing as arguments all the files in
+the specified set. The pager program comes from the
+.B $PAGER
+environment variable. This command may be abbreviated
+.BR P .
+.PP
+.B HELP
+runs the pager on the help file. The commands
+.B H
+and
+.B ?
+also act as help commands.
+.PP
+.B OFF
+exits the program.
+.B Q
+is short for
+.BR OFF .
+.PP
+All commands and keywords are case insensitive, so that
+.B SHOW ShOW
+and
+.B show
+all work equally well.
+.SH INTERFACE
+Two forms of commands are provided for interface with arbitrary
+programs. Any command that is not recognized as one
+of the above built in
+.I iid
+commands, is assumed to be a program which, when run, will print
+a list of file names.
+.I Iid
+runs the command as typed, and records the output as a new set
+which may be combined with other sets in subsequent queries.
+.PP
+If the command starts with a
+.BR !,
+.I iid
+strips off the leading
+.B !
+and simply runs the command. Any output goes to stdout and
+is not recorded as a set.
+.PP
+In both types of shell commands, any set numbers specified as
+arguments are expanded into a list of file names before running
+the command.
+.SH EXAMPLE
+.nf
+.ft L
+===> iid
+iid> ss lid "^get" or lid "Arg$"
+ S0 14 lid -kmn "^get"
+ S1 3 lid -kmn "Arg$"
+ S2 15 (lid -kmn "^get") OR (lid -kmn "Arg$")
+iid> f s1
+lid.c
+paths.c
+init.c
+iid> off
+.FT P
+.fi
+.EX off
+.PP
+In this example the
+.B ss
+command displays the sets it creates as it
+does the parts of the query. In this case 3 sets are created, set S0
+has 14 files in it, set S1 has 3 files and the union of the two sets,
+S2, has 15 files. A description of the query that created any given
+set is kept along with the set and displayed when sets are printed.
+.PP
+The
+.B f s1
+command lists the three files in set S1.
+.PP
+The
+.B off
+command terminates the example session.
+.SH HINTS
+The shell interface commands can be used to generate file sets by
+running the
+.I find
+or
+.I ls
+utilities, or compiles of a selected group of files can be done
+using the
+.BR ! cc
+command with a set number as the argument.
+.BR ! lp
+can be used to print a selected group of files.
+.PP
+This program interfaces nicely with
+.I emacs
+if you run the server program and specify the client program
+as your $PAGER.
+.SH SEE ALSO
+mkid(1),
+lid(1),
+aid(1).
diff --git a/iid.c b/iid.c
new file mode 100644
index 0000000..cb200c9
--- /dev/null
+++ b/iid.c
@@ -0,0 +1,2301 @@
+
+/* A Bison parser, made from iid.y with Bison version GNU Bison version 1.22
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define SET 258
+#define ID 259
+#define SHELL_QUERY 260
+#define SHELL_COMMAND 261
+#define LID 262
+#define AID 263
+#define BEGIN 264
+#define SETS 265
+#define SS 266
+#define FILES 267
+#define SHOW 268
+#define HELP 269
+#define OFF 270
+#define MATCH 271
+#define OR 272
+#define AND 273
+#define NOT 274
+
+#line 1 "iid.y"
+
+/* iid.y -- interactive mkid query language
+ Copyright (C) 1991 Tom Horsley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#ifdef HAVE_ALLOCA
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#define TEMP_ALLOC(s) alloca(s)
+#define TEMP_FREE(s)
+
+#else /* not HAVE_ALLOCA
+
+#define TEMP_ALLOC(s) malloc(s)
+#define TEMP_FREE(s) free(s)
+
+#endif /* not HAVE_ALLOCA */
+
+#define HASH_SIZE 947 /* size of hash table for file names */
+#define INIT_FILES 8000 /* start with bits for this many */
+#define INIT_SETSPACE 500 /* start with room for this many */
+#define MAXCMD 1024 /* input command buffer size */
+
+#define MAX(a,b) (((a)<(b))?(b):(a))
+#define MIN(a,b) (((a)>(b))?(b):(a))
+
+#ifndef PAGER
+#define PAGER "pg"
+#endif
+
+#define PROMPT "iid> "
+
+/* set_type is the struct defining a set of file names
+ * The file names are stored in a symbol table and assigned
+ * unique numbers. The set is a bit set of file numbers.
+ * One of these set structs is calloced for each new set
+ * constructed, the size allocated depends on the max file
+ * bit number. An array of pointers to sets are kept to
+ * represent the complete set of sets.
+ */
+
+struct set_struct {
+ char * set_desc ; /* string describing the set */
+ int set_num ; /* the set number */
+ int set_size ; /* number of long words in set */
+ unsigned long int set_tail ; /* set extended with these bits */
+ unsigned long int set_data[1] ;/* the actual set data (calloced) */
+} ;
+typedef struct set_struct set_type ;
+
+/* id_type is one element of an id_list
+ */
+
+struct id_struct {
+ struct id_struct * next_id ; /* Linked list of IDs */
+ char id [ 1 ] ; /* calloced data holding id string */
+} ;
+typedef struct id_struct id_type ;
+
+/* id_list_type is used during parsing to build lists of
+ * identifiers that will eventually represent arguments
+ * to be passed to the database query programs.
+ */
+
+struct id_list_struct {
+ int id_count ; /* count of IDs in the list */
+ id_type * * end_ptr_ptr ;/* pointer to link word at end of list */
+ id_type * id_list ; /* pointer to list of IDs */
+} ;
+typedef struct id_list_struct id_list_type ;
+
+/* symtab_type is used to record file names in the symbol table.
+ */
+struct symtab_struct {
+ struct symtab_struct * hash_link ; /* list of files with same hash code */
+ int mask_word ; /* word in bit vector */
+ unsigned long mask_bit ; /* bit in word */
+ char name [ 1 ] ; /* the file name */
+} ;
+typedef struct symtab_struct symtab_type ;
+
+/* LidCommand is the command to run for a Lid_group. It is set
+ * to "lid -kmn" if explicitly preceeded by "lid", otherwise
+ * it is the default command which is determined by an option.
+ */
+char const * LidCommand ;
+
+/* DefaultCommand is the default command for a Lid_group. If
+ * the -a option is given to iid, it is set to use 'aid'.
+ */
+char const * DefaultCommand = "lid -kmn" ;
+
+/* FileList is a lexically ordered list of file symbol table
+ * pointers. It is dynamically expanded when necessary.
+ */
+symtab_type * * FileList = NULL ;
+
+/* FileSpace is the number of long ints in TheFiles array.
+ */
+int FileSpace = 0 ;
+
+/* HashTable is the symbol table used to store file names. Each
+ * new name installed is assigned the next consecutive file number.
+ */
+symtab_type * HashTable [ HASH_SIZE ] ;
+
+/* HelpSet is a dummy set containing only one bit set which corresponds
+ * to the help file name. Simply a cheesy way to maximize sharing of
+ * the code that runs the pager.
+ */
+set_type * HelpSet ;
+
+/* high_bit is a unsigned long with the most significant bit set.
+ */
+unsigned long high_bit ;
+
+/* ListSpace is the amount of space avail in the FileList.
+ */
+int ListSpace = 0 ;
+
+/* MaxCurFile - max word that has any bit currently set in the
+ * TheFiles array.
+ */
+int MaxCurFile = 0 ;
+
+/* NextFileNum is the file number that will be assigned to the next
+ * new file name seen when it is installed in the symtab.
+ */
+int NextFileNum = 0 ;
+
+/* NextMaskBit is the bit within the next mask word that will
+ * correspond to the next file added to the symbol table.
+ */
+unsigned long NextMaskBit ;
+
+/* NextMaskWord is the next word number to be assigned to a file
+ * bit mask entry.
+ */
+int NextMaskWord = 0 ;
+
+/* NextSetNum is the number that will be assigned to the next set
+ * created. Starts at 0 because I am a C programmer.
+ */
+int NextSetNum = 0 ;
+
+/* The PAGER program to run on a SHOW command.
+ */
+char Pager[MAXCMD] ;
+
+/* Prompt - the string to use for a prompt.
+ */
+char Prompt[MAXCMD] ;
+
+/* SetSpace is the number of pointers available in TheSets. TheSets
+ * is realloced when we run out of space.
+ */
+int SetSpace = 0 ;
+
+/* TheFiles is a bit set used to construct the initial set of files
+ * generated while running one of the subprograms. It is copied to
+ * the alloced set once we know how many bits are set.
+ */
+unsigned long * TheFiles = NULL ;
+
+/* TheSets is a dynamically allocated array of pointers pointing
+ * the sets that have been allocated. It represents the set of
+ * sets.
+ */
+set_type * * TheSets = NULL ;
+
+/* VerboseQuery controls the actions of the semantic routines during
+ * the process of a query. If TRUE the sets are described as they
+ * are constructed.
+ */
+int VerboseQuery ;
+
+int yyerror( char const * s ) ;
+void ScanInit( char * line ) ;
+int yylex( void ) ;
+int ArgListSize( id_list_type * idlp ) ;
+int SetListSize( set_type * sp ) ;
+void FlushFiles( void ) ;
+void fatal( char const * s ) ;
+int CountBits( set_type * sp ) ;
+void OneDescription( set_type * sp ) ;
+void DescribeSets( void ) ;
+id_list_type * SetList( id_list_type * idlp , set_type * sp ) ;
+void PrintSet( set_type * sp ) ;
+void FlushSets( void ) ;
+id_list_type * InitList( void ) ;
+id_list_type * ExtendList( id_list_type * idlp , id_type * idp ) ;
+void InitIid( void ) ;
+symtab_type * InstallFile( char const * fp ) ;
+void RunPager( char * pp , set_type * sp ) ;
+void AddSet( set_type * sp ) ;
+set_type * RunProg( char const * pp , id_list_type * idlp ) ;
+void SetDirectory( id_type * dir ) ;
+set_type * SetIntersect( set_type * sp1 , set_type * sp2 ) ;
+set_type * SetUnion( set_type * sp1 , set_type * sp2 ) ;
+set_type * SetInverse( set_type * sp ) ;
+void RunShell( char * pp , id_list_type * idlp ) ;
+
+
+#line 229 "iid.y"
+typedef union {
+ set_type * setdef ;
+ id_type * strdef ;
+ id_list_type * listdef ;
+} YYSTYPE;
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 46
+#define YYFLAG -32768
+#define YYNTBASE 22
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 274 ? yytranslate[x] : 31)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 20,
+ 21, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 3, 6, 9, 12, 14, 16, 18, 21, 24,
+ 26, 28, 30, 34, 38, 41, 43, 45, 47, 50,
+ 54, 56, 59, 62, 64, 66, 69, 72, 74
+};
+
+static const short yyrhs[] = { 9,
+ 4, 0, 23, 25, 0, 24, 25, 0, 13, 3,
+ 0, 10, 0, 14, 0, 15, 0, 5, 29, 0,
+ 6, 29, 0, 11, 0, 12, 0, 26, 0, 25,
+ 18, 25, 0, 25, 17, 25, 0, 19, 25, 0,
+ 3, 0, 27, 0, 28, 0, 16, 30, 0, 20,
+ 25, 21, 0, 4, 0, 7, 30, 0, 8, 30,
+ 0, 4, 0, 3, 0, 29, 4, 0, 29, 3,
+ 0, 4, 0, 30, 4, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 255, 263, 264, 270, 276, 282, 288, 292, 299, 308,
+ 317, 326, 333, 342, 351, 362, 369, 378, 387, 395,
+ 403, 412, 421, 430, 438, 445, 451, 459, 467
+};
+
+static const char * const yytname[] = { "$","error","$illegal.","SET","ID",
+"SHELL_QUERY","SHELL_COMMAND","LID","AID","BEGIN","SETS","SS","FILES","SHOW",
+"HELP","OFF","MATCH","OR","AND","NOT","'('","')'","Command","Set_query","File_query",
+"Query","Primitive","Lid_group","Aid_group","Command_list","Id_list",""
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
+ 24, 25, 25, 25, 25, 26, 26, 26, 26, 26,
+ 27, 27, 28, 29, 29, 29, 29, 30, 30
+};
+
+static const short yyr2[] = { 0,
+ 2, 2, 2, 2, 1, 1, 1, 2, 2, 1,
+ 1, 1, 3, 3, 2, 1, 1, 1, 2, 3,
+ 1, 2, 2, 1, 1, 2, 2, 1, 2
+};
+
+static const short yydefact[] = { 0,
+ 0, 0, 0, 5, 10, 11, 0, 6, 7, 0,
+ 0, 25, 24, 8, 9, 1, 4, 16, 21, 0,
+ 0, 0, 0, 0, 2, 12, 17, 18, 3, 27,
+ 26, 28, 22, 23, 19, 15, 0, 0, 0, 29,
+ 20, 14, 13, 0, 0, 0
+};
+
+static const short yydefgoto[] = { 44,
+ 10, 11, 25, 26, 27, 28, 14, 33
+};
+
+static const short yypact[] = { 10,
+ 5, 5, 22,-32768,-32768,-32768, 28,-32768,-32768, -2,
+ -2,-32768,-32768, 7, 7,-32768,-32768,-32768,-32768, 30,
+ 30, 30, -2, -2, 12,-32768,-32768,-32768, 12,-32768,
+-32768,-32768, 31, 31, 31,-32768, -14, -2, -2,-32768,
+-32768, 18,-32768, 37, 38,-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,-32768, -11,-32768,-32768,-32768, 39, 11
+};
+
+
+#define YYLAST 41
+
+
+static const short yytable[] = { 29,
+ 18, 19, 38, 39, 20, 21, 41, 12, 13, 30,
+ 31, 36, 37, 22, 1, 2, 23, 24, 3, 4,
+ 5, 6, 7, 8, 9, 16, 42, 43, 38, 39,
+ 17, 34, 35, 32, 40, 39, 45, 46, 0, 0,
+ 15
+};
+
+static const short yycheck[] = { 11,
+ 3, 4, 17, 18, 7, 8, 21, 3, 4, 3,
+ 4, 23, 24, 16, 5, 6, 19, 20, 9, 10,
+ 11, 12, 13, 14, 15, 4, 38, 39, 17, 18,
+ 3, 21, 22, 4, 4, 18, 0, 0, -1, -1,
+ 2
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/lib/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include <malloc.h>
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include <malloc.h>
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYLEX
+#ifndef YYPURE
+#define YYLEX yylex()
+#else
+#ifdef YYLSP_NEEDED
+#define YYLEX yylex(&yylval, &yylloc)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+int yydebug_reducing = 0;
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 185 "/usr/lib/bison.simple"
+int
+yyparse()
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+#ifndef __cplusplus
+ fprintf(stderr, "Starting parse\n");
+#else /* __cplusplus */
+ clog << "Starting parse" << endl;
+#endif /* __cplusplus */
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug >= 3)
+#ifndef __cplusplus
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#else /* __cplusplus */
+ clog << "Stack size increased to " << yystacksize << endl;
+#endif /* __cplusplus */
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug >= 3)
+#ifndef __cplusplus
+ fprintf(stderr, "Entering state %d\n", yystate);
+#else /* __cplusplus */
+ clog << "Entering state " << yystate << endl;
+#endif /* __cplusplus */
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug >= 3)
+#ifndef __cplusplus
+ fprintf(stderr, "Reading a token: ");
+#else /* __cplusplus */
+ clog << "Reading a token: ";
+#endif /* __cplusplus */
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+#ifndef __cplusplus
+ fprintf(stderr, "Now at end of input.\n");
+#else /* __cplusplus */
+ clog << "Now at end of input." << endl;
+#endif /* __cplusplus */
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug >= 3)
+ {
+#ifndef __cplusplus
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+#else /* __cplusplus */
+ clog << "Next token is " << yychar << " (" << yytname[yychar1];
+#endif /* __cplusplus */
+#ifdef YYPRINT
+#ifndef __cplusplus
+ YYPRINT (stderr, yychar, yylval);
+#else /* __cplusplus */
+ YYPRINT (yychar, yylval);
+#endif /* __cplusplus */
+#endif
+#ifndef __cplusplus
+ fprintf (stderr, ")\n");
+#else /* __cplusplus */
+ clog << ')' << endl;
+#endif /* __cplusplus */
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ if (yydebug_reducing)
+ {
+#ifndef __cplusplus
+ fprintf(stderr, "\nShift:");
+#else /* __cplusplus */
+ clog << endl << "Shift:";
+#endif /* __cplusplus */
+ yydebug_reducing = 0;
+ }
+ if (yydebug >= 2)
+#ifndef __cplusplus
+ fprintf (stderr, "Shifting token %d: %s", yychar, yytname[yychar1]);
+#else /* __cplusplus */
+ clog << "Shifting token " << yychar << ": " << yytname[yychar1];
+#endif /* __cplusplus */
+ else
+#ifndef __cplusplus
+ fprintf (stderr, " %s", yytname[yychar1]);
+#else /* __cplusplus */
+ clog << ' ' << yytname[yychar1];
+#endif /* __cplusplus */
+#ifdef YYPRINT
+#ifndef __cplusplus
+ YYPRINT (stderr, yychar, yylval);
+#else /* __cplusplus */
+ YYPRINT (yychar, yylval);
+#endif /* __cplusplus */
+#endif
+ if (yydebug >= 2)
+#ifndef __cplusplus
+ fputc ('\n', stderr);
+#else /* __cplusplus */
+ clog << endl;
+#endif /* __cplusplus */
+ }
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+ if (!yydebug_reducing)
+ {
+#ifndef __cplusplus
+ fputc('\n', stderr);
+#else /* __cplusplus */
+ clog << endl;
+#endif /* __cplusplus */
+ yydebug_reducing = 1;
+ }
+ if (yydebug >= 2)
+#ifndef __cplusplus
+ fprintf (stderr, "Reducing via rule %d (line %d): ", yyn, yyrline[yyn]);
+#else /* __cplusplus */
+ clog << "Reducing via rule " << yyn << " (line " << yyrline[yyn] << " ): ";
+#endif /* __cplusplus */
+ else
+#ifndef __cplusplus
+ fprintf (stderr, YYFILE ":%d: ", yyrline[yyn]);
+#else /* __cplusplus */
+ clog << YYFILE ":" << yyrline[yyn] << ": ";
+#endif /* __cplusplus */
+
+ /* Print the symbols being reduced, and their result. */
+#ifdef __cplusplus
+ clog << yytname[yyr1[yyn]] << " <-";
+#endif /* __cplusplus */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+#ifndef __cplusplus
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, "-> %s\n", yytname[yyr1[yyn]]);
+#else /* __cplusplus */
+ clog << ' ' << yytname[yyrhs[i]];
+ clog << endl;
+#endif /* __cplusplus */
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 1:
+#line 257 "iid.y"
+{
+ /* cd to the directory specified as argument, flush sets */
+
+ SetDirectory(yyvsp[0]. strdef ) ;
+ FlushSets() ;
+ ;
+ break;}
+case 3:
+#line 265 "iid.y"
+{
+ /* print the list of files resulting from Query */
+
+ PrintSet(yyvsp[0]. setdef ) ;
+ ;
+ break;}
+case 4:
+#line 271 "iid.y"
+{
+ /* run PAGER on the list of files in SET */
+
+ RunPager(Pager, yyvsp[0]. setdef ) ;
+ ;
+ break;}
+case 5:
+#line 277 "iid.y"
+{
+ /* describe sets created so far */
+
+ DescribeSets() ;
+ ;
+ break;}
+case 6:
+#line 283 "iid.y"
+{
+ /* run PAGER on the help file */
+
+ RunPager(Pager, HelpSet) ;
+ ;
+ break;}
+case 7:
+#line 289 "iid.y"
+{
+ exit(0) ;
+ ;
+ break;}
+case 8:
+#line 293 "iid.y"
+{
+ /* run the shell command and eat the results as a file set */
+
+ OneDescription(RunProg(yyvsp[-1]. strdef ->id, yyvsp[0]. listdef )) ;
+ free(yyvsp[-1]. strdef ) ;
+ ;
+ break;}
+case 9:
+#line 300 "iid.y"
+{
+ /* run the shell command */
+
+ RunShell(yyvsp[-1]. strdef ->id, yyvsp[0]. listdef ) ;
+ free(yyvsp[-1]. strdef ) ;
+ ;
+ break;}
+case 10:
+#line 310 "iid.y"
+{
+ /* Turn on verbose query flag */
+
+ VerboseQuery = 1 ;
+ ;
+ break;}
+case 11:
+#line 319 "iid.y"
+{
+ /* Turn off verbose query flag */
+
+ VerboseQuery = 0 ;
+ ;
+ break;}
+case 12:
+#line 328 "iid.y"
+{
+ /* value of query is set associated with primitive */
+
+ yyval. setdef = yyvsp[0]. setdef ;
+ ;
+ break;}
+case 13:
+#line 334 "iid.y"
+{
+ /* value of query is intersection of the two query sets */
+
+ yyval. setdef = SetIntersect(yyvsp[-2]. setdef , yyvsp[0]. setdef ) ;
+ if (VerboseQuery) {
+ OneDescription(yyval. setdef ) ;
+ }
+ ;
+ break;}
+case 14:
+#line 343 "iid.y"
+{
+ /* value of query is union of the two query sets */
+
+ yyval. setdef = SetUnion(yyvsp[-2]. setdef , yyvsp[0]. setdef ) ;
+ if (VerboseQuery) {
+ OneDescription(yyval. setdef ) ;
+ }
+ ;
+ break;}
+case 15:
+#line 352 "iid.y"
+{
+ /* value of query is inverse of other query */
+
+ yyval. setdef = SetInverse(yyvsp[0]. setdef ) ;
+ if (VerboseQuery) {
+ OneDescription(yyval. setdef ) ;
+ }
+ ;
+ break;}
+case 16:
+#line 364 "iid.y"
+{
+ /* Value of primitive is value of recorded set */
+
+ yyval. setdef = yyvsp[0]. setdef ;
+ ;
+ break;}
+case 17:
+#line 370 "iid.y"
+{
+ /* Value of primitive is obtained by running an lid query */
+
+ yyval. setdef = RunProg(LidCommand, yyvsp[0]. listdef ) ;
+ if (VerboseQuery) {
+ OneDescription(yyval. setdef ) ;
+ }
+ ;
+ break;}
+case 18:
+#line 379 "iid.y"
+{
+ /* Value of primitive is obtained by running an aid query */
+
+ yyval. setdef = RunProg("aid -kmn", yyvsp[0]. listdef ) ;
+ if (VerboseQuery) {
+ OneDescription(yyval. setdef ) ;
+ }
+ ;
+ break;}
+case 19:
+#line 388 "iid.y"
+{
+ /* Match names from database against pattern */
+ yyval. setdef = RunProg("pid -kmn", yyvsp[0]. listdef ) ;
+ if (VerboseQuery) {
+ OneDescription(yyval. setdef ) ;
+ }
+ ;
+ break;}
+case 20:
+#line 396 "iid.y"
+{
+ /* value of primitive is value of query */
+
+ yyval. setdef = yyvsp[-1]. setdef ;
+ ;
+ break;}
+case 21:
+#line 405 "iid.y"
+{
+ /* make arg list holding single ID */
+
+ yyval. listdef = InitList() ;
+ yyval. listdef = ExtendList(yyval. listdef , yyvsp[0]. strdef ) ;
+ LidCommand = DefaultCommand ;
+ ;
+ break;}
+case 22:
+#line 413 "iid.y"
+{
+ /* arg list is Id_list */
+
+ yyval. listdef = yyvsp[0]. listdef ;
+ LidCommand = "lid -kmn" ;
+ ;
+ break;}
+case 23:
+#line 423 "iid.y"
+{
+ /* arg list is Id_list */
+
+ yyval. listdef = yyvsp[0]. listdef ;
+ ;
+ break;}
+case 24:
+#line 432 "iid.y"
+{
+ /* make arg list holding single ID */
+
+ yyval. listdef = InitList() ;
+ yyval. listdef = ExtendList(yyval. listdef , yyvsp[0]. strdef ) ;
+ ;
+ break;}
+case 25:
+#line 439 "iid.y"
+{
+ /* make arg list holding names from set */
+
+ yyval. listdef = InitList() ;
+ yyval. listdef = SetList(yyval. listdef , yyvsp[0]. setdef ) ;
+ ;
+ break;}
+case 26:
+#line 446 "iid.y"
+{
+ /* extend arg list with additional ID */
+
+ yyval. listdef = ExtendList(yyvsp[-1]. listdef , yyvsp[0]. strdef ) ;
+ ;
+ break;}
+case 27:
+#line 452 "iid.y"
+{
+ /* extend arg list with additional file names */
+
+ yyval. listdef = SetList(yyvsp[-1]. listdef , yyvsp[0]. setdef ) ;
+ ;
+ break;}
+case 28:
+#line 461 "iid.y"
+{
+ /* make arg list holding single ID */
+
+ yyval. listdef = InitList() ;
+ yyval. listdef = ExtendList(yyval. listdef , yyvsp[0]. strdef ) ;
+ ;
+ break;}
+case 29:
+#line 468 "iid.y"
+{
+ /* extend arg list with additional ID */
+
+ yyval. listdef = ExtendList(yyvsp[-1]. listdef , yyvsp[0]. strdef ) ;
+ ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 557 "/usr/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug >= 3)
+ {
+ short *ssp1 = yyss - 1;
+#ifndef __cplusplus
+ fprintf (stderr, "state stack now");
+#else /* __cplusplus */
+ clog << "state stack now";
+#endif /* __cplusplus */
+ while (ssp1 != yyssp)
+#ifndef __cplusplus
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+#else /* __cplusplus */
+ clog << ' ' << *++ssp1;
+ clog << endl;
+#endif /* __cplusplus */
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+#ifndef __cplusplus
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#else /* __cplusplus */
+ clog << "Discarding token " << yychar << " (" << yytname[yychar1] << ")." << endl;
+#endif /* __cplusplus */
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+#ifndef __cplusplus
+ fprintf (stderr, "Error: state stack now");
+#else /* __cplusplus */
+ clog << "Error: state stack now";
+#endif /* __cplusplus */
+ while (ssp1 != yyssp)
+#ifndef __cplusplus
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+#else /* __cplusplus */
+ clog << ' ' << *++ssp1;
+ clog << endl;
+#endif /* __cplusplus */
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+#ifndef __cplusplus
+ fprintf(stderr, "Shifting error token, ");
+#else /* __cplusplus */
+ clog << "Shifting error token, ";
+#endif /* __cplusplus */
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 475 "iid.y"
+
+
+/* ScanLine - a global variable holding a pointer to the current
+ * command being scanned.
+ */
+char * ScanLine ;
+
+/* ScanPtr - a global pointer to the current scan position in ScanLine.
+ */
+char * ScanPtr ;
+
+/* yytext - buffer holding the token.
+ */
+char yytext [ MAXCMD ] ;
+
+/* yyerror - process syntax errors.
+ */
+int
+yyerror( char const * s )
+{
+ if (*ScanPtr == '\0') {
+ fprintf(stderr,"Syntax error near end of command.\n") ;
+ } else {
+ fprintf(stderr,"Syntax error on or before %s\n",ScanPtr) ;
+ }
+ return(0) ;
+}
+
+/* ScanInit - initialize the yylex routine for the new line of input.
+ * Basically just initializes the global variables that hold the char
+ * ptrs the scanner uses.
+ */
+void
+ScanInit( char * line )
+{
+ /* skip the leading white space - the yylex routine is sensitive
+ * to keywords in the first position on the command line.
+ */
+
+ while (isspace(*line)) ++line ;
+ ScanLine = line ;
+ ScanPtr = line ;
+}
+
+/* yylex - the scanner for iid. Basically a kludge ad-hoc piece of junk,
+ * but what the heck, if it works...
+ *
+ * Mostly just scans for non white space strings and returns ID for them.
+ * Does check especially for '(' and ')'. Just before returning ID it
+ * checks for command names if it is the first token on line or
+ * AND, OR, LID, AID if it is in the middle of a line.
+ */
+int
+yylex( void )
+{
+ char * bp ;
+ char c ;
+ int code = ID ;
+ char * dp ;
+ char * sp ;
+ int val ;
+
+ bp = ScanPtr ;
+ while (isspace(*bp)) ++bp ;
+ sp = bp ;
+ c = *sp++ ;
+ if ((c == '(') || (c == ')') || (c == '\0')) {
+ ScanPtr = sp ;
+ if (c == '\0') {
+ --ScanPtr ;
+ }
+ return(c) ;
+ } else {
+ dp = yytext ;
+ while (! ((c == '(') || (c == ')') || (c == '\0') || isspace(c))) {
+ *dp++ = c ;
+ c = *sp++ ;
+ }
+ *dp++ = '\0' ;
+ ScanPtr = sp - 1 ;
+ if (bp == ScanLine) {
+
+ /* first token on line, check for command names */
+
+ if (strcasecmp(yytext, "SS")) return(SS) ;
+ if (strcasecmp(yytext, "FILES")) return(FILES) ;
+ if (strcasecmp(yytext, "F")) return(FILES) ;
+ if (strcasecmp(yytext, "HELP")) return(HELP) ;
+ if (strcasecmp(yytext, "H")) return(HELP) ;
+ if (strcasecmp(yytext, "?")) return(HELP) ;
+ if (strcasecmp(yytext, "BEGIN")) return(BEGIN) ;
+ if (strcasecmp(yytext, "B")) return(BEGIN) ;
+ if (strcasecmp(yytext, "SETS")) return(SETS) ;
+ if (strcasecmp(yytext, "SHOW")) return(SHOW) ;
+ if (strcasecmp(yytext, "P")) return(SHOW) ;
+ if (strcasecmp(yytext, "OFF")) return(OFF) ;
+ if (strcasecmp(yytext, "Q")) return(OFF) ;
+ if (strcasecmp(yytext, "QUIT")) return(OFF) ;
+ if (yytext[0] == '!') {
+ code = SHELL_COMMAND ;
+ } else {
+ code = SHELL_QUERY ;
+ }
+ } else {
+
+ /* not first token, check for operator names */
+
+ if (strcasecmp(yytext, "LID")) return(LID) ;
+ if (strcasecmp(yytext, "AID")) return(AID) ;
+ if (strcasecmp(yytext, "AND")) return(AND) ;
+ if (strcasecmp(yytext, "OR")) return(OR) ;
+ if (strcasecmp(yytext, "NOT")) return(NOT) ;
+ if (strcasecmp(yytext, "MATCH")) return(MATCH) ;
+ if ((yytext[0] == 's' || yytext[0] == 'S') && isdigit(yytext[1])) {
+
+ /* this might be a set specification */
+
+ sp = &yytext[1] ;
+ val = 0 ;
+ for ( ; ; ) {
+ c = *sp++ ;
+ if (c == '\0') {
+ if (val < NextSetNum) {
+ yylval.setdef = TheSets[val] ;
+ return(SET) ;
+ }
+ }
+ if (isdigit(c)) {
+ val = (val * 10) + (c - '0') ;
+ } else {
+ break ;
+ }
+ }
+ }
+ }
+ yylval.strdef = (id_type *)malloc(sizeof(id_type) + strlen(yytext)) ;
+ if (yylval.strdef == NULL) {
+ fatal("Out of memory in yylex") ;
+ }
+ yylval.strdef->next_id = NULL ;
+ if (code == SHELL_COMMAND) {
+ strcpy(yylval.strdef->id, &yytext[1]) ;
+ } else {
+ strcpy(yylval.strdef->id, yytext) ;
+ }
+ return(code) ;
+ }
+}
+
+/* The main program for iid - parse the command line, initialize processing,
+ * loop processing one command at a time.
+ */
+int
+main( int argc , char * argv [ ] )
+{
+ int c ; /* current option */
+ char * CmdPtr ; /* Points to the command string */
+ char Command [ MAXCMD ] ; /* Buffer for reading commands */
+ int Do1 = 0 ; /* 1 if should only do 1 command */
+ int DoPrompt ; /* 1 if should write a prompt */
+ int errors = 0 ; /* error count */
+
+ DoPrompt = isatty(fileno(stdin)) ;
+ while ((c = getopt(argc, argv, "Hac:")) != EOF) {
+ switch(c) {
+ case 'a':
+ DefaultCommand = "aid -kmn" ;
+ break ;
+ case 'c':
+ CmdPtr = optarg ;
+ Do1 = 1 ;
+ break ;
+ case 'H':
+ fputs("\
+iid: interactive ID database query tool. Call with:\n\
+ iid [-a] [-c] [-H]\n\
+\n\
+-a\tUse the aid as the default query command (not lid).\n\
+-c cmd\tExecute the single query cmd and exit.\n\
+-H\tPrint this message and exit.\n\
+\n\
+To get help after starting program type 'help'.\n\
+",stderr) ;
+ exit(0) ;
+ default:
+ ++errors ;
+ break ;
+ }
+ }
+ if (argc != optind) {
+ fputs("iid: Excess arguments ignored.\n",stderr) ;
+ ++errors ;
+ }
+ if (errors) {
+ fputs("run iid -H for help.\n",stderr) ;
+ exit(1) ;
+ }
+
+ /* initialize global data */
+
+ InitIid() ;
+
+ /* run the parser */
+
+ if (Do1) {
+ ScanInit(CmdPtr) ;
+ exit(yyparse()) ;
+ } else {
+ for ( ; ; ) {
+ if (DoPrompt) {
+ fputs(Prompt, stdout) ;
+ fflush(stdout) ;
+ }
+ gets(Command) ;
+ if (feof(stdin)) {
+ if (DoPrompt) fputs("\n", stdout) ;
+ strcpy(Command, "off") ;
+ }
+ ScanInit(Command) ;
+ errors += yyparse() ;
+ }
+ }
+}
+
+
+/* ArgListSize - count the size of an arg list so can alloca() enough
+ * space for the command.
+ */
+int
+ArgListSize( id_list_type * idlp )
+{
+ id_type * idep ;
+ int size = 0;
+
+ idep = idlp->id_list ;
+ while (idep != NULL) {
+ size += 1 + strlen(idep->id);
+ idep = idep->next_id;
+ }
+ return size;
+}
+
+/* SetListSize - count the size of a string build up from a set so we can
+ * alloca() enough space for args.
+ */
+int
+SetListSize( set_type * sp )
+{
+ int i ;
+ int size = 0 ;
+
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ size += 1 + strlen(FileList[i]->name);
+ }
+ }
+ }
+ return size;
+}
+
+/* FlushFiles - clear out the TheFiles array for the start of a new
+ * query.
+ */
+void
+FlushFiles( void )
+{
+ int i ;
+
+ if (TheFiles != NULL) {
+ for (i = 0; i <= MaxCurFile; ++i) {
+ TheFiles[i] = 0 ;
+ }
+ }
+ MaxCurFile = 0 ;
+}
+
+/* fatal - sometimes the only thing to do is die...
+ */
+void
+fatal( char const * s )
+{
+ fprintf(stderr,"Fatal error: %s\n", s) ;
+ exit(1) ;
+}
+
+/* CountBits - count the number of bits in a bit set. Actually fairly
+ * tricky since it needs to deal with sets having infinite tails
+ * as a result of a NOT operation.
+ */
+int
+CountBits( set_type * sp )
+{
+ unsigned long bit_mask ;
+ int count = 0 ;
+ int i ;
+
+ i = 0;
+ for ( ; ; ) {
+ for (bit_mask = high_bit; bit_mask != 0; bit_mask >>= 1) {
+ if (bit_mask == NextMaskBit && i == NextMaskWord) {
+ return(count) ;
+ }
+ if (i < sp->set_size) {
+ if (sp->set_data[i] & bit_mask) {
+ ++count ;
+ }
+ } else {
+ if (sp->set_tail == 0) return count;
+ if (sp->set_tail & bit_mask) {
+ ++count;
+ }
+ }
+ }
+ ++i;
+ }
+}
+
+/* OneDescription - Print a description of a set. This includes
+ * the set number, the number of files in the set, and the
+ * set description string.
+ */
+void
+OneDescription( set_type * sp )
+{
+ int elt_count ;
+ char setnum[20] ;
+
+ sprintf(setnum,"S%d",sp->set_num) ;
+ elt_count = CountBits(sp) ;
+ printf("%5s %6d %s\n",setnum,elt_count,sp->set_desc) ;
+}
+
+/* DescribeSets - Print description of all the sets.
+ */
+void
+DescribeSets( void )
+{
+ int i ;
+
+ if (NextSetNum > 0) {
+ for (i = 0; i < NextSetNum; ++i) {
+ OneDescription(TheSets[i]) ;
+ }
+ } else {
+ printf("No sets defined yet.\n") ;
+ }
+}
+
+/* SetList - Go through the bit set and add the file names in
+ * it to an identifier list.
+ */
+id_list_type *
+SetList( id_list_type * idlp , set_type * sp )
+{
+ int i ;
+ id_type * idep ;
+
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ idep = (id_type *)malloc(sizeof(id_type) +
+ strlen(FileList[i]->name)) ;
+ if (idep == NULL) {
+ fatal("Out of memory in SetList") ;
+ }
+ idep->next_id = NULL ;
+ strcpy(idep->id, FileList[i]->name) ;
+ idlp = ExtendList(idlp, idep) ;
+ }
+ }
+ }
+ return(idlp) ;
+}
+
+/* PrintSet - Go through the bit set and print the file names
+ * corresponding to all the set bits.
+ */
+void
+PrintSet( set_type * sp )
+{
+ int i ;
+
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ printf("%s\n",FileList[i]->name) ;
+ }
+ }
+ }
+}
+
+/* Free up all space used by current set of sets and reset all
+ * set numbers.
+ */
+void
+FlushSets( void )
+{
+ int i ;
+
+ for (i = 0; i < NextSetNum; ++i) {
+ free(TheSets[i]->set_desc) ;
+ free(TheSets[i]) ;
+ }
+ NextSetNum = 0 ;
+}
+
+/* InitList - create an empty identifier list.
+ */
+id_list_type *
+InitList( void )
+{
+ id_list_type * idlp ;
+
+ idlp = (id_list_type *)malloc(sizeof(id_list_type)) ;
+ if (idlp == NULL) {
+ fatal("Out of memory in InitList") ;
+ }
+ idlp->id_count = 0 ;
+ idlp->end_ptr_ptr = & (idlp->id_list) ;
+ idlp->id_list = NULL ;
+ return(idlp) ;
+}
+
+/* ExtendList - add one identifier to an ID list.
+ */
+id_list_type *
+ExtendList( id_list_type * idlp , id_type * idp )
+{
+ *(idlp->end_ptr_ptr) = idp ;
+ idlp->end_ptr_ptr = &(idp->next_id) ;
+ return(idlp) ;
+}
+
+/* InitIid - do all initial processing for iid.
+ * 1) Determine the size of a unsigned long for bit set stuff.
+ * 2) Find out the name of the pager program to use.
+ * 3) Create the HelpSet (pointing to the help file).
+ * 4) Setup the prompt.
+ */
+void
+InitIid( void )
+{
+ unsigned long bit_mask = 1 ; /* find number of bits in long */
+ int i ;
+ char const * page ; /* pager program */
+
+ do {
+ high_bit = bit_mask ;
+ bit_mask <<= 1 ;
+ } while (bit_mask != 0) ;
+
+ NextMaskBit = high_bit ;
+
+ page = getenv("PAGER") ;
+ if (page == NULL) {
+ page = PAGER ;
+ }
+ strcpy(Pager, page) ;
+
+ FlushFiles() ;
+ InstallFile(IID_HELP_FILE) ;
+ HelpSet = (set_type *)
+ malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ;
+ if (HelpSet == NULL) {
+ fatal("No memory for set in InitIid") ;
+ }
+ HelpSet->set_tail = 0 ;
+ HelpSet->set_desc = NULL ;
+ HelpSet->set_size = MaxCurFile + 1 ;
+ for (i = 0; i <= MaxCurFile; ++i) {
+ HelpSet->set_data[i] = TheFiles[i] ;
+ }
+
+ page = getenv("PS1") ;
+ if (page == NULL) {
+ page = PROMPT ;
+ }
+ strcpy(Prompt, page) ;
+}
+
+/* InstallFile - install a file name in the symtab. Return the
+ * symbol table pointer of the file.
+ */
+symtab_type *
+InstallFile( char const * fp )
+{
+ char c ;
+ unsigned long hash_code ;
+ int i ;
+ char const * sp ;
+ symtab_type * symp ;
+
+ hash_code = 0 ;
+ sp = fp ;
+ while ((c = *sp++) != '\0') {
+ hash_code <<= 1 ;
+ hash_code ^= (unsigned long)(c) ;
+ if (hash_code & high_bit) {
+ hash_code &= ~ high_bit ;
+ hash_code ^= 1 ;
+ }
+ }
+ hash_code %= HASH_SIZE ;
+ symp = HashTable[hash_code] ;
+ while (symp != NULL && strcmp(symp->name, fp)) {
+ symp = symp->hash_link ;
+ }
+ if (symp == NULL) {
+ symp = (symtab_type *)malloc(sizeof(symtab_type) + strlen(fp)) ;
+ if (symp == NULL) {
+ fatal("No memory for symbol table entry in InstallFile") ;
+ }
+ strcpy(symp->name, fp) ;
+ symp->hash_link = HashTable[hash_code] ;
+ HashTable[hash_code] = symp ;
+ if (NextMaskWord >= FileSpace) {
+ FileSpace += 1000 ;
+ if (TheFiles != NULL) {
+ TheFiles = (unsigned long *)
+ realloc(TheFiles, sizeof(unsigned long) * FileSpace) ;
+ } else {
+ TheFiles = (unsigned long *)
+ malloc(sizeof(unsigned long) * FileSpace) ;
+ }
+ if (TheFiles == NULL) {
+ fatal("No memory for TheFiles in InstallFile") ;
+ }
+ for (i = NextMaskWord; i < FileSpace; ++i) {
+ TheFiles[i] = 0 ;
+ }
+ }
+ symp->mask_word = NextMaskWord ;
+ symp->mask_bit = NextMaskBit ;
+ NextMaskBit >>= 1 ;
+ if (NextMaskBit == 0) {
+ NextMaskBit = high_bit ;
+ ++NextMaskWord ;
+ }
+ if (NextFileNum >= ListSpace) {
+ ListSpace += 1000 ;
+ if (FileList == NULL) {
+ FileList = (symtab_type **)
+ malloc(sizeof(symtab_type *) * ListSpace) ;
+ } else {
+ FileList = (symtab_type **)
+ realloc(FileList, ListSpace * sizeof(symtab_type *)) ;
+ }
+ if (FileList == NULL) {
+ fatal("No memory for FileList in InstallFile") ;
+ }
+ }
+ FileList[NextFileNum++] = symp ;
+ /* put code here to sort the file list by name someday */
+ }
+ TheFiles[symp->mask_word] |= symp->mask_bit ;
+ if (symp->mask_word > MaxCurFile) {
+ MaxCurFile = symp->mask_word ;
+ }
+ return(symp) ;
+}
+
+/* RunPager - run the users pager program on the list of files
+ * in the set.
+ */
+void
+RunPager( char * pp , set_type * sp )
+{
+ char * cmd ;
+ int i ;
+
+ cmd = (char *)TEMP_ALLOC(SetListSize(sp) + strlen(pp) + 2);
+ strcpy(cmd, pp) ;
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ strcat(cmd, " ") ;
+ strcat(cmd, FileList[i]->name) ;
+ }
+ }
+ }
+ system(cmd) ;
+ TEMP_FREE(cmd) ;
+}
+
+/* AddSet - add a new set to the universal list of sets. Assign
+ * it the next set number.
+ */
+void
+AddSet( set_type * sp )
+{
+ if (NextSetNum >= SetSpace) {
+ SetSpace += 1000 ;
+ if (TheSets != NULL) {
+ TheSets = (set_type **)
+ realloc(TheSets, sizeof(set_type *) * SetSpace) ;
+ } else {
+ TheSets = (set_type **)
+ malloc(sizeof(set_type *) * SetSpace) ;
+ }
+ if (TheSets == NULL) {
+ fatal("No memory for TheSets in AddSet") ;
+ }
+ }
+ sp->set_num = NextSetNum ;
+ TheSets[NextSetNum++] = sp ;
+}
+
+/* RunProg - run a program with arguments from id_list and
+ * accept list of file names back from the program which
+ * are installed in the symbol table and used to construct
+ * a new set.
+ */
+set_type *
+RunProg( char const * pp , id_list_type * idlp )
+{
+ int c ;
+ char * cmd ;
+ char * dp ;
+ char file [ MAXCMD ] ;
+ int i ;
+ id_type * idep ;
+ id_type * next_id ;
+ FILE * prog ;
+ set_type * sp ;
+
+ cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2);
+ FlushFiles() ;
+ strcpy(cmd, pp) ;
+ idep = idlp->id_list ;
+ while (idep != NULL) {
+ strcat(cmd, " ") ;
+ strcat(cmd, idep->id) ;
+ next_id = idep->next_id ;
+ free(idep) ;
+ idep = next_id ;
+ }
+ free(idlp) ;
+
+ /* run program with popen, reading the output. Assume each
+ * white space terminated string is a file name.
+ */
+ prog = popen(cmd, "r") ;
+ dp = file ;
+ while ((c = getc(prog)) != EOF) {
+ if (isspace(c)) {
+ if (dp != file) {
+ *dp++ = '\0' ;
+ InstallFile(file) ;
+ dp = file ;
+ }
+ } else {
+ *dp++ = c ;
+ }
+ }
+ if (dp != file) {
+ *dp++ = '\0' ;
+ InstallFile(file) ;
+ }
+ if (pclose(prog) != 0) {
+ /* if there was an error make an empty set, who knows what
+ * garbage the program printed.
+ */
+ FlushFiles() ;
+ }
+
+ sp = (set_type *)
+ malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ;
+ if (sp == NULL) {
+ fatal("No memory for set in RunProg") ;
+ }
+ sp->set_tail = 0 ;
+ sp->set_desc = (char *)malloc(strlen(cmd) + 1) ;
+ if (sp->set_desc == NULL) {
+ fatal("No memory for set description in RunProg") ;
+ }
+ strcpy(sp->set_desc, cmd) ;
+ sp->set_size = MaxCurFile + 1 ;
+ for (i = 0; i <= MaxCurFile; ++i) {
+ sp->set_data[i] = TheFiles[i] ;
+ }
+ AddSet(sp) ;
+ TEMP_FREE(cmd);
+ return(sp) ;
+}
+
+/* SetDirectory - change the working directory. This will
+ * determine which ID file is found by the subprograms.
+ */
+void
+SetDirectory( id_type * dir )
+{
+ if (chdir(dir->id) != 0) {
+ fprintf(stderr,"Directory %s not accessible.\n", dir->id) ;
+ }
+ free(dir) ;
+}
+
+/* SetIntersect - construct a new set from the intersection
+ * of two others. Also construct a new description string.
+ */
+set_type *
+SetIntersect( set_type * sp1 , set_type * sp2 )
+{
+ char * desc ;
+ int i ;
+ int len1 ;
+ int len2 ;
+ set_type * new_set ;
+ int new_size ;
+
+ if (sp1->set_tail || sp2->set_tail) {
+ new_size = MAX(sp1->set_size, sp2->set_size) ;
+ } else {
+ new_size = MIN(sp1->set_size, sp2->set_size) ;
+ }
+ new_set = (set_type *)malloc(sizeof(set_type) +
+ (new_size - 1) * sizeof(unsigned long)) ;
+ if (new_set == NULL) {
+ fatal("No memory for set in SetIntersect") ;
+ }
+ len1 = strlen(sp1->set_desc) ;
+ len2 = strlen(sp2->set_desc) ;
+ desc = (char *)malloc(len1 + len2 + 10) ;
+ if (desc == NULL) {
+ fatal("No memory for set description in SetIntersect") ;
+ }
+ new_set->set_desc = desc ;
+ strcpy(desc,"(") ;
+ ++desc ;
+ strcpy(desc, sp1->set_desc) ;
+ desc += len1 ;
+ strcpy(desc, ") AND (") ;
+ desc += 7 ;
+ strcpy(desc, sp2->set_desc) ;
+ desc += len2 ;
+ strcpy(desc, ")") ;
+ AddSet(new_set) ;
+ new_set->set_size = new_size ;
+ for (i = 0; i < new_size; ++i) {
+ new_set->set_data[i] =
+ ((i < sp1->set_size) ? sp1->set_data[i] : sp1->set_tail) &
+ ((i < sp2->set_size) ? sp2->set_data[i] : sp2->set_tail) ;
+ }
+ new_set->set_tail = sp1->set_tail & sp2->set_tail ;
+ return(new_set) ;
+}
+
+/* SetUnion - construct a new set from the union of two others.
+ * Also construct a new description string.
+ */
+set_type *
+SetUnion( set_type * sp1 , set_type * sp2 )
+{
+ char * desc ;
+ int i ;
+ int len1 ;
+ int len2 ;
+ set_type * new_set ;
+ int new_size ;
+
+ new_size = MAX(sp1->set_size, sp2->set_size) ;
+ new_set = (set_type *)malloc(sizeof(set_type) +
+ (new_size - 1) * sizeof(unsigned long)) ;
+ if (new_set == NULL) {
+ fatal("No memory for set in SetUnion") ;
+ }
+ len1 = strlen(sp1->set_desc) ;
+ len2 = strlen(sp2->set_desc) ;
+ desc = (char *)malloc(len1 + len2 + 9) ;
+ if (desc == NULL) {
+ fatal("No memory for set description in SetUnion") ;
+ }
+ new_set->set_desc = desc ;
+ strcpy(desc,"(") ;
+ ++desc ;
+ strcpy(desc, sp1->set_desc) ;
+ desc += len1 ;
+ strcpy(desc, ") OR (") ;
+ desc += 6 ;
+ strcpy(desc, sp2->set_desc) ;
+ desc += len2 ;
+ strcpy(desc, ")") ;
+ AddSet(new_set) ;
+ new_set->set_size = new_size ;
+ for (i = 0; i < new_size; ++i) {
+ new_set->set_data[i] =
+ ((i < sp1->set_size) ? (sp1->set_data[i]) : sp1->set_tail) |
+ ((i < sp2->set_size) ? (sp2->set_data[i]) : sp2->set_tail) ;
+ }
+ new_set->set_tail = sp1->set_tail | sp2->set_tail ;
+ return(new_set) ;
+}
+
+/* SetInverse - construct a new set from the inverse of another.
+ * Also construct a new description string.
+ *
+ * This is kind of tricky. An inverse set in iid may grow during
+ * the course of a session. By NOTing the set_tail extension the
+ * inverse at any given time will be defined as the inverse against
+ * a universe that grows as additional queries are made and new files
+ * are added to the database.
+ *
+ * Several alternative definitions were possible (snapshot the
+ * universe at the time of the NOT, go read the ID file to
+ * determine the complete universe), but this one was the one
+ * I picked.
+ */
+set_type *
+SetInverse( set_type * sp )
+{
+ char * desc ;
+ int i ;
+ set_type * new_set ;
+
+ new_set = (set_type *)malloc(sizeof(set_type) +
+ (sp->set_size - 1) * sizeof(unsigned long)) ;
+ if (new_set == NULL) {
+ fatal("No memory for set in SetInverse") ;
+ }
+ desc = (char *)malloc(strlen(sp->set_desc) + 5) ;
+ if (desc == NULL) {
+ fatal("No memory for set description in SetInverse") ;
+ }
+ new_set->set_desc = desc ;
+ strcpy(desc,"NOT ") ;
+ desc += 4 ;
+ strcpy(desc, sp->set_desc) ;
+ AddSet(new_set) ;
+ new_set->set_size = sp->set_size ;
+ for (i = 0; i < sp->set_size; ++i) {
+ new_set->set_data[i] = ~ sp->set_data[i] ;
+ }
+ new_set->set_tail = ~ sp->set_tail ;
+ return(new_set) ;
+}
+
+/* RunShell - run a program with arguments from id_list.
+ */
+void
+RunShell( char * pp , id_list_type * idlp )
+{
+ char * cmd ;
+ id_type * idep ;
+ id_type * next_id ;
+
+ cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2);
+ strcpy(cmd, pp) ;
+ idep = idlp->id_list ;
+ while (idep != NULL) {
+ strcat(cmd, " ") ;
+ strcat(cmd, idep->id) ;
+ next_id = idep->next_id ;
+ free(idep) ;
+ idep = next_id ;
+ }
+ free(idlp) ;
+ system(cmd) ;
+ TEMP_FREE(cmd);
+}
diff --git a/iid.help b/iid.help
new file mode 100644
index 0000000..6ec102c
--- /dev/null
+++ b/iid.help
@@ -0,0 +1,92 @@
+The iid program is an interactive shell on top of the mkid, lid, aid
+database programs. It allows interactive queries of an ID database in
+a fashion similar to a DIALOG session. Iid remembers the sets of files
+that were reported by any lid or aid request. These sets are refered
+to by set numbers. The commands available are:
+
+BEGIN <directory> cd to directory (presumably containing an ID file).
+B short for BEGIN
+SS <query> run query displaying the sets generated
+FILES <query> run query listing the files in the final set
+F short for FILES
+SHOW <set number> run pager program on files in set
+P short for SHOW
+SETS show currently defined sets
+HELP run pager on this file
+? or H short commands for HELP
+OFF exit iid
+<cmd> run a shell command as a file name query
+!<cmd> run a shell command
+
+A <set number> is the letter 's' (or 'S') followed (with no space) by
+a number. Set numbers may be used as terms in a query.
+
+A <query> is:
+ <set number>
+ <identifier>
+ lid <identifier list>
+ aid <identifier list>
+ match <wild card list>
+ <query> or <query>
+ <query> and <query>
+
+The words "lid", "aid", "match", "or", and "and" are keywords, along
+with any word that looks like a set number. If you have to use one of
+these (or in arguments to lid, aid or match, shell escape characters)
+then quote the name.
+
+The "match" operator constructs a set of files by running the "pid"
+program with the wild card pattern as an argument. This is the only
+operator which constructs sets based on file names rather than
+contents.
+
+An identifier by itself is simply shorthand for "lid identifier". (If
+the -a option was used to invoke iid, then a simple identifier is
+shorthand for "aid identifier").
+
+Example run:
+
+===> iid
+===> ss lid "^get" or lid "Arg$"
+ S0 14 lid -kmn "^get"
+ S1 3 lid -kmn "Arg$"
+ S2 15 (lid -kmn "^get") OR (lid -kmn "Arg$")
+===> f s1
+lid.c
+paths.c
+init.c
+===> ls *.c
+ S3 28 ls *.c
+===> ls s*
+ S4 9 ls s*
+===> ss s3 and s4
+ S5 4 (ls *.c) AND (ls s*)
+===> !grep vhil s5
+scan-c.c: setCArgs("vhil",'+',"v");
+scan-c.c: setCArgs("vhil",'+',"v");
+===> off
+
+In this example the 'ss' command displays the sets it creats as it
+does the parts of the query. In this case 3 sets are created, set S0
+has 14 files in it, set S1 has 3 files and the union of the two sets,
+S2, has 15 files. A description of the query that created any given
+set is kept along with the set and displayed when sets are printed.
+
+The 'f s1' command says list the files in set S1, and the three files
+in the set are displayed.
+
+The 'ls' commands are examples of using arbitrary shell commands to
+generate lists of files. In this case the 'ls' command. (This could
+have been done as part of another query using the 'match' operator).
+
+The '!grep vhil s5' command runs the 'grep' shell command passing as
+arguments 'vhil' and the names of all the files in s5.
+
+The 'off' command terminated the example session.
+
+Keywords, commands, and set numbers are recognized regardless of case
+(and is And is aNd). Other parameters are case sensitive.
+
+The iid program can also be run in a batch mode using the -c option.
+For more information on command line options, run "iid -H", or use the
+Unix 'man' command.
diff --git a/iid.y b/iid.y
new file mode 100644
index 0000000..b923dcf
--- /dev/null
+++ b/iid.y
@@ -0,0 +1,1334 @@
+%{
+/* iid.y -- interactive mkid query language
+ Copyright (C) 1991 Tom Horsley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#ifdef HAVE_ALLOCA
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#define TEMP_ALLOC(s) alloca(s)
+#define TEMP_FREE(s)
+
+#else /* not HAVE_ALLOCA
+
+#define TEMP_ALLOC(s) malloc(s)
+#define TEMP_FREE(s) free(s)
+
+#endif /* not HAVE_ALLOCA */
+
+#define HASH_SIZE 947 /* size of hash table for file names */
+#define INIT_FILES 8000 /* start with bits for this many */
+#define INIT_SETSPACE 500 /* start with room for this many */
+#define MAXCMD 1024 /* input command buffer size */
+
+#define MAX(a,b) (((a)<(b))?(b):(a))
+#define MIN(a,b) (((a)>(b))?(b):(a))
+
+#ifndef PAGER
+#define PAGER "pg"
+#endif
+
+#define PROMPT "iid> "
+
+/* set_type is the struct defining a set of file names
+ * The file names are stored in a symbol table and assigned
+ * unique numbers. The set is a bit set of file numbers.
+ * One of these set structs is calloced for each new set
+ * constructed, the size allocated depends on the max file
+ * bit number. An array of pointers to sets are kept to
+ * represent the complete set of sets.
+ */
+
+struct set_struct {
+ char * set_desc ; /* string describing the set */
+ int set_num ; /* the set number */
+ int set_size ; /* number of long words in set */
+ unsigned long int set_tail ; /* set extended with these bits */
+ unsigned long int set_data[1] ;/* the actual set data (calloced) */
+} ;
+typedef struct set_struct set_type ;
+
+/* id_type is one element of an id_list
+ */
+
+struct id_struct {
+ struct id_struct * next_id ; /* Linked list of IDs */
+ char id [ 1 ] ; /* calloced data holding id string */
+} ;
+typedef struct id_struct id_type ;
+
+/* id_list_type is used during parsing to build lists of
+ * identifiers that will eventually represent arguments
+ * to be passed to the database query programs.
+ */
+
+struct id_list_struct {
+ int id_count ; /* count of IDs in the list */
+ id_type * * end_ptr_ptr ;/* pointer to link word at end of list */
+ id_type * id_list ; /* pointer to list of IDs */
+} ;
+typedef struct id_list_struct id_list_type ;
+
+/* symtab_type is used to record file names in the symbol table.
+ */
+struct symtab_struct {
+ struct symtab_struct * hash_link ; /* list of files with same hash code */
+ int mask_word ; /* word in bit vector */
+ unsigned long mask_bit ; /* bit in word */
+ char name [ 1 ] ; /* the file name */
+} ;
+typedef struct symtab_struct symtab_type ;
+
+/* LidCommand is the command to run for a Lid_group. It is set
+ * to "lid -kmn" if explicitly preceeded by "lid", otherwise
+ * it is the default command which is determined by an option.
+ */
+char const * LidCommand ;
+
+/* DefaultCommand is the default command for a Lid_group. If
+ * the -a option is given to iid, it is set to use 'aid'.
+ */
+char const * DefaultCommand = "lid -kmn" ;
+
+/* FileList is a lexically ordered list of file symbol table
+ * pointers. It is dynamically expanded when necessary.
+ */
+symtab_type * * FileList = NULL ;
+
+/* FileSpace is the number of long ints in TheFiles array.
+ */
+int FileSpace = 0 ;
+
+/* HashTable is the symbol table used to store file names. Each
+ * new name installed is assigned the next consecutive file number.
+ */
+symtab_type * HashTable [ HASH_SIZE ] ;
+
+/* HelpSet is a dummy set containing only one bit set which corresponds
+ * to the help file name. Simply a cheesy way to maximize sharing of
+ * the code that runs the pager.
+ */
+set_type * HelpSet ;
+
+/* high_bit is a unsigned long with the most significant bit set.
+ */
+unsigned long high_bit ;
+
+/* ListSpace is the amount of space avail in the FileList.
+ */
+int ListSpace = 0 ;
+
+/* MaxCurFile - max word that has any bit currently set in the
+ * TheFiles array.
+ */
+int MaxCurFile = 0 ;
+
+/* NextFileNum is the file number that will be assigned to the next
+ * new file name seen when it is installed in the symtab.
+ */
+int NextFileNum = 0 ;
+
+/* NextMaskBit is the bit within the next mask word that will
+ * correspond to the next file added to the symbol table.
+ */
+unsigned long NextMaskBit ;
+
+/* NextMaskWord is the next word number to be assigned to a file
+ * bit mask entry.
+ */
+int NextMaskWord = 0 ;
+
+/* NextSetNum is the number that will be assigned to the next set
+ * created. Starts at 0 because I am a C programmer.
+ */
+int NextSetNum = 0 ;
+
+/* The PAGER program to run on a SHOW command.
+ */
+char Pager[MAXCMD] ;
+
+/* Prompt - the string to use for a prompt.
+ */
+char Prompt[MAXCMD] ;
+
+/* SetSpace is the number of pointers available in TheSets. TheSets
+ * is realloced when we run out of space.
+ */
+int SetSpace = 0 ;
+
+/* TheFiles is a bit set used to construct the initial set of files
+ * generated while running one of the subprograms. It is copied to
+ * the alloced set once we know how many bits are set.
+ */
+unsigned long * TheFiles = NULL ;
+
+/* TheSets is a dynamically allocated array of pointers pointing
+ * the sets that have been allocated. It represents the set of
+ * sets.
+ */
+set_type * * TheSets = NULL ;
+
+/* VerboseQuery controls the actions of the semantic routines during
+ * the process of a query. If TRUE the sets are described as they
+ * are constructed.
+ */
+int VerboseQuery ;
+
+int yyerror( char const * s ) ;
+void ScanInit( char * line ) ;
+int yylex( void ) ;
+int ArgListSize( id_list_type * idlp ) ;
+int SetListSize( set_type * sp ) ;
+void FlushFiles( void ) ;
+void fatal( char const * s ) ;
+int CountBits( set_type * sp ) ;
+void OneDescription( set_type * sp ) ;
+void DescribeSets( void ) ;
+id_list_type * SetList( id_list_type * idlp , set_type * sp ) ;
+void PrintSet( set_type * sp ) ;
+void FlushSets( void ) ;
+id_list_type * InitList( void ) ;
+id_list_type * ExtendList( id_list_type * idlp , id_type * idp ) ;
+void InitIid( void ) ;
+symtab_type * InstallFile( char const * fp ) ;
+void RunPager( char * pp , set_type * sp ) ;
+void AddSet( set_type * sp ) ;
+set_type * RunProg( char const * pp , id_list_type * idlp ) ;
+void SetDirectory( id_type * dir ) ;
+set_type * SetIntersect( set_type * sp1 , set_type * sp2 ) ;
+set_type * SetUnion( set_type * sp1 , set_type * sp2 ) ;
+set_type * SetInverse( set_type * sp ) ;
+void RunShell( char * pp , id_list_type * idlp ) ;
+
+%}
+
+%union {
+ set_type * setdef ;
+ id_type * strdef ;
+ id_list_type * listdef ;
+}
+
+%token < setdef > SET
+
+%token < strdef > ID SHELL_QUERY SHELL_COMMAND
+
+%type < setdef > Query Primitive
+
+%type < listdef > Lid_group Aid_group Id_list Command_list
+
+%token LID AID BEGIN SETS SS FILES SHOW HELP OFF MATCH
+
+%left OR
+
+%left AND
+
+%left NOT
+
+%start Command
+
+%%
+
+Command :
+ BEGIN ID
+ {
+ /* cd to the directory specified as argument, flush sets */
+
+ SetDirectory($2) ;
+ FlushSets() ;
+ }
+| Set_query Query
+| File_query Query
+ {
+ /* print the list of files resulting from Query */
+
+ PrintSet($2) ;
+ }
+| SHOW SET
+ {
+ /* run PAGER on the list of files in SET */
+
+ RunPager(Pager, $2) ;
+ }
+| SETS
+ {
+ /* describe sets created so far */
+
+ DescribeSets() ;
+ }
+| HELP
+ {
+ /* run PAGER on the help file */
+
+ RunPager(Pager, HelpSet) ;
+ }
+| OFF
+ {
+ exit(0) ;
+ }
+| SHELL_QUERY Command_list
+ {
+ /* run the shell command and eat the results as a file set */
+
+ OneDescription(RunProg($1->id, $2)) ;
+ free($1) ;
+ }
+| SHELL_COMMAND Command_list
+ {
+ /* run the shell command */
+
+ RunShell($1->id, $2) ;
+ free($1) ;
+ }
+;
+
+Set_query :
+ SS
+ {
+ /* Turn on verbose query flag */
+
+ VerboseQuery = 1 ;
+ }
+;
+
+File_query :
+ FILES
+ {
+ /* Turn off verbose query flag */
+
+ VerboseQuery = 0 ;
+ }
+;
+
+Query :
+ Primitive
+ {
+ /* value of query is set associated with primitive */
+
+ $$ = $1 ;
+ }
+| Query AND Query
+ {
+ /* value of query is intersection of the two query sets */
+
+ $$ = SetIntersect($1, $3) ;
+ if (VerboseQuery) {
+ OneDescription($$) ;
+ }
+ }
+| Query OR Query
+ {
+ /* value of query is union of the two query sets */
+
+ $$ = SetUnion($1, $3) ;
+ if (VerboseQuery) {
+ OneDescription($$) ;
+ }
+ }
+| NOT Query
+ {
+ /* value of query is inverse of other query */
+
+ $$ = SetInverse($2) ;
+ if (VerboseQuery) {
+ OneDescription($$) ;
+ }
+ }
+;
+
+Primitive :
+ SET
+ {
+ /* Value of primitive is value of recorded set */
+
+ $$ = $1 ;
+ }
+| Lid_group
+ {
+ /* Value of primitive is obtained by running an lid query */
+
+ $$ = RunProg(LidCommand, $1) ;
+ if (VerboseQuery) {
+ OneDescription($$) ;
+ }
+ }
+| Aid_group
+ {
+ /* Value of primitive is obtained by running an aid query */
+
+ $$ = RunProg("aid -kmn", $1) ;
+ if (VerboseQuery) {
+ OneDescription($$) ;
+ }
+ }
+| MATCH Id_list
+ {
+ /* Match names from database against pattern */
+ $$ = RunProg("pid -kmn", $2) ;
+ if (VerboseQuery) {
+ OneDescription($$) ;
+ }
+ }
+| '(' Query ')'
+ {
+ /* value of primitive is value of query */
+
+ $$ = $2 ;
+ }
+;
+
+Lid_group :
+ ID
+ {
+ /* make arg list holding single ID */
+
+ $$ = InitList() ;
+ $$ = ExtendList($$, $1) ;
+ LidCommand = DefaultCommand ;
+ }
+| LID Id_list
+ {
+ /* arg list is Id_list */
+
+ $$ = $2 ;
+ LidCommand = "lid -kmn" ;
+ }
+;
+
+Aid_group :
+ AID Id_list
+ {
+ /* arg list is Id_list */
+
+ $$ = $2 ;
+ }
+;
+
+Command_list :
+ ID
+ {
+ /* make arg list holding single ID */
+
+ $$ = InitList() ;
+ $$ = ExtendList($$, $1) ;
+ }
+| SET
+ {
+ /* make arg list holding names from set */
+
+ $$ = InitList() ;
+ $$ = SetList($$, $1) ;
+ }
+| Command_list ID
+ {
+ /* extend arg list with additional ID */
+
+ $$ = ExtendList($1, $2) ;
+ }
+| Command_list SET
+ {
+ /* extend arg list with additional file names */
+
+ $$ = SetList($1, $2) ;
+ }
+;
+
+Id_list :
+ ID
+ {
+ /* make arg list holding single ID */
+
+ $$ = InitList() ;
+ $$ = ExtendList($$, $1) ;
+ }
+| Id_list ID
+ {
+ /* extend arg list with additional ID */
+
+ $$ = ExtendList($1, $2) ;
+ }
+;
+
+%%
+
+/* ScanLine - a global variable holding a pointer to the current
+ * command being scanned.
+ */
+char * ScanLine ;
+
+/* ScanPtr - a global pointer to the current scan position in ScanLine.
+ */
+char * ScanPtr ;
+
+/* yytext - buffer holding the token.
+ */
+char yytext [ MAXCMD ] ;
+
+/* yyerror - process syntax errors.
+ */
+int
+yyerror( char const * s )
+{
+ if (*ScanPtr == '\0') {
+ fprintf(stderr,"Syntax error near end of command.\n") ;
+ } else {
+ fprintf(stderr,"Syntax error on or before %s\n",ScanPtr) ;
+ }
+ return(0) ;
+}
+
+/* ScanInit - initialize the yylex routine for the new line of input.
+ * Basically just initializes the global variables that hold the char
+ * ptrs the scanner uses.
+ */
+void
+ScanInit( char * line )
+{
+ /* skip the leading white space - the yylex routine is sensitive
+ * to keywords in the first position on the command line.
+ */
+
+ while (isspace(*line)) ++line ;
+ ScanLine = line ;
+ ScanPtr = line ;
+}
+
+/* yylex - the scanner for iid. Basically a kludge ad-hoc piece of junk,
+ * but what the heck, if it works...
+ *
+ * Mostly just scans for non white space strings and returns ID for them.
+ * Does check especially for '(' and ')'. Just before returning ID it
+ * checks for command names if it is the first token on line or
+ * AND, OR, LID, AID if it is in the middle of a line.
+ */
+int
+yylex( void )
+{
+ char * bp ;
+ char c ;
+ int code = ID ;
+ char * dp ;
+ char * sp ;
+ int val ;
+
+ bp = ScanPtr ;
+ while (isspace(*bp)) ++bp ;
+ sp = bp ;
+ c = *sp++ ;
+ if ((c == '(') || (c == ')') || (c == '\0')) {
+ ScanPtr = sp ;
+ if (c == '\0') {
+ --ScanPtr ;
+ }
+ return(c) ;
+ } else {
+ dp = yytext ;
+ while (! ((c == '(') || (c == ')') || (c == '\0') || isspace(c))) {
+ *dp++ = c ;
+ c = *sp++ ;
+ }
+ *dp++ = '\0' ;
+ ScanPtr = sp - 1 ;
+ if (bp == ScanLine) {
+
+ /* first token on line, check for command names */
+
+ if (strcasecmp(yytext, "SS")) return(SS) ;
+ if (strcasecmp(yytext, "FILES")) return(FILES) ;
+ if (strcasecmp(yytext, "F")) return(FILES) ;
+ if (strcasecmp(yytext, "HELP")) return(HELP) ;
+ if (strcasecmp(yytext, "H")) return(HELP) ;
+ if (strcasecmp(yytext, "?")) return(HELP) ;
+ if (strcasecmp(yytext, "BEGIN")) return(BEGIN) ;
+ if (strcasecmp(yytext, "B")) return(BEGIN) ;
+ if (strcasecmp(yytext, "SETS")) return(SETS) ;
+ if (strcasecmp(yytext, "SHOW")) return(SHOW) ;
+ if (strcasecmp(yytext, "P")) return(SHOW) ;
+ if (strcasecmp(yytext, "OFF")) return(OFF) ;
+ if (strcasecmp(yytext, "Q")) return(OFF) ;
+ if (strcasecmp(yytext, "QUIT")) return(OFF) ;
+ if (yytext[0] == '!') {
+ code = SHELL_COMMAND ;
+ } else {
+ code = SHELL_QUERY ;
+ }
+ } else {
+
+ /* not first token, check for operator names */
+
+ if (strcasecmp(yytext, "LID")) return(LID) ;
+ if (strcasecmp(yytext, "AID")) return(AID) ;
+ if (strcasecmp(yytext, "AND")) return(AND) ;
+ if (strcasecmp(yytext, "OR")) return(OR) ;
+ if (strcasecmp(yytext, "NOT")) return(NOT) ;
+ if (strcasecmp(yytext, "MATCH")) return(MATCH) ;
+ if ((yytext[0] == 's' || yytext[0] == 'S') && isdigit(yytext[1])) {
+
+ /* this might be a set specification */
+
+ sp = &yytext[1] ;
+ val = 0 ;
+ for ( ; ; ) {
+ c = *sp++ ;
+ if (c == '\0') {
+ if (val < NextSetNum) {
+ yylval.setdef = TheSets[val] ;
+ return(SET) ;
+ }
+ }
+ if (isdigit(c)) {
+ val = (val * 10) + (c - '0') ;
+ } else {
+ break ;
+ }
+ }
+ }
+ }
+ yylval.strdef = (id_type *)malloc(sizeof(id_type) + strlen(yytext)) ;
+ if (yylval.strdef == NULL) {
+ fatal("Out of memory in yylex") ;
+ }
+ yylval.strdef->next_id = NULL ;
+ if (code == SHELL_COMMAND) {
+ strcpy(yylval.strdef->id, &yytext[1]) ;
+ } else {
+ strcpy(yylval.strdef->id, yytext) ;
+ }
+ return(code) ;
+ }
+}
+
+/* The main program for iid - parse the command line, initialize processing,
+ * loop processing one command at a time.
+ */
+int
+main( int argc , char * argv [ ] )
+{
+ int c ; /* current option */
+ char * CmdPtr ; /* Points to the command string */
+ char Command [ MAXCMD ] ; /* Buffer for reading commands */
+ int Do1 = 0 ; /* 1 if should only do 1 command */
+ int DoPrompt ; /* 1 if should write a prompt */
+ int errors = 0 ; /* error count */
+
+ DoPrompt = isatty(fileno(stdin)) ;
+ while ((c = getopt(argc, argv, "Hac:")) != EOF) {
+ switch(c) {
+ case 'a':
+ DefaultCommand = "aid -kmn" ;
+ break ;
+ case 'c':
+ CmdPtr = optarg ;
+ Do1 = 1 ;
+ break ;
+ case 'H':
+ fputs("\
+iid: interactive ID database query tool. Call with:\n\
+ iid [-a] [-c] [-H]\n\
+\n\
+-a\tUse the aid as the default query command (not lid).\n\
+-c cmd\tExecute the single query cmd and exit.\n\
+-H\tPrint this message and exit.\n\
+\n\
+To get help after starting program type 'help'.\n\
+",stderr) ;
+ exit(0) ;
+ default:
+ ++errors ;
+ break ;
+ }
+ }
+ if (argc != optind) {
+ fputs("iid: Excess arguments ignored.\n",stderr) ;
+ ++errors ;
+ }
+ if (errors) {
+ fputs("run iid -H for help.\n",stderr) ;
+ exit(1) ;
+ }
+
+ /* initialize global data */
+
+ InitIid() ;
+
+ /* run the parser */
+
+ if (Do1) {
+ ScanInit(CmdPtr) ;
+ exit(yyparse()) ;
+ } else {
+ for ( ; ; ) {
+ if (DoPrompt) {
+ fputs(Prompt, stdout) ;
+ fflush(stdout) ;
+ }
+ gets(Command) ;
+ if (feof(stdin)) {
+ if (DoPrompt) fputs("\n", stdout) ;
+ strcpy(Command, "off") ;
+ }
+ ScanInit(Command) ;
+ errors += yyparse() ;
+ }
+ }
+}
+
+
+/* ArgListSize - count the size of an arg list so can alloca() enough
+ * space for the command.
+ */
+int
+ArgListSize( id_list_type * idlp )
+{
+ id_type * idep ;
+ int size = 0;
+
+ idep = idlp->id_list ;
+ while (idep != NULL) {
+ size += 1 + strlen(idep->id);
+ idep = idep->next_id;
+ }
+ return size;
+}
+
+/* SetListSize - count the size of a string build up from a set so we can
+ * alloca() enough space for args.
+ */
+int
+SetListSize( set_type * sp )
+{
+ int i ;
+ int size = 0 ;
+
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ size += 1 + strlen(FileList[i]->name);
+ }
+ }
+ }
+ return size;
+}
+
+/* FlushFiles - clear out the TheFiles array for the start of a new
+ * query.
+ */
+void
+FlushFiles( void )
+{
+ int i ;
+
+ if (TheFiles != NULL) {
+ for (i = 0; i <= MaxCurFile; ++i) {
+ TheFiles[i] = 0 ;
+ }
+ }
+ MaxCurFile = 0 ;
+}
+
+/* fatal - sometimes the only thing to do is die...
+ */
+void
+fatal( char const * s )
+{
+ fprintf(stderr,"Fatal error: %s\n", s) ;
+ exit(1) ;
+}
+
+/* CountBits - count the number of bits in a bit set. Actually fairly
+ * tricky since it needs to deal with sets having infinite tails
+ * as a result of a NOT operation.
+ */
+int
+CountBits( set_type * sp )
+{
+ unsigned long bit_mask ;
+ int count = 0 ;
+ int i ;
+
+ i = 0;
+ for ( ; ; ) {
+ for (bit_mask = high_bit; bit_mask != 0; bit_mask >>= 1) {
+ if (bit_mask == NextMaskBit && i == NextMaskWord) {
+ return(count) ;
+ }
+ if (i < sp->set_size) {
+ if (sp->set_data[i] & bit_mask) {
+ ++count ;
+ }
+ } else {
+ if (sp->set_tail == 0) return count;
+ if (sp->set_tail & bit_mask) {
+ ++count;
+ }
+ }
+ }
+ ++i;
+ }
+}
+
+/* OneDescription - Print a description of a set. This includes
+ * the set number, the number of files in the set, and the
+ * set description string.
+ */
+void
+OneDescription( set_type * sp )
+{
+ int elt_count ;
+ char setnum[20] ;
+
+ sprintf(setnum,"S%d",sp->set_num) ;
+ elt_count = CountBits(sp) ;
+ printf("%5s %6d %s\n",setnum,elt_count,sp->set_desc) ;
+}
+
+/* DescribeSets - Print description of all the sets.
+ */
+void
+DescribeSets( void )
+{
+ int i ;
+
+ if (NextSetNum > 0) {
+ for (i = 0; i < NextSetNum; ++i) {
+ OneDescription(TheSets[i]) ;
+ }
+ } else {
+ printf("No sets defined yet.\n") ;
+ }
+}
+
+/* SetList - Go through the bit set and add the file names in
+ * it to an identifier list.
+ */
+id_list_type *
+SetList( id_list_type * idlp , set_type * sp )
+{
+ int i ;
+ id_type * idep ;
+
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ idep = (id_type *)malloc(sizeof(id_type) +
+ strlen(FileList[i]->name)) ;
+ if (idep == NULL) {
+ fatal("Out of memory in SetList") ;
+ }
+ idep->next_id = NULL ;
+ strcpy(idep->id, FileList[i]->name) ;
+ idlp = ExtendList(idlp, idep) ;
+ }
+ }
+ }
+ return(idlp) ;
+}
+
+/* PrintSet - Go through the bit set and print the file names
+ * corresponding to all the set bits.
+ */
+void
+PrintSet( set_type * sp )
+{
+ int i ;
+
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ printf("%s\n",FileList[i]->name) ;
+ }
+ }
+ }
+}
+
+/* Free up all space used by current set of sets and reset all
+ * set numbers.
+ */
+void
+FlushSets( void )
+{
+ int i ;
+
+ for (i = 0; i < NextSetNum; ++i) {
+ free(TheSets[i]->set_desc) ;
+ free(TheSets[i]) ;
+ }
+ NextSetNum = 0 ;
+}
+
+/* InitList - create an empty identifier list.
+ */
+id_list_type *
+InitList( void )
+{
+ id_list_type * idlp ;
+
+ idlp = (id_list_type *)malloc(sizeof(id_list_type)) ;
+ if (idlp == NULL) {
+ fatal("Out of memory in InitList") ;
+ }
+ idlp->id_count = 0 ;
+ idlp->end_ptr_ptr = & (idlp->id_list) ;
+ idlp->id_list = NULL ;
+ return(idlp) ;
+}
+
+/* ExtendList - add one identifier to an ID list.
+ */
+id_list_type *
+ExtendList( id_list_type * idlp , id_type * idp )
+{
+ *(idlp->end_ptr_ptr) = idp ;
+ idlp->end_ptr_ptr = &(idp->next_id) ;
+ return(idlp) ;
+}
+
+/* InitIid - do all initial processing for iid.
+ * 1) Determine the size of a unsigned long for bit set stuff.
+ * 2) Find out the name of the pager program to use.
+ * 3) Create the HelpSet (pointing to the help file).
+ * 4) Setup the prompt.
+ */
+void
+InitIid( void )
+{
+ unsigned long bit_mask = 1 ; /* find number of bits in long */
+ int i ;
+ char const * page ; /* pager program */
+
+ do {
+ high_bit = bit_mask ;
+ bit_mask <<= 1 ;
+ } while (bit_mask != 0) ;
+
+ NextMaskBit = high_bit ;
+
+ page = getenv("PAGER") ;
+ if (page == NULL) {
+ page = PAGER ;
+ }
+ strcpy(Pager, page) ;
+
+ FlushFiles() ;
+ InstallFile(IID_HELP_FILE) ;
+ HelpSet = (set_type *)
+ malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ;
+ if (HelpSet == NULL) {
+ fatal("No memory for set in InitIid") ;
+ }
+ HelpSet->set_tail = 0 ;
+ HelpSet->set_desc = NULL ;
+ HelpSet->set_size = MaxCurFile + 1 ;
+ for (i = 0; i <= MaxCurFile; ++i) {
+ HelpSet->set_data[i] = TheFiles[i] ;
+ }
+
+ page = getenv("PS1") ;
+ if (page == NULL) {
+ page = PROMPT ;
+ }
+ strcpy(Prompt, page) ;
+}
+
+/* InstallFile - install a file name in the symtab. Return the
+ * symbol table pointer of the file.
+ */
+symtab_type *
+InstallFile( char const * fp )
+{
+ char c ;
+ unsigned long hash_code ;
+ int i ;
+ char const * sp ;
+ symtab_type * symp ;
+
+ hash_code = 0 ;
+ sp = fp ;
+ while ((c = *sp++) != '\0') {
+ hash_code <<= 1 ;
+ hash_code ^= (unsigned long)(c) ;
+ if (hash_code & high_bit) {
+ hash_code &= ~ high_bit ;
+ hash_code ^= 1 ;
+ }
+ }
+ hash_code %= HASH_SIZE ;
+ symp = HashTable[hash_code] ;
+ while (symp != NULL && strcmp(symp->name, fp)) {
+ symp = symp->hash_link ;
+ }
+ if (symp == NULL) {
+ symp = (symtab_type *)malloc(sizeof(symtab_type) + strlen(fp)) ;
+ if (symp == NULL) {
+ fatal("No memory for symbol table entry in InstallFile") ;
+ }
+ strcpy(symp->name, fp) ;
+ symp->hash_link = HashTable[hash_code] ;
+ HashTable[hash_code] = symp ;
+ if (NextMaskWord >= FileSpace) {
+ FileSpace += 1000 ;
+ if (TheFiles != NULL) {
+ TheFiles = (unsigned long *)
+ realloc(TheFiles, sizeof(unsigned long) * FileSpace) ;
+ } else {
+ TheFiles = (unsigned long *)
+ malloc(sizeof(unsigned long) * FileSpace) ;
+ }
+ if (TheFiles == NULL) {
+ fatal("No memory for TheFiles in InstallFile") ;
+ }
+ for (i = NextMaskWord; i < FileSpace; ++i) {
+ TheFiles[i] = 0 ;
+ }
+ }
+ symp->mask_word = NextMaskWord ;
+ symp->mask_bit = NextMaskBit ;
+ NextMaskBit >>= 1 ;
+ if (NextMaskBit == 0) {
+ NextMaskBit = high_bit ;
+ ++NextMaskWord ;
+ }
+ if (NextFileNum >= ListSpace) {
+ ListSpace += 1000 ;
+ if (FileList == NULL) {
+ FileList = (symtab_type **)
+ malloc(sizeof(symtab_type *) * ListSpace) ;
+ } else {
+ FileList = (symtab_type **)
+ realloc(FileList, ListSpace * sizeof(symtab_type *)) ;
+ }
+ if (FileList == NULL) {
+ fatal("No memory for FileList in InstallFile") ;
+ }
+ }
+ FileList[NextFileNum++] = symp ;
+ /* put code here to sort the file list by name someday */
+ }
+ TheFiles[symp->mask_word] |= symp->mask_bit ;
+ if (symp->mask_word > MaxCurFile) {
+ MaxCurFile = symp->mask_word ;
+ }
+ return(symp) ;
+}
+
+/* RunPager - run the users pager program on the list of files
+ * in the set.
+ */
+void
+RunPager( char * pp , set_type * sp )
+{
+ char * cmd ;
+ int i ;
+
+ cmd = (char *)TEMP_ALLOC(SetListSize(sp) + strlen(pp) + 2);
+ strcpy(cmd, pp) ;
+ for (i = 0; i < NextFileNum; ++i) {
+ if (FileList[i]->mask_word < sp->set_size) {
+ if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
+ strcat(cmd, " ") ;
+ strcat(cmd, FileList[i]->name) ;
+ }
+ }
+ }
+ system(cmd) ;
+ TEMP_FREE(cmd) ;
+}
+
+/* AddSet - add a new set to the universal list of sets. Assign
+ * it the next set number.
+ */
+void
+AddSet( set_type * sp )
+{
+ if (NextSetNum >= SetSpace) {
+ SetSpace += 1000 ;
+ if (TheSets != NULL) {
+ TheSets = (set_type **)
+ realloc(TheSets, sizeof(set_type *) * SetSpace) ;
+ } else {
+ TheSets = (set_type **)
+ malloc(sizeof(set_type *) * SetSpace) ;
+ }
+ if (TheSets == NULL) {
+ fatal("No memory for TheSets in AddSet") ;
+ }
+ }
+ sp->set_num = NextSetNum ;
+ TheSets[NextSetNum++] = sp ;
+}
+
+/* RunProg - run a program with arguments from id_list and
+ * accept list of file names back from the program which
+ * are installed in the symbol table and used to construct
+ * a new set.
+ */
+set_type *
+RunProg( char const * pp , id_list_type * idlp )
+{
+ int c ;
+ char * cmd ;
+ char * dp ;
+ char file [ MAXCMD ] ;
+ int i ;
+ id_type * idep ;
+ id_type * next_id ;
+ FILE * prog ;
+ set_type * sp ;
+
+ cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2);
+ FlushFiles() ;
+ strcpy(cmd, pp) ;
+ idep = idlp->id_list ;
+ while (idep != NULL) {
+ strcat(cmd, " ") ;
+ strcat(cmd, idep->id) ;
+ next_id = idep->next_id ;
+ free(idep) ;
+ idep = next_id ;
+ }
+ free(idlp) ;
+
+ /* run program with popen, reading the output. Assume each
+ * white space terminated string is a file name.
+ */
+ prog = popen(cmd, "r") ;
+ dp = file ;
+ while ((c = getc(prog)) != EOF) {
+ if (isspace(c)) {
+ if (dp != file) {
+ *dp++ = '\0' ;
+ InstallFile(file) ;
+ dp = file ;
+ }
+ } else {
+ *dp++ = c ;
+ }
+ }
+ if (dp != file) {
+ *dp++ = '\0' ;
+ InstallFile(file) ;
+ }
+ if (pclose(prog) != 0) {
+ /* if there was an error make an empty set, who knows what
+ * garbage the program printed.
+ */
+ FlushFiles() ;
+ }
+
+ sp = (set_type *)
+ malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ;
+ if (sp == NULL) {
+ fatal("No memory for set in RunProg") ;
+ }
+ sp->set_tail = 0 ;
+ sp->set_desc = (char *)malloc(strlen(cmd) + 1) ;
+ if (sp->set_desc == NULL) {
+ fatal("No memory for set description in RunProg") ;
+ }
+ strcpy(sp->set_desc, cmd) ;
+ sp->set_size = MaxCurFile + 1 ;
+ for (i = 0; i <= MaxCurFile; ++i) {
+ sp->set_data[i] = TheFiles[i] ;
+ }
+ AddSet(sp) ;
+ TEMP_FREE(cmd);
+ return(sp) ;
+}
+
+/* SetDirectory - change the working directory. This will
+ * determine which ID file is found by the subprograms.
+ */
+void
+SetDirectory( id_type * dir )
+{
+ if (chdir(dir->id) != 0) {
+ fprintf(stderr,"Directory %s not accessible.\n", dir->id) ;
+ }
+ free(dir) ;
+}
+
+/* SetIntersect - construct a new set from the intersection
+ * of two others. Also construct a new description string.
+ */
+set_type *
+SetIntersect( set_type * sp1 , set_type * sp2 )
+{
+ char * desc ;
+ int i ;
+ int len1 ;
+ int len2 ;
+ set_type * new_set ;
+ int new_size ;
+
+ if (sp1->set_tail || sp2->set_tail) {
+ new_size = MAX(sp1->set_size, sp2->set_size) ;
+ } else {
+ new_size = MIN(sp1->set_size, sp2->set_size) ;
+ }
+ new_set = (set_type *)malloc(sizeof(set_type) +
+ (new_size - 1) * sizeof(unsigned long)) ;
+ if (new_set == NULL) {
+ fatal("No memory for set in SetIntersect") ;
+ }
+ len1 = strlen(sp1->set_desc) ;
+ len2 = strlen(sp2->set_desc) ;
+ desc = (char *)malloc(len1 + len2 + 10) ;
+ if (desc == NULL) {
+ fatal("No memory for set description in SetIntersect") ;
+ }
+ new_set->set_desc = desc ;
+ strcpy(desc,"(") ;
+ ++desc ;
+ strcpy(desc, sp1->set_desc) ;
+ desc += len1 ;
+ strcpy(desc, ") AND (") ;
+ desc += 7 ;
+ strcpy(desc, sp2->set_desc) ;
+ desc += len2 ;
+ strcpy(desc, ")") ;
+ AddSet(new_set) ;
+ new_set->set_size = new_size ;
+ for (i = 0; i < new_size; ++i) {
+ new_set->set_data[i] =
+ ((i < sp1->set_size) ? sp1->set_data[i] : sp1->set_tail) &
+ ((i < sp2->set_size) ? sp2->set_data[i] : sp2->set_tail) ;
+ }
+ new_set->set_tail = sp1->set_tail & sp2->set_tail ;
+ return(new_set) ;
+}
+
+/* SetUnion - construct a new set from the union of two others.
+ * Also construct a new description string.
+ */
+set_type *
+SetUnion( set_type * sp1 , set_type * sp2 )
+{
+ char * desc ;
+ int i ;
+ int len1 ;
+ int len2 ;
+ set_type * new_set ;
+ int new_size ;
+
+ new_size = MAX(sp1->set_size, sp2->set_size) ;
+ new_set = (set_type *)malloc(sizeof(set_type) +
+ (new_size - 1) * sizeof(unsigned long)) ;
+ if (new_set == NULL) {
+ fatal("No memory for set in SetUnion") ;
+ }
+ len1 = strlen(sp1->set_desc) ;
+ len2 = strlen(sp2->set_desc) ;
+ desc = (char *)malloc(len1 + len2 + 9) ;
+ if (desc == NULL) {
+ fatal("No memory for set description in SetUnion") ;
+ }
+ new_set->set_desc = desc ;
+ strcpy(desc,"(") ;
+ ++desc ;
+ strcpy(desc, sp1->set_desc) ;
+ desc += len1 ;
+ strcpy(desc, ") OR (") ;
+ desc += 6 ;
+ strcpy(desc, sp2->set_desc) ;
+ desc += len2 ;
+ strcpy(desc, ")") ;
+ AddSet(new_set) ;
+ new_set->set_size = new_size ;
+ for (i = 0; i < new_size; ++i) {
+ new_set->set_data[i] =
+ ((i < sp1->set_size) ? (sp1->set_data[i]) : sp1->set_tail) |
+ ((i < sp2->set_size) ? (sp2->set_data[i]) : sp2->set_tail) ;
+ }
+ new_set->set_tail = sp1->set_tail | sp2->set_tail ;
+ return(new_set) ;
+}
+
+/* SetInverse - construct a new set from the inverse of another.
+ * Also construct a new description string.
+ *
+ * This is kind of tricky. An inverse set in iid may grow during
+ * the course of a session. By NOTing the set_tail extension the
+ * inverse at any given time will be defined as the inverse against
+ * a universe that grows as additional queries are made and new files
+ * are added to the database.
+ *
+ * Several alternative definitions were possible (snapshot the
+ * universe at the time of the NOT, go read the ID file to
+ * determine the complete universe), but this one was the one
+ * I picked.
+ */
+set_type *
+SetInverse( set_type * sp )
+{
+ char * desc ;
+ int i ;
+ set_type * new_set ;
+
+ new_set = (set_type *)malloc(sizeof(set_type) +
+ (sp->set_size - 1) * sizeof(unsigned long)) ;
+ if (new_set == NULL) {
+ fatal("No memory for set in SetInverse") ;
+ }
+ desc = (char *)malloc(strlen(sp->set_desc) + 5) ;
+ if (desc == NULL) {
+ fatal("No memory for set description in SetInverse") ;
+ }
+ new_set->set_desc = desc ;
+ strcpy(desc,"NOT ") ;
+ desc += 4 ;
+ strcpy(desc, sp->set_desc) ;
+ AddSet(new_set) ;
+ new_set->set_size = sp->set_size ;
+ for (i = 0; i < sp->set_size; ++i) {
+ new_set->set_data[i] = ~ sp->set_data[i] ;
+ }
+ new_set->set_tail = ~ sp->set_tail ;
+ return(new_set) ;
+}
+
+/* RunShell - run a program with arguments from id_list.
+ */
+void
+RunShell( char * pp , id_list_type * idlp )
+{
+ char * cmd ;
+ id_type * idep ;
+ id_type * next_id ;
+
+ cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2);
+ strcpy(cmd, pp) ;
+ idep = idlp->id_list ;
+ while (idep != NULL) {
+ strcat(cmd, " ") ;
+ strcat(cmd, idep->id) ;
+ next_id = idep->next_id ;
+ free(idep) ;
+ idep = next_id ;
+ }
+ free(idlp) ;
+ system(cmd) ;
+ TEMP_FREE(cmd);
+}
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..ab74c88
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,238 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/lid.1 b/lid.1
new file mode 100644
index 0000000..ce98212
--- /dev/null
+++ b/lid.1
@@ -0,0 +1,211 @@
+.TH LID 1
+.SH NAME
+lid, gid, eid, aid, pid \- query id database
+.SH SYNOPSIS
+.B lid
+.RB [ \-f \^file]
+.RB [ \-u \^n]
+.RB [ \-r \^dir]
+.RB [ \-edoxamseknc]
+patterns...
+.PP
+.B gid
+.RB [ \-f \^file]
+.RB [ \-r \^dir]
+.RB [ \-edoxamsec]
+patterns...
+.PP
+.B eid
+.RB [ \-f \^file]
+.RB [ \-r \^dir]
+.RB [ \-doxamsec]
+patterns...
+.PP
+.B aid
+.RB [ \-f \^file]
+.RB [ \-r \^dir]
+.RB [ \-doxamsc]
+patterns...
+.PP
+.B pid
+.RB [ \-f \^file]
+.RB [ \-r \^dir]
+.RB [ \-ekncb]
+patterns...
+.SH DESCRIPTION
+These commands provide a flexible query interface to the
+.I id
+database.
+.I Lid\^
+does a lookup on
+.IR patters
+and prints out lines in this way:
+.PP
+.nf
+idname ../hdir/hfile.h ../cdir/{cfile1,cfile2}.c
+.fi
+.PP
+Notice that multiple files with the same directory prefix
+and suffix are concatenated in the globbing-set-notation of
+.IR csh (1).
+Also notice that all of the
+.I id
+database query commands adjust the list of pathnames to be relative
+to your current working directory, provided that
+.IR mkid (1)
+was used to build the database, and your working directory
+is located within the sub-tree covered by the
+.I id
+database.
+.PP
+If multiple names match on pattern, then there will be one line
+of output per name. The mnemonic significance of the name is
+\fI\|l(ookup) id\fP.
+.PP
+.I Gid
+does a lookup and then searches for the names it matches in the
+files where they occur. The mnemonic for this name is
+\fI\|g(rep)id\fP.
+.PP
+.I Eid
+does a lookup, and then invokes an editor on all files with
+the matched name as an initial search string. Of course, this
+name stands for
+\fI\|e(dit) id\fP.
+.PP
+.I Eid
+uses four environment variables to control its invocation of the
+editor.
+Naturally,
+.B EDITOR
+is used to locate the editing program.
+.B EIDARG
+is a
+.IR printf (3S)
+string used to specify the form of the initial-search-string
+argument. If the editor does not support such an argument,
+this variable may be left unset.
+.B EIDLDEL
+and
+.B EIDRDEL
+specify the form of the left and right word-delimiters respectively.
+The best way to explain the use of these last three variables is
+with an example. Here are the proper settings for vi(1):
+.nf
+EIDARG='+/%s/' # initial search argument template
+EIDLDEL='\\<' # left word-delimiter
+EIDRDEL='\\>' # right word-delimiter
+.fi
+.PP
+.I Patterns
+may be simple alpha-numeric strings, or regular expressions in the
+style of
+.IR regcmp (3).
+If the string contains no regular-expression meta-characters, it is
+searched for as a
+.IR word .
+If the string contains meta-characters, or if the \-e argument is
+supplied, it is searched for as regular-expression.
+.PP
+.I Aid\^
+produces output in the style of
+.I lid\^
+but its pattern arguments are searched for as substrings within
+the identifiers in the database. No regular-expression search
+is performed, even if the pattern contains meta-characters.
+The search is conducted in an alphabetic case insensitive manner.
+The mnemonic for this name is
+\fI\|a(propos) id\fP.
+.PP
+.I Pid\^
+is used to match the input patterns against the names of the files
+in the database rather than the contents of the files. The pattern
+is assumed to be a simple shell wild card pattern unless the
+.B \-e
+option is given, in which case full regular expression matching
+is used.
+The
+.B \-b
+option can be used to restrict the match to just the basename portion
+of the full absolute path name of the file.
+The mnemonic for this name is
+\fI\|p(ath) id\fP.
+.PP
+The following options are recognized:
+.TP 10
+.BR \-f file\^
+Use
+.I file\^
+as the database instead of the default
+.BR ID .
+.TP 10
+.BR \-u n
+Lists all identifiers in the database that are non-unique within the first
+.I n
+characters. This facility is particularly helpful when porting a program
+to a system whose compiler or linker has fewer significant characters
+for identifiers.
+.TP 10
+.BR \-r dir\^
+Assume the names stored in the database are relative to this directory.
+This option is useful if you create the database in one place, then move
+it somewhere else. Normally all the query tools assume the names in
+the database are relative to the location of the database.
+.TP 10
+.B \-c
+This option is similar to
+.BR \-r ,
+but it tells the id query tool to assume the names in the ID database
+are stored relative to the current working directory.
+.TP 10
+.B \-k
+Suppresses the use of \fL{\fP and \fL}\fP as a shorthand in the
+generated list of file names. Each name is output in full.
+.TP 10
+.B \-n
+Suppresses printing the name of the search string, only the names of
+the files containing the string are printed. Together with the \fB\-k\fP
+option this can be used to generate lists of files to pass to other
+programs.
+.PP
+.TP 10
+.B \-b
+In the
+.I pid
+program, the
+.B \-b
+option is used to force pattern matching on just the base names of the
+file, otherwise the pattern matching is done on the full absolute file
+name.
+.PP
+The remaining options are for use in conjunction with numeric patterns:
+.TP 10
+.B \-doxa
+These options may be specified in any combination.
+They limit numeric matches to specific radixes.
+The
+.BR \-d ,
+.BR \-o ,
+and
+.B \-x
+options limit matches to decimal, octal, and hexadecimal respectively.
+The
+.BR \-a
+option is a shorthand for specifying all three radixes.
+.PP
+Searches for numbers
+are conducted numerically rather than lexically, so that all
+representations for a given number are potentially available
+from a single search.
+.TP 10
+.B \-m
+Merge multiple lines of output into a single line.
+.TP 10
+.B \-s
+Limit the results of the search to identifiers that occur only
+once in the entire set of sources covered by the database.
+This option is useful for finding identifiers that are defined
+but never used.
+.SH SEE ALSO
+mkid(1),
+fid(1).
diff --git a/lid.c b/lid.c
new file mode 100644
index 0000000..1e35bea
--- /dev/null
+++ b/lid.c
@@ -0,0 +1,1390 @@
+/* lid.c -- primary query interface for mkid database
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <rx.h>
+#include <assert.h>
+#include <limits.h>
+#include "alloc.h"
+#include "idfile.h"
+#include "idarg.h"
+#include "token.h"
+#include "bitops.h"
+#include "strxtra.h"
+#include "misc.h"
+#include "filenames.h"
+
+typedef void (*doit_t) (char const *name, char **argv);
+
+unsigned char *tree8_to_bits (unsigned char *bits_vec, unsigned char const *hits_tree8);
+void tree8_to_bits_1 (unsigned char **bits_vec, unsigned char const **hits_tree8, int level);
+char **tree8_to_argv (unsigned char const *hits_tree8);
+char **bits_to_argv (unsigned char const *bits_vec);
+
+static void usage (void);
+void look_id (char const *name, char **argv);
+void grep_id (char const *name, char **argv);
+void edit_id (char const *name, char **argv);
+int skip_to_argv (char **argv);
+int find_plain (char const *arg, doit_t doit);
+int find_anchor (char const *arg, doit_t doit);
+int find_regexp (char const *arg, doit_t doit);
+int find_number (char const *arg, doit_t doit);
+int find_non_unique (int, doit_t doit);
+int find_apropos (char const *arg, doit_t doit);
+void parse_frequency_arg (char const *arg);
+int frequency_wanted (char const *tok);
+char const *strcpos (char const *s1, char const *s2);
+char const *file_regexp (char const *name0, char const *left_delimit, char const *right_delimit);
+off_t find_token (char const *token);
+int is_regexp (char *name);
+char **vec_to_argv (int const *vec);
+int file_name_wildcard (char const *re, char const *fn);
+int match_file_names (char const *re, doit_t doit);
+int word_match (char const *name0, char const *line);
+int radix (char const *name);
+int stoi (char const *name);
+int otoi (char const *name);
+int dtoi (char const *name);
+int xtoi (char const *name);
+void savetty (void);
+void restoretty (void);
+void linetty (void);
+void chartty (void);
+
+enum radix {
+ RADIX_OCT = 1,
+ RADIX_DEC = 2,
+ RADIX_HEX = 4,
+ RADIX_ALL = RADIX_DEC | RADIX_OCT | RADIX_HEX,
+};
+
+#define TOLOWER(c) (isupper (c) ? tolower (c) : (c))
+#define IS_ALNUM(c) (isalnum (c) || (c) == '_')
+
+#ifndef CRUNCH_DEFAULT
+#define CRUNCH_DEFAULT 1
+#endif
+
+/* Sorry about all the globals, but it's really cleaner this way. */
+FILE *id_FILE;
+int merging;
+int radix_arg;
+int echo_on = 1;
+int crunch_on = CRUNCH_DEFAULT;
+int file_name_regexp = 0;
+int match_base = 0;
+char id_dir[BUFSIZ];
+off_t anchor_offset;
+int tree8_levels;
+int bits_vec_size;
+char PWD_name[BUFSIZ];
+struct idhead idh;
+struct idarg *id_args;
+int (*find_func) (char const *arg, doit_t doit);
+unsigned short frequency_low = 1;
+unsigned short frequency_high = USHRT_MAX;
+char *buf;
+char *buf2;
+unsigned char *bits_vec;
+
+char const *program_name;
+
+static void
+usage (void)
+{
+ fprintf (stderr, "Usage: %s [-f<file>] [-u<n>] [-r<dir>] [-mewdoxasknc] patterns...\n", program_name);
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ char const *id_file_name = IDFILE;
+ doit_t doit = look_id;
+ int force_merge = 0;
+ int unique_limit = 0;
+ int use_id_file_name = 1;
+ int use_pwd_file_name = 0;
+ int use_relative_file_name = 0;
+ char const *REL_file_name = NULL;
+ int (*forced_find_func) (char const *arg, doit_t doit) = NULL;
+
+ program_name = basename (GETARG (argc, argv));
+
+ while (argc)
+ {
+ char const *arg = GETARG (argc, argv);
+ int op = *arg++;
+ switch (op)
+ {
+ case '-':
+ case '+':
+ break;
+ default:
+ UNGETARG (argc, argv);
+ goto argsdone;
+ }
+ while (*arg)
+ switch (*arg++)
+ {
+ case 'f':
+ id_file_name = arg;
+ goto nextarg;
+ case 'u':
+ unique_limit = stoi (arg);
+ goto nextarg;
+ case 'm':
+ force_merge = 1;
+ break;
+ case 'e':
+ forced_find_func = find_regexp;
+ file_name_regexp = 1;
+ break;
+ case 'w':
+ forced_find_func = find_plain;
+ break;
+ case 'd':
+ radix_arg |= RADIX_DEC;
+ break;
+ case 'o':
+ radix_arg |= RADIX_OCT;
+ break;
+ case 'x':
+ radix_arg |= RADIX_HEX;
+ break;
+ case 'a':
+ radix_arg |= RADIX_ALL;
+ break;
+ case 'F':
+ parse_frequency_arg (arg);
+ goto nextarg;
+ case 'k':
+ crunch_on = 0;
+ break;
+ case 'g':
+ crunch_on = 1;
+ break;
+ case 'n':
+ echo_on = 0;
+ break;
+ case 'c':
+ use_id_file_name = 0;
+ use_pwd_file_name = 1;
+ break;
+ case 'b':
+ match_base = 1;
+ break;
+ case 'r':
+ use_id_file_name = 0;
+ use_relative_file_name = 1;
+ REL_file_name = arg;
+ goto nextarg;
+ default:
+ usage ();
+ }
+ nextarg:;
+ }
+argsdone:
+
+ if (use_pwd_file_name && use_relative_file_name)
+ {
+ fprintf (stderr, "%s: please use only one of -c or -r\n", program_name);
+ usage ();
+ }
+ /* Look for the ID database up the tree */
+ id_file_name = look_up (id_file_name);
+ if (id_file_name == NULL)
+ {
+ filerr ("open", id_file_name);
+ exit (1);
+ }
+ /* Find out current directory to relate names to */
+ if (kshgetwd (PWD_name) == NULL)
+ {
+ fprintf (stderr, "%s: cannot determine current directory\n", program_name);
+ exit (1);
+ }
+ strcat (PWD_name, "/");
+ /* Determine absolute path name that database files are relative to */
+ if (use_id_file_name)
+ {
+ strcpy (id_dir, span_file_name (PWD_name, id_file_name));
+ *(strrchr (id_dir, '/') + 1) = '\0';
+ }
+ else if (use_pwd_file_name)
+ {
+ strcpy (id_dir, PWD_name);
+ }
+ else
+ {
+ strcpy (id_dir, span_file_name (PWD_name, REL_file_name));
+ strcat (id_dir, "/");
+ }
+ id_FILE = init_idfile (id_file_name, &idh, &id_args);
+ if (id_FILE == NULL)
+ {
+ filerr ("open", id_file_name);
+ exit (1);
+ }
+ bits_vec_size = (idh.idh_paths + 7) >> 3;
+ tree8_levels = tree8_count_levels (idh.idh_paths);
+
+ switch (program_name[0])
+ {
+ case 'a':
+ forced_find_func = find_apropos;
+ /*FALLTHROUGH*/
+ case 'l':
+ doit = look_id;
+ break;
+ case 'g':
+ doit = grep_id;
+ break;
+ case 'e':
+ doit = edit_id;
+ break;
+ case 'p':
+ forced_find_func = match_file_names;
+ doit = look_id;
+ break;
+ default:
+ program_name = "[algep]id";
+ usage ();
+ }
+
+ if (argc == 0)
+ {
+ UNGETARG (argc, argv);
+ *(char const **)argv = ".";
+ }
+
+ while (argc)
+ {
+ long val = -1;
+ char *arg = GETARG (argc, argv);
+
+ if (forced_find_func)
+ find_func = forced_find_func;
+ else if (radix (arg) && (val = stoi (arg)) >= 0)
+ find_func = find_number;
+ else if (is_regexp (arg))
+ find_func = find_regexp;
+ else if (arg[0] == '^')
+ find_func = find_anchor;
+ else
+ find_func = find_plain;
+
+ if ((doit == look_id && !force_merge)
+ || (find_func == find_number
+ && val > 7
+ && radix_arg != RADIX_DEC
+ && radix_arg != RADIX_OCT
+ && radix_arg != RADIX_HEX))
+ merging = 0;
+ else
+ merging = 1;
+
+ buf = malloc (idh.idh_buf_size);
+ buf2 = malloc (idh.idh_buf_size);
+ bits_vec = MALLOC (unsigned char, bits_vec_size);
+
+ if (unique_limit)
+ {
+ if (!find_non_unique (unique_limit, doit))
+ fprintf (stderr, "All identifiers are unique within the first %d characters\n", unique_limit);
+ exit (0);
+ }
+ else if (!(*find_func) (arg, doit))
+ {
+ fprintf (stderr, "%s: not found\n", arg);
+ continue;
+ }
+ }
+ exit (0);
+}
+
+void
+look_id (char const *name, char **argv)
+{
+ char const *arg;
+ char const *dir;
+ int crunching = 0;
+
+ if (echo_on)
+ printf ("%-14s ", name);
+ while (*argv)
+ {
+ arg = *argv++;
+ if (*argv && crunch_on && can_crunch (arg, *argv))
+ {
+ if (crunching)
+ printf (",%s", root_name (arg));
+ else
+ {
+ dir = dirname (arg);
+ if (dir && !strequ (dir, "."))
+ printf ("%s/", dir);
+ printf ("{%s", root_name (arg));
+ }
+ crunching = 1;
+ }
+ else
+ {
+ if (crunching)
+ printf (",%s}%s", root_name (arg), suff_name (arg));
+ else
+ fputs (arg, stdout);
+ crunching = 0;
+ if (*argv)
+ putchar (' ');
+ }
+ }
+ putchar ('\n');
+}
+
+void
+grep_id (char const *name, char **argv)
+{
+ FILE *gid_FILE;
+ char const *gid_name;
+ char line[BUFSIZ];
+ char const *re = NULL;
+ int line_number;
+
+ if (merging)
+ {
+ re = file_regexp (name, "[^a-zA-Z0-9_]_*", "[^a-zA-Z0-9_]");
+ if (re)
+ {
+ char const *regexp_error = re_comp (re);
+ if (regexp_error)
+ {
+ fprintf (stderr, "%s: Syntax Error: %s (%s)\n", program_name, re, regexp_error);
+ return;
+ }
+ }
+ }
+
+ line[0] = ' '; /* sentry */
+ while (*argv)
+ {
+ gid_FILE = fopen (gid_name = *argv++, "r");
+ if (gid_FILE == NULL)
+ {
+ filerr ("open", gid_name);
+ continue;
+ }
+ line_number = 0;
+ while (fgets (&line[1], sizeof (line), gid_FILE))
+ {
+ line_number++;
+ if (re)
+ {
+ if (!re_exec (line))
+ continue;
+ }
+ else if (!word_match (name, line))
+ continue;
+ printf ("%s:%d: %s", gid_name, line_number, &line[1]);
+ }
+ fclose (gid_FILE);
+ }
+}
+
+void
+edit_id (char const *name, char **argv)
+{
+ char re_buffer[BUFSIZ];
+ char ed_arg_buffer[BUFSIZ];
+ char const *re;
+ int c;
+ int skip;
+ static char const *editor;
+ static char const *eid_arg;
+ static char const *eid_right_del;
+ static char const *eid_left_del;
+
+ if (editor == NULL)
+ editor = getenv ("EDITOR");
+ if (editor == NULL)
+ {
+ char const *ucb_vi = "/usr/ucb/vi";
+ char const *bin_vi = "/usr/bin/vi";
+
+ if (access (ucb_vi, 01) == 0)
+ editor = ucb_vi;
+ else if (access (bin_vi, 01) == 0)
+ editor = bin_vi;
+ else
+ editor = "/bin/ed"; /* YUCK! */
+ if (editor == ucb_vi || editor == bin_vi)
+ {
+ eid_arg = "+1;/%s/";
+ eid_left_del = "\\<";
+ eid_right_del = "\\>";
+ }
+ }
+ if (eid_left_del == NULL)
+ {
+ eid_arg = getenv ("EIDARG");
+ eid_left_del = getenv ("EIDLDEL");
+ if (eid_left_del == NULL)
+ eid_left_del = "";
+ eid_right_del = getenv ("EIDRDEL");
+ if (eid_right_del == NULL)
+ eid_right_del = "";
+ }
+
+ look_id (name, argv);
+ savetty ();
+ for (;;)
+ {
+ printf ("Edit? [y1-9^S/nq] ");
+ fflush (stdout);
+ chartty ();
+ c = (getchar () & 0177);
+ restoretty ();
+ switch (TOLOWER (c))
+ {
+ case '/':
+ case ('s' & 037):
+ putchar ('/');
+ skip = skip_to_argv (argv);
+ if (skip < 0)
+ continue;
+ argv += skip;
+ goto editit;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ putchar (c);
+ skip = c - '0';
+ break;
+ case 'y':
+ putchar (c);
+ /*FALLTHROUGH*/
+ case '\n':
+ case '\r':
+ skip = 0;
+ break;
+ case 'q':
+ putchar (c);
+ putchar ('\n');
+ exit (0);
+ case 'n':
+ putchar (c);
+ putchar ('\n');
+ return;
+ default:
+ putchar (c);
+ putchar ('\n');
+ continue;
+ }
+
+ putchar ('\n');
+ while (skip--)
+ if (*++argv == NULL)
+ continue;
+ break;
+ }
+editit:
+
+ if (merging)
+ re = file_regexp (name, eid_left_del, eid_right_del);
+ else
+ re = NULL;
+ if (re == NULL)
+ {
+ re = re_buffer;
+ sprintf (re_buffer, "%s%s%s", eid_left_del, name, eid_right_del);
+ }
+
+ switch (fork ())
+ {
+ case -1:
+ fprintf (stderr, "%s: Cannot fork (%s)\n", program_name, uerror ());
+ exit (1);
+ case 0:
+ argv--;
+ if (eid_arg)
+ {
+ argv--;
+ sprintf (ed_arg_buffer, eid_arg, re);
+ argv[1] = ed_arg_buffer;
+ }
+ *(char const **) argv = editor;
+ execv (editor, argv);
+ filerr ("exec", editor);
+ default:
+ {
+ void (*oldint) (int) = signal (SIGINT, SIG_IGN);
+ void (*oldquit) (int) = signal (SIGQUIT, SIG_IGN);
+
+ while (wait (0) == -1 && errno == EINTR)
+ ;
+
+ signal (SIGINT, oldint);
+ signal (SIGQUIT, oldquit);
+ }
+ break;
+ }
+}
+
+int
+skip_to_argv (char **argv)
+{
+ char pattern[BUFSIZ];
+ int count;
+
+ if (gets (pattern) == NULL)
+ return -1;
+
+ for (count = 0; *argv; count++, argv++)
+ if (strcpos (*argv, pattern))
+ return count;
+ return -1;
+}
+
+int
+find_plain (char const *arg, doit_t doit)
+{
+ if (find_token (arg) == 0)
+ return 0;
+ gets_past_00 (buf, id_FILE);
+ assert (*buf);
+ if (!frequency_wanted (buf))
+ return 0;
+ (*doit) (buf, tree8_to_argv (tok_hits_addr (buf)));
+ return 1;
+}
+
+int
+find_anchor (char const *arg, doit_t doit)
+{
+ int count;
+ int length;
+
+ if (find_token (++arg) == 0)
+ return 0;
+
+ length = strlen (arg);
+ count = 0;
+ if (merging)
+ memset (bits_vec, 0, bits_vec_size);
+ while (gets_past_00 (buf, id_FILE) > 0)
+ {
+ assert (*buf);
+ if (!frequency_wanted (buf))
+ continue;
+ if (!strnequ (arg, buf, length))
+ break;
+ if (merging)
+ tree8_to_bits (bits_vec, tok_hits_addr (buf));
+ else
+ (*doit) (buf, tree8_to_argv (tok_hits_addr (buf)));
+ count++;
+ }
+ if (merging && count)
+ (*doit) (--arg, bits_to_argv (bits_vec));
+
+ return count;
+}
+
+int
+find_regexp (char const *re, doit_t doit)
+{
+ int count;
+ char const *regexp_error;
+
+ regexp_error = re_comp (re);
+ if (regexp_error)
+ {
+ fprintf (stderr, "%s: Syntax Error: %s (%s)\n", program_name, re, regexp_error);
+ return 0;
+ }
+ fseek (id_FILE, idh.idh_tokens_offset, SEEK_SET);
+
+ count = 0;
+ if (merging)
+ memset (bits_vec, 0, bits_vec_size);
+ while (gets_past_00 (buf, id_FILE) > 0)
+ {
+ assert (*buf);
+ if (!frequency_wanted (buf))
+ continue;
+ if (!re_exec (buf))
+ continue;
+ if (merging)
+ tree8_to_bits (bits_vec, tok_hits_addr (buf));
+ else
+ (*doit) (buf, tree8_to_argv (tok_hits_addr (buf)));
+ count++;
+ }
+ if (merging && count)
+ (*doit) (re, bits_to_argv (bits_vec));
+
+ return count;
+}
+
+int
+find_number (char const *arg, doit_t doit)
+{
+ int count;
+ int rdx;
+ int val;
+ int hit_digits = 0;
+
+ rdx = (val = stoi (arg)) ? RADIX_ALL : radix (arg);
+ fseek (id_FILE, idh.idh_tokens_offset, SEEK_SET);
+
+ count = 0;
+ if (merging)
+ memset (bits_vec, 0, bits_vec_size);
+ while (gets_past_00 (buf, id_FILE) > 0)
+ {
+ if (hit_digits)
+ {
+ if (!isdigit (*buf))
+ break;
+ }
+ else
+ {
+ if (isdigit (*buf))
+ hit_digits = 1;
+ }
+
+ if (!((radix_arg ? radix_arg : rdx) & radix (buf))
+ || stoi (buf) != val)
+ continue;
+ if (merging)
+ tree8_to_bits (bits_vec, tok_hits_addr (buf));
+ else
+ (*doit) (buf, tree8_to_argv (tok_hits_addr (buf)));
+ count++;
+ }
+ if (merging && count)
+ (*doit) (arg, bits_to_argv (bits_vec));
+
+ return count;
+}
+
+/* Find identifiers that are non-unique within the first `count'
+ characters. */
+int
+find_non_unique (int limit, doit_t doit)
+{
+ char *old = buf;
+ char *new = buf2;
+ int consecutive = 0;
+ int count = 0;
+ char name[1024];
+
+ if (limit <= 1)
+ usage ();
+ assert (limit < sizeof(name));
+
+ name[0] = '^';
+ *new = '\0';
+ fseek (id_FILE, idh.idh_tokens_offset, SEEK_SET);
+ while (gets_past_00 (old, id_FILE) > 0)
+ {
+ char *tmp;
+ if (!(tok_flags (old) & TOK_NAME))
+ continue;
+ tmp = old;
+ old = new;
+ new = tmp;
+ if (!strnequ (new, old, limit))
+ {
+ if (consecutive && merging)
+ {
+ strncpy (&name[1], old, limit);
+ (*doit) (name, bits_to_argv (bits_vec));
+ }
+ consecutive = 0;
+ continue;
+ }
+ if (!consecutive++)
+ {
+ if (merging)
+ tree8_to_bits (bits_vec, tok_hits_addr (old));
+ else
+ (*doit) (old, tree8_to_argv (tok_hits_addr (old)));
+ count++;
+ }
+ if (merging)
+ tree8_to_bits (bits_vec, tok_hits_addr (new));
+ else
+ (*doit) (new, tree8_to_argv (tok_hits_addr (new)));
+ count++;
+ }
+ if (consecutive && merging)
+ {
+ strncpy (&name[1], new, limit);
+ (*doit) (name, bits_to_argv (bits_vec));
+ }
+ return count;
+}
+
+int
+find_apropos (char const *arg, doit_t doit)
+{
+ int count;
+
+ fseek (id_FILE, idh.idh_tokens_offset, SEEK_SET);
+
+ count = 0;
+ if (merging)
+ memset (bits_vec, 0, bits_vec_size);
+ while (gets_past_00 (buf, id_FILE) > 0)
+ {
+ assert (*buf);
+ if (!frequency_wanted (buf))
+ continue;
+ if (strcpos (buf, arg) == NULL)
+ continue;
+ if (merging)
+ tree8_to_bits (bits_vec, tok_hits_addr (buf));
+ else
+ (*doit) (buf, tree8_to_argv (tok_hits_addr (buf)));
+ count++;
+ }
+ if (merging && count)
+ (*doit) (arg, bits_to_argv (bits_vec));
+
+ return count;
+}
+
+void
+parse_frequency_arg (char const *arg)
+{
+ if (*arg == '-')
+ frequency_low = 1;
+ else
+ {
+ frequency_low = atoi (arg);
+ while (isdigit (*arg))
+ arg++;
+ if (*arg == '-')
+ arg++;
+ }
+ if (*arg)
+ frequency_high = atoi (arg);
+ else if (arg[-1] == '-')
+ frequency_high = USHRT_MAX;
+ else
+ frequency_high = frequency_low;
+ if (frequency_low > frequency_high)
+ fprintf (stderr, "Bogus frequencies: %u > %u\n", frequency_low, frequency_high);
+}
+
+int
+frequency_wanted (char const *tok)
+{
+ unsigned int count = tok_count (tok);
+ return (frequency_low <= count && count <= frequency_high);
+}
+
+/* if string `s2' occurs in `s1', return a pointer to the first match.
+ Ignore differences in alphabetic case. */
+char const *
+strcpos (char const *s1, char const *s2)
+{
+ char const *s1p;
+ char const *s2p;
+ char const *s1last;
+
+ for (s1last = &s1[strlen (s1) - strlen (s2)]; s1 <= s1last; s1++)
+ for (s1p = s1, s2p = s2; TOLOWER (*s1p) == TOLOWER (*s2p); s1p++)
+ if (*++s2p == '\0')
+ return s1;
+ return NULL;
+}
+
+/* Convert the regular expression that we used to locate identifiers
+ in the id database into one suitable for locating the identifiers
+ in files. */
+char const *
+file_regexp (char const *name0, char const *left_delimit, char const *right_delimit)
+{
+ static char re_buffer[BUFSIZ];
+ char *name = (char *) name0;
+
+ if (find_func == find_number && merging)
+ {
+ sprintf (re_buffer, "%s0*[Xx]*0*%d[Ll]*%s", left_delimit, stoi (name), right_delimit);
+ return re_buffer;
+ }
+
+ if (!is_regexp (name) && name[0] != '^')
+ return NULL;
+
+ if (name[0] == '^')
+ name0++;
+ else
+ left_delimit = "";
+ while (*++name)
+ ;
+ if (*--name == '$')
+ *name = '\0';
+ else
+ right_delimit = "";
+
+ sprintf (re_buffer, "%s%s%s", left_delimit, name0, right_delimit);
+ return re_buffer;
+}
+
+off_t
+find_token (char const *token_0)
+{
+ off_t offset = 0;
+ off_t start = idh.idh_tokens_offset - 2;
+ off_t end = idh.idh_end_offset;
+ off_t anchor_offset = 0;
+ int order = -1;
+
+ while (start < end)
+ {
+ int c;
+ int incr = 1;
+ char const *token;
+
+ offset = start + (end - start) / 2;
+ fseek (id_FILE, offset, SEEK_SET);
+ offset += skip_past_00 (id_FILE);
+ if (offset >= end)
+ {
+ offset = start + 2;
+ fseek (id_FILE, offset, SEEK_SET);
+ }
+
+ /* compare the token names */
+ token = token_0;
+ while (*token == (c = getc (id_FILE)) && *token && c)
+ {
+ token++;
+ incr++;
+ }
+ if (c && !*token && find_func == find_anchor)
+ anchor_offset = offset;
+ order = *token - c;
+
+ if (order < 0)
+ end = offset - 2;
+ else if (order > 0)
+ start = offset + incr + skip_past_00 (id_FILE) - 2;
+ else
+ break;
+ }
+
+ if (order)
+ {
+ if (anchor_offset)
+ offset = anchor_offset;
+ else
+ return 0;
+ }
+ fseek (id_FILE, offset, SEEK_SET);
+ return offset;
+}
+
+/* Are there any regexp meta-characters in name?? */
+int
+is_regexp (char *name)
+{
+ int backslash = 0;
+
+ if (*name == '^')
+ name++;
+ while (*name)
+ {
+ if (*name == '\\')
+ {
+ if (strchr ("<>", name[1]))
+ return 1;
+ name++, backslash++;
+ }
+ else if (strchr ("[]{}().*+^$", *name))
+ return 1;
+ name++;
+ }
+ if (backslash)
+ while (*name)
+ {
+ if (*name == '\\')
+ strcpy (name, name + 1);
+ name++;
+ }
+ return 0;
+}
+
+/* file_name_wildcard implements a simple pattern matcher that
+ emulates the shell wild card capability.
+
+ * - any string of chars
+ ? - any char
+ [] - any char in set (if first char is !, any not in set)
+ \ - literal match next char */
+int
+file_name_wildcard (char const *re, char const *fn)
+{
+ int c;
+ int i;
+ char set[256];
+ int revset;
+
+ while ((c = *re++) != '\0')
+ {
+ if (c == '*')
+ {
+ if (*re == '\0')
+ return 1; /* match anything at end */
+ while (*fn != '\0')
+ {
+ if (file_name_wildcard (re, fn))
+ return 1;
+ ++fn;
+ }
+ return 0;
+ }
+ else if (c == '?')
+ {
+ if (*fn++ == '\0')
+ return 0;
+ }
+ else if (c == '[')
+ {
+ c = *re++;
+ memset (set, 0, 256);
+ if (c == '!')
+ {
+ revset = 1;
+ c = *re++;
+ }
+ else
+ revset = 0;
+ while (c != ']')
+ {
+ if (c == '\\')
+ c = *re++;
+ set[c] = 1;
+ if ((*re == '-') && (*(re + 1) != ']'))
+ {
+ re += 1;
+ while (++c <= *re)
+ set[c] = 1;
+ ++re;
+ }
+ c = *re++;
+ }
+ if (revset)
+ for (i = 1; i < 256; ++i)
+ set[i] = !set[i];
+ if (!set[(int)*fn++])
+ return 0;
+ }
+ else
+ {
+ if (c == '\\')
+ c = *re++;
+ if (c != *fn++)
+ return 0;
+ }
+ }
+ return (*fn == '\0');
+}
+
+/* match_file_names implements the pid tool. This matches the *names*
+ of files in the database against the input pattern rather than the
+ *contents* of the files. */
+
+int
+match_file_names (char const *re, doit_t doit)
+{
+ char const *absname;
+ struct idarg *ida = id_args;
+ int i;
+ int count = 0;
+ int matched;
+
+ if (file_name_regexp)
+ {
+ char const *regexp_error = re_comp (re);
+ if (regexp_error)
+ {
+ fprintf (stderr, "%s: Syntax Error: %s (%s)\n", program_name, re, regexp_error);
+ return 0;
+ }
+ }
+
+ for (i = 0; i < idh.idh_paths; i++, ida++)
+ {
+ if (*ida->ida_arg == 0)
+ continue;
+ if (match_base)
+ {
+ absname = strrchr (ida->ida_arg, '/');
+ if (absname == NULL)
+ absname = ida->ida_arg;
+ }
+ else
+ absname = span_file_name (id_dir, ida->ida_arg);
+ if (file_name_regexp)
+ matched = re_exec (absname);
+ else
+ matched = file_name_wildcard (re, absname);
+ if (matched)
+ {
+ BITSET (bits_vec, i);
+ ++count;
+ }
+ }
+ if (count)
+ (*doit) (re, bits_to_argv (bits_vec));
+ return count;
+}
+
+/* Does `name' occur in `line' delimited by non-alphanumerics?? */
+int
+word_match (char const *name0, char const *line)
+{
+ char const *name = name0;
+
+ for (;;)
+ {
+ /* find an initial-character match */
+ while (*line != *name)
+ {
+ if (*line == '\n')
+ return 0;
+ line++;
+ }
+ /* do we have a word delimiter on the left ?? */
+ if (isalnum (line[-1]))
+ {
+ line++;
+ continue;
+ }
+ /* march down both strings as long as we match */
+ while (*++name == *++line)
+ ;
+ /* is this the end of `name', is there a word delimiter ?? */
+ if (*name == '\0' && !IS_ALNUM (*line))
+ return 1;
+ name = name0;
+ }
+}
+
+/* Use the C lexical rules to determine an ascii number's radix. The
+ radix is returned as a bit map, so that more than one radix may
+ apply. In particular, it is impossible to determine the radix of
+ 0, so return all possibilities. */
+int
+radix (char const *name)
+{
+ if (!isdigit (*name))
+ return 0;
+ if (*name != '0')
+ return RADIX_DEC;
+ name++;
+ if (*name == 'x' || *name == 'X')
+ return RADIX_HEX;
+ while (*name && *name == '0')
+ name++;
+ return (RADIX_OCT | ((*name) ? 0 : RADIX_DEC));
+}
+
+/* Convert an ascii string number to an integer. Determine the radix
+ before converting. */
+int
+stoi (char const *name)
+{
+ switch (radix (name))
+ {
+ case RADIX_DEC:
+ return (dtoi (name));
+ case RADIX_OCT:
+ return (otoi (&name[1]));
+ case RADIX_HEX:
+ return (xtoi (&name[2]));
+ case RADIX_DEC | RADIX_OCT:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/* Convert an ascii octal number to an integer. */
+int
+otoi (char const *name)
+{
+ int n = 0;
+
+ while (*name >= '0' && *name <= '7')
+ {
+ n *= 010;
+ n += *name++ - '0';
+ }
+ if (*name == 'l' || *name == 'L')
+ name++;
+ return (*name ? -1 : n);
+}
+
+/* Convert an ascii decimal number to an integer. */
+int
+dtoi (char const *name)
+{
+ int n = 0;
+
+ while (isdigit (*name))
+ {
+ n *= 10;
+ n += *name++ - '0';
+ }
+ if (*name == 'l' || *name == 'L')
+ name++;
+ return (*name ? -1 : n);
+}
+
+/* Convert an ascii hex number to an integer. */
+int
+xtoi (char const *name)
+{
+ int n = 0;
+
+ while (isxdigit (*name))
+ {
+ n *= 0x10;
+ if (isdigit (*name))
+ n += *name++ - '0';
+ else if (islower (*name))
+ n += 0xa + *name++ - 'a';
+ else
+ n += 0xA + *name++ - 'A';
+ }
+ if (*name == 'l' || *name == 'L')
+ name++;
+ return (*name ? -1 : n);
+}
+
+unsigned char *
+tree8_to_bits (unsigned char *bits_vec, unsigned char const *hits_tree8)
+{
+ unsigned char* bv = bits_vec;
+ tree8_to_bits_1 (&bv, &hits_tree8, tree8_levels);
+ return bits_vec;
+}
+
+void
+tree8_to_bits_1 (unsigned char **bits_vec, unsigned char const **hits_tree8, int level)
+{
+ int hits = *(*hits_tree8)++;
+
+ if (--level)
+ {
+ int incr = 1 << ((level - 1) * 3);
+ int bit;
+ for (bit = 1; bit & 0xff; bit <<= 1)
+ {
+ if (bit & hits)
+ tree8_to_bits_1 (bits_vec, hits_tree8, level);
+ else
+ *bits_vec += incr;
+ }
+ }
+ else
+ *(*bits_vec)++ |= hits;
+}
+
+char **
+bits_to_argv (unsigned char const *bits_vec)
+{
+ int const reserved_argv_slots = 3;
+ static char **argv_0;
+ char **argv;
+ struct idarg *ida = id_args;
+ struct idarg *end = &id_args[idh.idh_paths];
+
+ if (argv_0 == NULL)
+ argv_0 = MALLOC (char *, idh.idh_paths + reserved_argv_slots + 2);
+ argv = &argv_0[reserved_argv_slots];
+
+ for (;;)
+ {
+ int hits;
+ int bit;
+
+ while (*bits_vec == 0)
+ {
+ bits_vec++;
+ ida += 8;
+ if (ida >= end)
+ goto out;
+ }
+ hits = *bits_vec++;
+ for (bit = 1; bit & 0xff; bit <<= 1)
+ {
+ if (bit & hits)
+ {
+ if (!(ida->ida_flags & IDA_RELATIVE))
+ {
+ char const *abs_name = span_file_name (id_dir, ida->ida_arg);
+ char const *rel_name = relative_file_name (PWD_name, abs_name);
+ char const *short_name = (strlen (rel_name) > strlen (abs_name)
+ ? abs_name : rel_name);
+ if (!strequ (short_name, ida->ida_arg))
+ ida->ida_arg = strdup (short_name);
+ ida->ida_flags |= IDA_RELATIVE;
+ }
+ *argv++ = ida->ida_arg;
+ }
+ if (++ida >= end)
+ goto out;
+ }
+ }
+out:
+ *argv = NULL;
+ return &argv_0[reserved_argv_slots];
+}
+
+char **
+tree8_to_argv (unsigned char const *hits_tree8)
+{
+ memset (bits_vec, 0, bits_vec_size);
+ return bits_to_argv (tree8_to_bits (bits_vec, hits_tree8));
+}
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_TERMIOS_H || HAVE_TERMIO_H
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#if HAVE_TERMIO_H
+#include <termio.h>
+#endif
+
+struct termio linemode;
+struct termio charmode;
+struct termio savemode;
+
+void
+savetty (void)
+{
+ ioctl (0, TCGETA, &savemode);
+ charmode = linemode = savemode;
+
+ charmode.c_lflag &= ~(ECHO | ICANON | ISIG);
+ charmode.c_cc[VMIN] = 1;
+ charmode.c_cc[VTIME] = 0;
+
+ linemode.c_lflag |= (ECHO | ICANON | ISIG);
+ linemode.c_cc[VEOF] = 'd' & 037;
+ linemode.c_cc[VEOL] = 0377;
+}
+
+void
+restoretty (void)
+{
+ ioctl (0, TCSETA, &savemode);
+}
+
+void
+linetty (void)
+{
+ ioctl (0, TCSETA, &linemode);
+}
+
+void
+chartty (void)
+{
+ ioctl (0, TCSETA, &charmode);
+}
+
+#else /* not HAVE_TERMIOS_H || HAVE_TERMIO_H */
+
+#if HAVE_SGTTYB_H
+#include <sgttyb.h>
+
+struct sgttyb linemode;
+struct sgttyb charmode;
+struct sgttyb savemode;
+
+savetty()
+{
+#ifdef TIOCGETP
+ ioctl(0, TIOCGETP, &savemode);
+#else
+ gtty(0, &savemode);
+#endif
+ charmode = linemode = savemode;
+
+ charmode.sg_flags &= ~ECHO;
+ charmode.sg_flags |= RAW;
+
+ linemode.sg_flags |= ECHO;
+ linemode.sg_flags &= ~RAW;
+}
+
+restoretty()
+{
+#ifdef TIOCSETP
+ ioctl(0, TIOCSETP, &savemode);
+#else
+ stty(0, &savemode);
+#endif
+}
+
+linetty()
+{
+#ifdef TIOCSETP
+ ioctl(0, TIOCSETP, &linemode);
+#else
+ stty(0, &linemode);
+#endif
+}
+
+chartty()
+{
+#ifdef TIOCSETP
+ ioctl(0, TIOCSETP, &charmode);
+#else
+ stty(0, &charmode);
+#endif
+}
+
+#endif /* HAVE_SGTTYB_H */
+#endif /* not HAVE_TERMIOS_H || HAVE_TERMIO_H */
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..ad9efbf
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,134 @@
+/* misc.c -- miscellaneous common functions
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "strxtra.h"
+#include "misc.h"
+
+char const *
+basename (char const *path)
+{
+ char *base;
+
+ base = strrchr (path, '/');
+ if (base)
+ return ++base;
+ else
+ return path;
+}
+
+char const *
+dirname (char const *path)
+{
+ char *base;
+
+ base = strrchr (path, '/');
+ if (base)
+ return strndup (path, base - path);
+ else
+ return ".";
+}
+
+/* This is like fgets(3s), except that lines are delimited by NULs
+ rather than newlines. Also, we return the number of characters
+ gotten rather than the address of buf0. */
+int
+fgets0 (char *buf0, int size, FILE * in_FILE)
+{
+ char *buf;
+ int c;
+ char *end;
+
+ buf = buf0;
+ end = &buf[size];
+ while ((c = getc (in_FILE)) > 0 && buf < end)
+ *buf++ = c;
+ *buf = '\0';
+ return (buf - buf0);
+}
+
+extern char const *program_name;
+
+char const *
+uerror (void)
+{
+ static char errbuf[10];
+
+ if (errno == 0 || errno >= sys_nerr)
+ {
+ sprintf (errbuf, "error %d", errno);
+ return errbuf;
+ }
+ return sys_errlist[errno];
+}
+
+void
+filerr (char const *syscall, char const *file_name)
+{
+ fprintf (stderr, "%s: Cannot %s `%s' (%s)\n", program_name, syscall, file_name, uerror ());
+}
+
+int
+tree8_count_levels (int cardinality)
+{
+ int levels = 1;
+ cardinality--;
+ while (cardinality >>= 3)
+ ++levels;
+ return levels;
+}
+
+int
+gets_past_00 (char *tok, FILE *input_FILE)
+{
+ int got = 0;
+ int c;
+ do
+ {
+ do
+ {
+ got++;
+ c = getc (input_FILE);
+ *tok++ = c;
+ }
+ while (c > 0);
+ got++;
+ c = getc (input_FILE);
+ *tok++ = c;
+ }
+ while (c > 0);
+ return got - 2;
+}
+
+int
+skip_past_00 (FILE *input_FILE)
+{
+ int skipped = 0;
+ do
+ {
+ do
+ skipped++;
+ while (getc (input_FILE) > 0);
+ skipped++;
+ }
+ while (getc (input_FILE) > 0);
+ return skipped;
+}
diff --git a/misc.h b/misc.h
new file mode 100644
index 0000000..18185a9
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,31 @@
+/* misc.c -- defs for interface to misc.c
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _misc_h_
+#define _misc_h_
+
+char const *basename (char const *path);
+char const *dirname (char const *path);
+int fgets0 (char *buf0, int size, FILE *in_FILE);
+char const *uerror (void);
+void filerr (char const *syscall, char const *file_name);
+int tree8_count_levels (int cardinality);
+int gets_past_00 (char *tok, FILE *input_FILE);
+int skip_past_00 (FILE *input_FILE);
+
+#endif /* not _misc_h_ */
diff --git a/mkid.1 b/mkid.1
new file mode 100644
index 0000000..6cdf1a7
--- /dev/null
+++ b/mkid.1
@@ -0,0 +1,187 @@
+.TH MKID 1
+.SH NAME
+mkid \- make an id database
+.SH SYNOPSIS
+.B mkid
+.RB [ \-v ]
+.RB [ \-f \^out-file]
+.RB [ \-s \^directory]
+.RB [ \-r \^directory]
+.RB [ \-S \^scanarg]
+.RB [ \-a \^arg-file]
+.RB [ \- ]
+.RB [ \-u ]
+.RB [ files... ]
+.SH DESCRIPTION
+.I Mkid\^
+builds a database that stores numbers and identifier names, as well
+as the names of the files in which they occur.
+.I Mkid\^
+is particularly useful with large programs spread out across multiple
+source files. It serves as an aid for program maintenance and as a
+.I guide\^
+for perusing a program.
+.PP
+The following options are recognized:
+.TP 10
+.B \-v
+Verbose.
+Report
+.IR mkid 's
+progress in building the database. The output comes on standard error.
+.TP 10
+.BI \-f out-file\^
+Write the finished database into
+.IR out-file .
+.B ID\^
+is the default.
+Normally the names of the files scanned are written to the database
+as specified in the argument list. If the database sepcified with
+.B \-f
+is not located in the current directory, then the file names are
+adjusted so that they are relative to the directory that the
+database is located in.
+.TP 10
+.BI \-s directory\^
+.TP 10
+.BI \-r directory\^
+If
+.IR mkid 's
+attempt to open a source-file fails, it will try to checkout the
+corresponding SCCS or RCS file if present. The
+.B \-s
+option tells
+.I mkid\^
+which directory holds the SCCS file.
+Similarly, the
+.B \-r
+option tells
+.I mkid\^
+which directory holds the RCS file.
+If neither the RCS or SCCS directories are specified,
+.I mkid\^
+will first look for an SCCS file in the current directory, then in
+.BI sccs ,
+and finally in
+.BI SCCS .
+It will then look for an RCS file in the current directory, and finally in
+.BI RCS .
+.TP 10
+.BI \-a arg-file\^
+Open and read
+.I arg-file\^
+in order to obtain a list of source file arguments. Source file names
+must appear one to a line.
+.BI \-S ,
+.BI \-r ,
+and
+.BI \-s
+arguments may also be placed one per line in
+.IR file .
+They are distinguished from source file names by their leading `-'. If a file name begins
+with `-', it can be distinguished from an argument by explicitly prepending the current
+directory string: `./'.
+.TP 10
+.B \-
+This operates in the same manner as the
+.B \-a
+option described above, but reads from the standard input instead of a file.
+.TP 10
+.B \-u
+Update an existing database. Only those files that have been modified
+since the database was built will be rescanned. This is a significant
+time-saver for updating large databases where few sources have changed.
+.TP 10
+.B files...
+If neither the
+.BI \-a ,
+.BI \- ,
+nor
+.BI \-u ,
+arguments have been specified, take file names from the command line.
+.TP 10
+.BI \-S scanarg\^
+.I Mkid\^
+scans source files in order to obtain numbers and identifier names.
+Since the lexical rules of languages differ,
+.I mkid\^
+applies a different scanning function to each language in order
+to conform to that language's lexical rules.
+.I Mkid\^
+determines the source file's language by examining its filename
+suffix which commonly occurs after a dot (`.').
+The
+.B \-S
+argument is a way of passing language specific arguments to the
+scanner for that language. This argument takes a number of forms:
+.br
+-S<suffix>=<language>
+.br
+-S<language>-<arg>
+.br
++S-<arg>
+.br
+-S<lang>/<lang>/<filter>
+.br
+The first form associates a suffix with a language.
+For example -S.c=vhil would cause all .c files to be scanned
+as though they were language vhil rather than c.
+You may find
+out which suffixes are defined for which languages with the following
+options: `-S<suffix>=?' tells which language is bound to
+.IR <suffix> ,
+`-S?=<language>' tells which suffixes are bound to
+.IR <language> ,
+and `-S?=?' reports all bindings between suffixes and languages.
+.PP
+The second form passes an argument for processing by the scanner
+for a specific language. The third form passes an argument to
+all scanners.
+.PP
+Finally, the <lang>/<lang>/<filter> form defines a shell command
+to filter the file with. This can be used to run an arbitrary
+program to filter the contents of a file before it is passed
+to one of the existing language scanners. It is typically
+used in conjunction with the plain text scanner.
+The first <lang> defines a new language, the second <lang>
+specifies an existing language whose scanner will be used,
+and the remaining <filter> is an arbitrary shell command.
+.PP
+You may get a brief summary of the scanner-specific options for a
+language by supplying the following option: `-S<language>?'.
+.PP
+Here is a brief summary of the options for the
+.I `asm'\^
+(assembler) language.
+.PP
+The
+.B \-u\^
+option controls whether or not the assembler scanner should strip
+off a leading
+.I underscore\^
+(`_') character. If your assembler prepends an
+.I underscore\^
+to external symbols, then you should tell the scanner to strip it
+off, so that references to the same symbol from assembly and from
+a high-level language will look the same.
+.PP
+The
+.B \-c<cc>\^
+option supplies the character(s) used to begin a comment that extends
+to the end of the line.
+.PP
+The
+.B \-a<cc>\^
+option indicates character(s) that are legal in names, in addition to
+the alpha-numeric characters. If the option appears as `-a', names
+that contain these characters are ignored. If it appears as `+a', these
+names are added to the database.
+.SH BUGS
+This manual page needs to be more complete about the scanner-specific
+arguments.
+.PP
+At the moment, the only scanners implemented are for C, assembly
+language, and plain text. There ought to be scanners for Ada, Pascal,
+Fortran, and Lisp.
+.SH SEE ALSO
+lid(1), deroff(1), detex(1).
diff --git a/mkid.c b/mkid.c
new file mode 100644
index 0000000..39da994
--- /dev/null
+++ b/mkid.c
@@ -0,0 +1,1091 @@
+/* mkid.c -- build an identifer database
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdio.h>
+#define fileno(FP) ((FP)->_fileno)
+#include <string.h>
+#include "strxtra.h"
+#include <ctype.h>
+#include <errno.h>
+#include "alloc.h"
+#include "idfile.h"
+#include "idarg.h"
+#include "token.h"
+#include "bitops.h"
+#include "misc.h"
+#include "filenames.h"
+#include "scanners.h"
+
+struct summary
+{
+ struct token **sum_tokens;
+ unsigned char const *sum_hits;
+ struct summary *sum_parent;
+ struct summary *sum_kids[8];
+ long sum_tokens_size;
+ long sum_hits_count;
+ int sum_free_index;
+ int sum_level;
+};
+
+#define MAX_LEVELS 5 /* log_8 of the max # of files log_8 32768 == 5 */
+
+struct token
+{
+ unsigned short tok_count;
+ unsigned char tok_flags;
+ unsigned char tok_hits[MAX_LEVELS];
+ char tok_name[1];
+};
+
+struct token **hash_table; /* Vector of token pointers */
+
+char *bitsset (char *s1, char const *s2, int n);
+char *bitsclr (char *s1, char const *s2, int n);
+char *bitsand (char *s1, char const *s2, int n);
+char *bitsxor (char *s1, char const *s2, int n);
+int bitstst (char const *s1, char const *s2, int n);
+int bitsany (char const *s, int n);
+int round2(int rough);
+struct token *make_token (char const *name, int);
+void scan_1_file (char const *(*get_token) (FILE*, int*), FILE *source_FILE);
+struct idarg *parse_idargs (int argc, char **argv);
+struct idarg *parse_idargs_from_FILE (FILE *arg_FILE, struct idarg *idarg);
+void init_hash_table (int file_name_count);
+void scan_files (struct idarg *idarg);
+void report_statistics (void);
+
+void init_hash (long size);
+struct token **hash_lookup (char const *key);
+unsigned int string_hash_1 (char const *key);
+unsigned int string_hash_2 (char const *key);
+void rehash (void);
+
+void write_idfile (char const *id_file, struct idarg *idargs);
+void bump_current_hits_signature (void);
+void init_hits_signature (int index);
+int bit_to_index (int bit);
+int compare_tokens (void const *x, void const *y);
+void free_summary_tokens (void);
+void summarize (void);
+void assert_hits (struct summary *summary);
+void write_hits (FILE *fp, struct summary *summary, unsigned char const *tail_hits);
+void sign_token (struct token *token);
+void add_token_to_summary (struct summary *summary, struct token *token);
+void init_summary (void);
+struct summary *make_sibling_summary (struct summary *summary);
+int count_vec_size (struct summary *summary, unsigned char const *tail_hits);
+int count_buf_size (struct summary *summary, unsigned char const *tail_hits);
+void usage (void);
+
+/* Miscellaneous statistics */
+long name_tokens;
+long number_tokens;
+long string_tokens;
+long literal_tokens;
+long comment_tokens;
+long occurrences;
+long heap_size;
+long hits_length = 0;
+long tokens_length = 0;
+long output_length = 0;
+
+/* Hash table maintenance */
+long hash_size; /* # of slots */
+long hash_capacity; /* # of usable slots */
+long hash_fill; /* # of keys inserted in table */
+long hash_probes = 0;
+long hash_lookups = 0;
+int hash_rehashes = 0;
+
+int verbose_flag = 0;
+int statistics_flag = 1;
+
+int args_count = 0; /* # of args to save */
+int scan_count = 0; /* # of files to scan */
+int file_name_count = 0; /* # of files in database */
+int levels = 0; /* ceil(log(8)) of file_name_count */
+
+unsigned char current_hits_signature[MAX_LEVELS];
+#define INIT_TOKENS_SIZE(level) (1 << ((level) + 13))
+struct summary *summary_root;
+struct summary *summary_leaf;
+
+char PWD_name[BUFSIZ]; /* The current working directory */
+char absolute_idfile_name[BUFSIZ]; /* The absolute name of the database */
+char const *id_file = IDFILE;
+
+char const *program_name;
+
+void
+usage (void)
+{
+ fprintf (stderr, "\
+Usage: %s [-v] [-f<idfile>] [(+|-)l[<lang>]] [(+|-)S<scanarg>] [-a<argfile>] [-] [-u] [files...]\n\
+ -v Verbose: print reports of progress\n\
+ -a<file> Open file for arguments\n\
+ - Read newline-separated args from stdin\n\
+ -l<lang> Force files to be scanned as <lang> until +l<lang>\n\
+ -S<lang>-<arg> Pass arg to <lang> scanner\n\
+ -S.<suffix>=<lang> Scan files with .<suffix> as <lang>\n\
+ -S<lang>? Print usage documentation for <lang>\n\
+ -u Update an existing database (unimplemented)\n\
+\n\
+Version %s; Made %s %s\n",
+ program_name, FULL_VERSION, __DATE__, __TIME__);
+
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ struct idarg *idarg_0;
+ char const *sbrk0;
+
+ program_name = basename (GETARG (argc, argv));
+ if (kshgetwd (PWD_name) == NULL)
+ {
+ fprintf (stderr, "%s: cannot get current working directory name.\n", program_name);
+ return 1;
+ }
+ strcat (PWD_name, "/");
+
+ idarg_0 = parse_idargs (argc, argv);
+ if (idarg_0 == NULL)
+ {
+ fprintf (stderr, "Nothing to do...\n");
+ return 0;
+ }
+
+ sbrk0 = (char const *)sbrk (0);
+ init_hash (scan_count * 64);
+
+ strcpy (absolute_idfile_name, span_file_name (PWD_name, id_file));
+ if (access (id_file, 06) < 0
+ && (errno != ENOENT || access (dirname (id_file), 06) < 0))
+ {
+ filerr ("modify", id_file);
+ return 1;
+ }
+
+ init_hits_signature (0);
+ init_summary ();
+ init_scanners ();
+
+ scan_files (idarg_0);
+
+ if (hash_fill == 0)
+ return 0;
+
+ free_summary_tokens ();
+ free (hash_table);
+
+ write_idfile (id_file, idarg_0);
+ heap_size = (char const *)sbrk (0) - sbrk0;
+
+ if (statistics_flag)
+ report_statistics ();
+ return 0;
+}
+
+void
+scan_files (struct idarg *idarg)
+{
+ int keep_lang = 0;
+
+ for ( ; idarg->ida_next; idarg = idarg->ida_next)
+ {
+ char const *(*scanner) (FILE*, int*);
+ FILE *source_FILE;
+ char *arg = idarg->ida_arg;
+ char const *lang_name = NULL;
+ char const *suff;
+ char const *filter;
+
+ if (idarg->ida_index < 0)
+ {
+ int op = *arg++;
+ switch (*arg++)
+ {
+ case 'l':
+ if (*arg == '\0')
+ {
+ keep_lang = 0;
+ lang_name = NULL;
+ break;
+ }
+ if (op == '+')
+ keep_lang = 1;
+ lang_name = arg;
+ break;
+ case 'S':
+ set_scan_args (op, strdup (arg));
+ break;
+ default:
+ usage ();
+ }
+ continue;
+ }
+ if (!(idarg->ida_flags & IDA_SCAN_ME))
+ goto skip;
+
+ suff = strrchr (arg, '.');
+ if (lang_name == NULL)
+ {
+ if (suff == NULL)
+ suff = "";
+ lang_name = get_lang_name (suff);
+ if (lang_name == NULL)
+ lang_name = get_lang_name ("");
+ if (lang_name == NULL)
+ {
+ fprintf (stderr, "%s: No language assigned to suffix: `%s'\n", program_name, suff);
+ goto skip;
+ }
+ }
+ scanner = get_scanner (lang_name);
+ if (scanner == NULL)
+ {
+ fprintf (stderr, "%s: No scanner for language: `%s'\n", program_name, lang_name);
+ goto skip;
+ }
+ filter = get_filter (suff);
+ source_FILE = open_source_FILE (arg, filter);
+ if (source_FILE == NULL)
+ goto skip;
+ if (verbose_flag)
+ {
+ if (filter)
+ {
+ printf ("%s: ", lang_name);
+ printf (filter, arg);
+ putchar ('\n');
+ }
+ else
+ printf ("%s: %s\n", lang_name, arg);
+ }
+ scan_1_file (scanner, source_FILE);
+ close_source_FILE (source_FILE, filter);
+ skip:
+ if (!keep_lang)
+ lang_name = NULL;
+ if (idarg->ida_index < file_name_count)
+ {
+ if (current_hits_signature[0] & 0x80)
+ summarize ();
+ bump_current_hits_signature ();
+ }
+ }
+}
+
+void
+report_statistics (void)
+{
+ printf ("Name=%ld, ", name_tokens);
+ printf ("Number=%ld, ", number_tokens);
+ printf ("String=%ld, ", string_tokens);
+ printf ("Literal=%ld, ", literal_tokens);
+ printf ("Comment=%ld\n", comment_tokens);
+
+ printf ("Files=%d, ", scan_count);
+ printf ("Tokens=%ld, ", occurrences);
+ printf ("Bytes=%ld Kb, ", input_chars / 1024);
+ printf ("Heap=%ld Kb, ", heap_size / 1024);
+ printf ("Output=%ld (%ld tok, %ld hit)\n", output_length, tokens_length, hits_length);
+
+ printf ("Load=%ld/%ld=%.2f, ", hash_fill, hash_size,
+ (double) hash_fill / (double) hash_size);
+ printf ("Rehash=%d, ", hash_rehashes);
+ printf ("Probes=%ld/%ld=%.2f, ", hash_probes, hash_lookups,
+ (double) hash_probes / (double) hash_lookups);
+ printf ("Freq=%ld/%ld=%.2f\n", occurrences, hash_fill,
+ (double) occurrences / (double) hash_fill);
+}
+
+struct idarg *
+parse_idargs (int argc, char **argv)
+{
+ struct idarg *idarg;
+ struct idarg *idarg_0;
+ char *arg;
+ int op;
+ FILE *arg_FILE = NULL;
+ int args_from = 0;
+ enum {
+ AF_CMDLINE = 0x1, /* file args came on command line */
+ AF_FILE = 0x2, /* file args came from a file (-f<file>) */
+ AF_IDFILE = 0x4, /* file args came from an old ID file (-u) */
+ AF_QUERY = 0x8
+ }; /* no file args necessary: usage query */
+
+ idarg = idarg_0 = CALLOC(struct idarg, 1);
+
+ /* Process some arguments, and snarf-up some others for processing
+ later. */
+ while (argc)
+ {
+ arg = GETARG (argc, argv);
+ if (*arg != '-' && *arg != '+')
+ {
+ /* arguments are from command line (not pipe) */
+ args_from |= AF_CMDLINE;
+ idarg->ida_arg = arg;
+ idarg->ida_flags = IDA_SCAN_ME;
+ idarg->ida_index = file_name_count++;
+ scan_count++;
+ idarg = (idarg->ida_next = CALLOC(struct idarg, 1));
+
+ continue;
+ }
+ op = *arg++;
+ switch (*arg++)
+ {
+ case 'u':
+#if 0
+ args_from |= AF_IDFILE;
+ old_idarg (id_file, &idarg);
+ break;
+#endif
+ case '\0':
+ args_from |= AF_FILE;
+ idarg = parse_idargs_from_FILE (stdin, idarg);
+ break;
+ case 'a':
+ arg_FILE = fopen (arg, "r");
+ if (arg_FILE == NULL)
+ filerr ("open", arg);
+ else
+ {
+ args_from |= AF_FILE;
+ idarg = parse_idargs_from_FILE (arg_FILE, idarg);
+ }
+ break;
+ case 'f':
+ id_file = arg;
+ break;
+ case 'v':
+ verbose_flag = 1;
+ break;
+ case 'S':
+ if (strchr (&arg[-2], '?'))
+ {
+ set_scan_args (op, arg);
+ args_from |= AF_QUERY;
+ }
+ /*FALLTHROUGH */
+ case 'l':
+ case 's':
+ case 'r':
+ idarg->ida_arg = &arg[-2];
+ idarg->ida_index = -1;
+ idarg = (idarg->ida_next = CALLOC(struct idarg, 1));
+
+ args_count++;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (args_from & AF_QUERY)
+ exit (0);
+ /* File args should only come from one place. Ding the user if
+ arguments came from multiple places, or if none were supplied at
+ all. */
+ switch (args_from)
+ {
+ case AF_CMDLINE:
+ case AF_FILE:
+ case AF_IDFILE:
+ if (file_name_count > 0)
+ break;
+ /*FALLTHROUGH */
+ case 0:
+ fprintf (stderr, "%s: Use -u, -f<file>, or cmd-line for file args!\n", program_name);
+ usage ();
+ default:
+ fprintf (stderr, "%s: Use only one of: -u, -f<file>, or cmd-line for file args!\n", program_name);
+ usage ();
+ }
+
+ if (scan_count == 0)
+ return NULL;
+
+ return idarg_0;
+}
+
+
+/* Cons up a list of idarg as supplied in a file. */
+struct idarg *
+parse_idargs_from_FILE (FILE *arg_FILE, struct idarg *idarg)
+{
+ int file_count;
+ char buf[BUFSIZ];
+ char *arg;
+
+ file_count = 0;
+ while (fgets (buf, sizeof (buf), arg_FILE))
+ {
+ idarg->ida_arg = arg = strndup (buf, strlen (buf) - 1);
+ if (*arg == '+' || *arg == '-')
+ idarg->ida_index = -1;
+ else
+ {
+ idarg->ida_flags = IDA_SCAN_ME;
+ idarg->ida_index = file_name_count++;
+ scan_count++;
+ }
+ idarg = idarg->ida_next = CALLOC (struct idarg, 1);
+ }
+ return idarg;
+}
+
+void
+scan_1_file (char const *(*get_token) (FILE*, int*), FILE *source_FILE)
+{
+ struct token **slot;
+ char const *key;
+ int flags;
+
+ while ((key = (*get_token) (source_FILE, &flags)) != NULL)
+ {
+ struct token *token = *(slot = hash_lookup (key));
+
+ if (token)
+ {
+ token->tok_flags |= flags;
+ if (token->tok_count < USHRT_MAX)
+ token->tok_count++;
+ if (!(token->tok_hits[0] & current_hits_signature[0]))
+ sign_token (token);
+ } else {
+ *slot = token = make_token (key, flags);
+ sign_token (token);
+ if (hash_fill++ >= hash_capacity)
+ rehash ();
+ }
+ }
+}
+
+/* As the database is written, may need to adjust the file names. If
+ we are generating the ID file in a remote directory, then adjust
+ the file names to be relative to the location of the ID database.
+
+ (This would be a common useage if you want to make a database for a
+ directory which you have no write access to, so you cannot create
+ the ID file.) */
+void
+write_idfile (char const *id_file, struct idarg *idarg)
+{
+ struct token **tokens;
+ int i;
+ FILE *id_FILE;
+ int lasti;
+ struct idhead idh;
+ int fixup_names;
+ char *lsl;
+ int buf_size;
+ int vec_size;
+ int tok_size;
+ int max_buf_size = 0;
+ int max_vec_size = 0;
+
+ if (verbose_flag)
+ printf ("Writing `%s'...\n", id_file);
+ lsl = strrchr (relative_file_name (PWD_name, absolute_idfile_name), '/');
+ if (lsl == NULL)
+ {
+ /* The database is in the cwd, don't adjust the names */
+ fixup_names = 0;
+ }
+ else
+ {
+ /* The database is not in cwd, adjust names so they are relative
+ to the location of the database, make absolute_idfile_name just be the
+ directory path to ID. */
+ fixup_names = 1;
+ *(lsl + 1) = '\0';
+ }
+ id_FILE = fopen (id_file, "w+b");
+ if (id_FILE == NULL)
+ {
+ filerr ("create", id_file);
+ exit (1);
+ }
+ strncpy (idh.idh_magic, IDH_MAGIC, sizeof (idh.idh_magic));
+ idh.idh_version = IDH_VERSION;
+ idh.idh_pad_1 = 0;
+ idh.idh_flags = IDH_COUNTS;
+
+ /* write out the list of pathnames */
+ fseek (id_FILE, sizeof_idhead (), 0);
+ idh.idh_args_offset = ftell (id_FILE);
+ for (i = lasti = 0; idarg->ida_next; idarg = idarg->ida_next)
+ {
+ if (idarg->ida_index > 0)
+ while (++lasti < idarg->ida_index)
+ {
+ i++;
+ putc ('\0', id_FILE);
+ }
+ if (fixup_names)
+ fputs (relative_file_name (absolute_idfile_name, span_file_name (PWD_name, idarg->ida_arg)), id_FILE);
+ else
+ fputs (idarg->ida_arg, id_FILE);
+ i++;
+ putc ('\0', id_FILE);
+ }
+ idh.idh_args = i;
+ idh.idh_paths = file_name_count;
+
+ /* write out the list of identifiers */
+
+ putc ('\0', id_FILE);
+ putc ('\0', id_FILE);
+ idh.idh_tokens_offset = ftell(id_FILE);
+
+ assert (summary_root->sum_hits_count == hash_fill);
+ tokens = REALLOC (summary_root->sum_tokens, struct token *, hash_fill);
+ qsort (tokens, hash_fill, sizeof (struct token *), compare_tokens);
+ for (i = 0; i < hash_fill; i++, tokens++)
+ {
+ struct token *token = *tokens;
+ if (*token->tok_name == '\0')
+ {
+ fprintf (stderr, "Blank token!\n");
+ hash_fill--;
+ i--;
+ continue;
+ }
+ occurrences += token->tok_count;
+ if (token->tok_flags & TOK_NUMBER)
+ number_tokens++;
+ if (token->tok_flags & TOK_NAME)
+ name_tokens++;
+ if (token->tok_flags & TOK_STRING)
+ string_tokens++;
+ if (token->tok_flags & TOK_LITERAL)
+ literal_tokens++;
+ if (token->tok_flags & TOK_COMMENT)
+ comment_tokens++;
+
+ fputs (token->tok_name, id_FILE);
+ putc ('\0', id_FILE);
+ if (token->tok_count > 0xff)
+ token->tok_flags |= TOK_SHORT_COUNT;
+ putc (token->tok_flags, id_FILE);
+ putc (token->tok_count & 0xff, id_FILE);
+ if (token->tok_flags & TOK_SHORT_COUNT)
+ putc (token->tok_count >> 8, id_FILE);
+
+ vec_size = count_vec_size (summary_root, token->tok_hits + levels);
+ buf_size = count_buf_size (summary_root, token->tok_hits + levels);
+ hits_length += buf_size;
+ tok_size = strlen (token->tok_name) + 1;
+ tokens_length += tok_size;
+ buf_size += tok_size + sizeof (token->tok_flags) + sizeof (token->tok_count) + 2;
+ if (buf_size > max_buf_size)
+ max_buf_size = buf_size;
+ if (vec_size > max_vec_size)
+ max_vec_size = vec_size;
+
+ write_hits (id_FILE, summary_root, token->tok_hits + levels);
+ putc ('\0', id_FILE);
+ putc ('\0', id_FILE);
+ }
+ assert_hits (summary_root);
+ idh.idh_tokens = hash_fill;
+ output_length = ftell (id_FILE);
+ idh.idh_end_offset = output_length - 2;
+ idh.idh_buf_size = max_buf_size;
+ idh.idh_vec_size = max_vec_size;
+
+ write_idhead (id_FILE, &idh);
+ fclose (id_FILE);
+}
+
+void
+init_hash (long size)
+{
+ hash_size = round2 (size);
+ if (hash_size < 512)
+ hash_size = 512;
+#if 0
+ if (hash_size > 0x8000)
+ hash_size = 0x8000;
+#endif
+ if (hash_size > (128 * 1024))
+ hash_size /= 2;
+ hash_capacity = hash_size - (hash_size >> 4); /* about 94% */
+ hash_table = CALLOC (struct token *, hash_size);
+ assert (hash_table);
+}
+
+/* Use double hashing with open addressing.
+ The table size is always a power of two. */
+struct token **
+hash_lookup (char const *key)
+{
+ unsigned int hash1 = string_hash_1 (key) % hash_size;
+ unsigned int hash2 = 0;
+ struct token **slot = &hash_table[hash1];
+
+ hash_lookups++;
+ for (;;)
+ {
+ hash_probes++;
+ if (*slot == NULL || strcmp (key, (*slot)->tok_name) == 0)
+ return slot;
+
+ if (!hash2)
+ hash2 = string_hash_2 (key);
+ hash1 = (hash1 + hash2) % hash_size;
+ slot = &hash_table[hash1];
+ }
+}
+
+/* Primary hash function for string keys. */
+unsigned int
+string_hash_1 (char const *key)
+{
+ unsigned int sum = 0;
+
+ key--;
+ while (*++key)
+ sum += (*key << (key[1] & 0xf));
+
+ return sum;
+}
+
+/* Secondary hash function for string keys. The result must be
+ relatively prime to the table size, which is a power of two,
+ so any odd number will do. */
+unsigned int
+string_hash_2 (char const *key)
+{
+ unsigned int sum = 0;
+
+ key--;
+ while (*++key)
+ sum += (*key << (key[1] & 0x7));
+
+ return sum | 1;
+}
+
+/* Double the size of the hash table in the event of overflow... */
+void
+rehash (void)
+{
+ long old_hash_size = hash_size;
+ struct token **old_hash_table = hash_table;
+ struct token **htp;
+ struct token **slot;
+
+ hash_size *= 2;
+ if (verbose_flag)
+ printf ("Rehashing... (doubling size to %ld)\n", hash_size);
+ hash_rehashes++;
+ hash_capacity = hash_size - (hash_size >> 4);
+ hash_table = CALLOC (struct token *, hash_size);
+
+ for (htp = old_hash_table; htp < &old_hash_table[old_hash_size]; htp++)
+ {
+ if (*htp == NULL)
+ continue;
+ slot = hash_lookup ((*htp)->tok_name);
+
+ if (*slot)
+ {
+ fprintf (stderr, "%s: Duplicate hash entry!\n", (*slot)->tok_name);
+ exit (1);
+ }
+ *slot = *htp;
+ }
+ free (old_hash_table);
+}
+
+/* Round a given number up to the nearest power of 2. */
+int
+round2 (int rough)
+{
+ int round;
+
+ round = 1;
+ while (rough)
+ {
+ round <<= 1;
+ rough >>= 1;
+ }
+ return round;
+}
+
+/* Allocate a new token struct and fill in the name field. We
+ allocate memory in large chunks to avoid frequent calls to malloc ()
+ which is a major pig. */
+struct token *
+make_token (char const *name, int flags)
+{
+ struct token *token = (struct token *) malloc (sizeof (struct token) + strlen (name));
+
+ if (!token)
+ {
+ fprintf (stderr, "malloc failure! \n");
+ exit (1);
+ }
+ token->tok_count = 1;
+ token->tok_flags = flags;
+ memset (token->tok_hits, 0, sizeof (token->tok_hits));
+ strcpy (token->tok_name, name);
+
+ return token;
+}
+
+/* ///////////// summary stuff //////////////////////////////////////////// */
+
+void
+bump_current_hits_signature (void)
+{
+ unsigned char *hits = current_hits_signature;
+ while (*hits & 0x80)
+ *hits++ = 1;
+ *hits <<= 1;
+}
+
+void
+init_hits_signature (int index)
+{
+ unsigned char *hits = current_hits_signature;
+ unsigned char const *end = &current_hits_signature[MAX_LEVELS];
+ while (hits < end)
+ {
+ *hits = 1 << (index & 7);
+ index >>= 3;
+ hits++;
+ }
+}
+
+int
+bit_to_index (int bit)
+{
+ int i = 0;
+ while (bit >>= 1)
+ i++;
+ return i;
+}
+
+int
+compare_tokens (void const *x, void const *y)
+{
+ return strcmp ((*(struct token const *const *)x)->tok_name,
+ (*(struct token const *const *)y)->tok_name);
+}
+
+void
+free_summary_tokens (void)
+{
+ struct summary *summary = summary_leaf;
+ while (summary != summary_root)
+ {
+ free (summary->sum_tokens);
+ summary = summary->sum_parent;
+ }
+}
+
+void
+summarize (void)
+{
+ unsigned char const *hits_sig = current_hits_signature;
+ struct summary *summary = summary_leaf;
+
+ do
+ {
+ long count = summary->sum_hits_count;
+ unsigned char *hits = MALLOC (unsigned char, count + 1);
+ int level = summary->sum_level;
+ struct token **tokens = summary->sum_tokens;
+ long init_size = INIT_TOKENS_SIZE (summary->sum_level);
+
+ if (verbose_flag)
+ {
+ char const *fmt;
+ if (count < init_size / 2)
+ fmt = "level %d: %ld < %ld/2\n";
+ else if (count > init_size * 2)
+ fmt = "level %d: %ld > %ld*2\n";
+ else if (count < init_size)
+ fmt = "level %d: %ld < %ld\n";
+ else if (count > init_size)
+ fmt = "level %d: %ld > %ld\n";
+ else
+ fmt = "level %d: %ld == %ld\n";
+ printf (fmt, summary->sum_level, count, init_size);
+ }
+
+ qsort (tokens, count, sizeof (struct token *), compare_tokens);
+ summary->sum_hits = hits;
+ while (count--)
+ {
+ unsigned char *hit = &(*tokens++)->tok_hits[level];
+ *hits++ = *hit;
+ *hit = 0;
+ }
+ *hits++ = 0;
+ if (summary->sum_parent)
+ {
+ free (summary->sum_tokens);
+ summary->sum_tokens = 0;
+ }
+ summary = summary->sum_parent;
+ }
+ while (*++hits_sig & 0x80);
+ summary_leaf = make_sibling_summary (summary_leaf);
+}
+
+void
+init_summary (void)
+{
+ long size = INIT_TOKENS_SIZE (0);
+ summary_root = summary_leaf = CALLOC (struct summary, 1);
+ summary_root->sum_tokens_size = size;
+ summary_root->sum_tokens = MALLOC (struct token *, size);
+}
+
+struct summary *
+make_sibling_summary (struct summary *summary)
+{
+ struct summary *parent = summary->sum_parent;
+ long size;
+
+ if (parent == NULL)
+ {
+ levels++;
+ size = INIT_TOKENS_SIZE (levels);
+ summary_root = summary->sum_parent = parent = CALLOC (struct summary, 1);
+ parent->sum_level = levels;
+ parent->sum_kids[0] = summary;
+ parent->sum_hits_count = summary->sum_hits_count;
+ parent->sum_free_index = 1;
+ parent->sum_tokens_size = size;
+ parent->sum_tokens = REALLOC (summary->sum_tokens, struct token *, size);
+ summary->sum_tokens = 0;
+ }
+ if (parent->sum_free_index == 8)
+ parent = make_sibling_summary (parent);
+ summary = CALLOC (struct summary, 1);
+ summary->sum_level = parent->sum_level - 1;
+ parent->sum_kids[parent->sum_free_index++] = summary;
+ summary->sum_parent = parent;
+ size = INIT_TOKENS_SIZE (summary->sum_level);
+ summary->sum_tokens_size = size;
+ summary->sum_tokens = MALLOC (struct token *, size);
+ return summary;
+}
+
+int
+count_vec_size (struct summary *summary, unsigned char const *tail_hits)
+{
+ struct summary **kids;
+ unsigned int hits = (summary->sum_hits ? *summary->sum_hits : *tail_hits);
+
+ kids = summary->sum_kids;
+ if (*kids == NULL)
+ {
+ static char bits_per_nybble[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+ return bits_per_nybble[hits & 0xf] + bits_per_nybble[hits >> 4];
+ }
+ else
+ {
+ int bit;
+ int count = 0;
+ --tail_hits;
+ for (bit = 1; bit & 0xff; bit <<= 1, ++kids)
+ if (bit & hits)
+ count += count_vec_size (*kids, tail_hits);
+ return count;
+ }
+}
+
+int
+count_buf_size (struct summary *summary, unsigned char const *tail_hits)
+{
+ struct summary **kids;
+ unsigned int hits = (summary->sum_hits ? *summary->sum_hits : *tail_hits);
+
+ kids = summary->sum_kids;
+ if (*kids == NULL)
+ return 1;
+ else
+ {
+ int bit;
+ int count = 1;
+ --tail_hits;
+ for (bit = 1; bit & 0xff; bit <<= 1, ++kids)
+ if (bit & hits)
+ count += count_buf_size (*kids, tail_hits);
+ return count;
+ }
+}
+
+void
+assert_hits (struct summary* summary)
+{
+ struct summary **kids = summary->sum_kids;
+ struct summary **end = &kids[8];
+
+ if (summary == summary_root)
+ assert (summary->sum_hits == 0);
+ else
+ assert (summary->sum_hits && *summary->sum_hits == 0);
+
+ if (end[-1] == 0)
+ while (*--end == 0)
+ ;
+ while (kids < end)
+ assert_hits (*kids++);
+}
+
+void
+write_hits (FILE *fp, struct summary *summary, unsigned char const *tail_hits)
+{
+ struct summary **kids;
+ unsigned int hits = (summary->sum_hits ? *summary->sum_hits++ : *tail_hits);
+
+ assert (hits);
+ putc (hits, fp);
+
+ kids = summary->sum_kids;
+ if (*kids)
+ {
+ int bit;
+ --tail_hits;
+ for (bit = 1; (bit & 0xff) && *kids; bit <<= 1, ++kids)
+ if (bit & hits)
+ write_hits (fp, *kids, tail_hits);
+ }
+}
+
+void
+sign_token (struct token *token)
+{
+ unsigned char *tok_hits = token->tok_hits;
+ unsigned char *hits_sig = current_hits_signature;
+ unsigned char *end = &current_hits_signature[MAX_LEVELS];
+ struct summary *summary = summary_leaf;
+
+ while (summary)
+ {
+ if (*tok_hits == 0)
+ add_token_to_summary (summary, token);
+ if (*tok_hits & *hits_sig)
+ break;
+ *tok_hits |= *hits_sig;
+ summary = summary->sum_parent;
+ tok_hits++;
+ hits_sig++;
+ }
+ while (hits_sig < end)
+ {
+ if (*tok_hits & *hits_sig)
+ break;
+ *tok_hits |= *hits_sig;
+ tok_hits++;
+ hits_sig++;
+ }
+}
+
+void
+add_token_to_summary (struct summary *summary, struct token *token)
+{
+ long size = summary->sum_tokens_size;
+
+ if (summary->sum_hits_count >= size)
+ {
+ size *= 2;
+ summary->sum_tokens = REALLOC (summary->sum_tokens, struct token *, size);
+ summary->sum_tokens_size = size;
+ }
+ summary->sum_tokens[summary->sum_hits_count++] = token;
+}
+
+int
+bitsany (char const *s, int n)
+{
+ while (n--)
+ if (*s++)
+ return 1;
+
+ return 0;
+}
+
+char *
+bitsset (char *s1, char const *s2, int n)
+{
+ while (n--)
+ *s1++ |= *s2++;
+
+ return s1;
+}
+
+char *
+bitsclr (char *s1, char const *s2, int n)
+{
+ while (n--)
+ *s1++ &= ~*s2++;
+
+ return s1;
+}
+
+#if 0
+
+char *
+bitsand (char *s1, char const *s2, int n)
+{
+ while (n--)
+ *s1++ &= *s2++;
+
+ return s1;
+}
+
+char *
+bitsxor (char *s1, char const *s2, int n)
+{
+ while (n--)
+ *s1++ ^= *s2++;
+
+ return s1;
+}
+
+int
+bitstst (char const *s1, char const *s2, int n)
+{
+ while (n--)
+ if (*s1++ & *s2++)
+ return 1;
+
+ return 0;
+}
+
+#endif
diff --git a/mkid.info b/mkid.info
new file mode 100644
index 0000000..029013b
--- /dev/null
+++ b/mkid.info
@@ -0,0 +1,1094 @@
+This is Info file mkid.info, produced by Makeinfo-1.55 from the input
+file mkid.texinfo.
+
+START-INFO-DIR-ENTRY
+* mkid: (mkid). Identifier database utilities
+END-INFO-DIR-ENTRY
+
+ This file documents the `mkid' identifier database utilities.
+
+ Copyright (C) 1991 Tom Horsley
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be stated in a
+translation.
+
+
+File: mkid.info, Node: Top, Next: Overview, Prev: (dir), Up: (dir)
+
+* Menu:
+
+* Overview:: What is an ID database and what tools manipulate it?
+* Mkid:: Mkid
+* Database Query Tools:: Database Query Tools
+* Iid:: Iid
+* Other Tools:: Other Tools
+* Command Index:: Command Index
+
+
+File: mkid.info, Node: Overview, Next: Mkid, Prev: Top, Up: Top
+
+Overview
+********
+
+ An ID database is simply a file containing a list of file names, a
+list of identifiers, and a binary relation (stored as a bit matrix)
+indicating which of the identifiers appear in each file. With this
+database and some tools to manipulate the data, a host of tasks become
+simpler and faster. You can `grep' through hundreds of files for a
+name, skipping the files that don't contain the name. You can search
+for all the memos containing references to a project. You can edit
+every file that calls some function, adding a new required argument.
+Anyone with a large software project to maintain, or a large set of
+text files to organize can benefit from the ID database and the tools
+that manipulate it.
+
+ There are several programs in the ID family. The `mkid' program
+scans the files, finds the identifiers and builds the ID database. The
+`lid' and `aid' tools are used to generate lists of file names
+containing an identifier (perhaps to recompile every file that
+references a macro which just changed). The `eid' program will invoke
+an editor on each of the files containing an identifier and the `gid'
+program will `grep' for an identifier in the subset of files known to
+contain it. The `pid' tool is used to query the path names of the
+files in the database (rather than the contents). Finally, the `iid'
+tool is an interactive program supporting complex queries to intersect
+and join sets of file names.
+
+* Menu:
+
+* History:: History
+
+
+File: mkid.info, Node: History, Prev: Overview, Up: Overview
+
+History
+=======
+
+ Greg McGary conceived of the ideas behind mkid when he began hacking
+the UNIX kernel in 1984. He needed a navigation tool to help him find
+his way the expansive, unfamiliar landscape. The first mkid-like tools
+were built with shell scripts, and produced an ascii database that looks
+much like the output of `lid' with no arguments. It took over an hour
+on a VAX 11/750 to build a database for a 4.1BSDish kernel. Lookups
+were done with the UNIX command `look', modified to handle very long
+lines.
+
+ In 1986, Greg rewrote mkid, lid, fid and idx in C to improve
+performance. Database-build times were shortened by an order of
+magnitude. The mkid tools were first posted to `comp.sources.unix'
+September of 1987.
+
+ Over the next few years, several versions diverged from the original
+source. Tom Horsley at Harris Computer Systems Division stepped forward
+to take over maintenance and integrated some of the fixes from divergent
+versions. He also wrote the `iid' program. A pre-release of `mkid2'
+was posted to `alt.sources' near the end of 1990. At that time Tom
+wrote this texinfo manual with the encouragement the net community.
+(Tom thanks Doug Scofield and Bill Leonard whom I dragooned into
+helping me poorf raed and edit -- they found several problems in the
+initial version.)
+
+ In January, 1995, Greg McGary reemerged as the primary maintaner and
+is hereby launching `mkid-3' whose primary new feature is an efficient
+algorithm for building databases that is linear over the size of the
+input text for both time and space. (The old algorithm was quadratic
+for space and choked on very large source trees.) The code is now under
+GPL and might become a part of the GNU system. `Mkid-3' is an interim
+release, since several significant enhacements are in the works. These
+include an optional coupling with GNU grep, so that grep can use an ID
+database for hints; a cscope work-alike query interface; incremental
+update of the ID database; and an automatic file-tree walker so you
+need not explicitly supply every file name argument to the `mkid'
+program.
+
+
+File: mkid.info, Node: Mkid, Next: Database Query Tools, Prev: Overview, Up: Top
+
+Mkid
+****
+
+ The `mkid' program builds the ID database. To do this it must scan
+each of the files included in the database. This takes some time, but
+once the work is done the query programs run very rapidly.
+
+ The `mkid' program knows how to scan a variety of of files. For
+example, it knows how to skip over comments and strings in a C program,
+only picking out the identifiers used in the code.
+
+ Identifiers are not the only thing included in the database.
+Numbers are also scanned and included in the database indexed by their
+binary value. Since the same number can be written many different ways
+(47, 0x2f, 057 in a C program for instance), this feature allows you to
+find hard coded uses of constants without regard to the radix used to
+specify them.
+
+ All the places in this document where identifiers are written about
+should really mention identifiers and numbers, but that gets fairly
+clumsy after a while, so you should always keep in mind that numbers are
+included in the database as well as identifiers.
+
+* Menu:
+
+* Mkid Command Line Options:: Mkid Command Line Options
+* Builtin Scanners:: Builtin Scanners
+* Adding Your Own Scanner:: Adding Your Own Scanner
+* Mkid Examples:: Mkid Examples
+
+
+File: mkid.info, Node: Mkid Command Line Options, Next: Builtin Scanners, Prev: Mkid, Up: Mkid
+
+Mkid Command Line Options
+=========================
+
+ - Command: mkid [`-v'] [`-SSCANARG'] [`-aARG-FILE'] [`-']
+ [`-fOUT-FILE'] [`-u'] [`files'...]
+ `-v'
+ Verbose. Mkid tells you as it scans each file and indicates
+ which scanner it is using. It also summarizes some statistics
+ about the database at the end.
+
+ `-SSCANARG'
+ The `-S' option is used to specify arguments to the various
+ language scanners. *Note Scanner Arguments::, for details.
+
+ `-aARG-FILE'
+ Name a file containing additional command line arguments (one
+ per line). This may be used to specify lists of file names
+ longer than will fit on a command line.
+
+ `-'
+ A simple `-' by itself means read arguments from stdin.
+
+ `-fOUT-FILE'
+ Specify the name of the database file to create. The default
+ name is `ID' (in the current directory), but you may specify
+ any name. The file names stored in the database will be
+ stored relative to the directory containing the database, so
+ if you move the database after creating it, you may have
+ trouble finding files unless they remain in the same relative
+ position.
+
+ `-u'
+ The `-u' option updates an existing database by rescanning
+ any files that have changed since the database was written.
+ Unfortunately you cannot incrementally add new files to a
+ database.
+
+ `files'
+ Remaining arguments are names of files to be scanned and
+ included in the database.
+
+* Menu:
+
+* Scanner Arguments:: Scanner Arguments
+
+
+File: mkid.info, Node: Scanner Arguments, Prev: Mkid Command Line Options, Up: Mkid Command Line Options
+
+Scanner Arguments
+-----------------
+
+ Scanner arguments all start with `-S'. Scanner arguments are used to
+tell `mkid' which language scanner to use for which files, to pass
+language specific options to the individual scanners, and to get some
+limited online help about scanner options.
+
+ `Mkid' usually determines which language scanner to use on a file by
+looking at the suffix of the file name. The suffix starts at the last
+`.' in a file name and includes the `.' and all remaining characters
+(for example the suffix of `fred.c' is `.c'). Not all files have a
+suffix, and not all suffixes are bound to a specific language by mkid.
+If `mkid' cannot determine what language a file is, it will use the
+language bound to the `.default' suffix. The plain text scanner is
+normally bound to `.default', but the `-S' option can be used to change
+any language bindings.
+
+ There are several different forms for scanner options:
+`-S.<SUFFIX>=<LANGUAGE>'
+ `Mkid' determines which language scanner to use on a file by
+ examining the file name suffix. The `.' is part of the suffix and
+ must be specified in this form of the `-S' option. For example
+ `-S.y=c' tells `mkid' to use the `c' language scanner for all
+ files ending in the `.y' suffix.
+
+`-S.<SUFFIX>=?'
+ `Mkid' has several built in suffixes it already recognizes. Passing
+ a `?' will cause it to print the language it will use to scan files
+ with that suffix.
+
+`-S?=<LANGUAGE>'
+ This form will print which suffixes are scanned with the given
+ language.
+
+`-S?=?'
+ This prints all the suffix==>language bindings recognized by
+ `mkid'.
+
+`-S<LANGUAGE>-<ARG>'
+ Each language scanner accepts scanner dependent arguments. This
+ form of the `-S' option is used to pass arbitrary arguments to the
+ language scanners.
+
+`-S<LANGUAGE>?'
+ Passing a `?' instead of a language option will print a brief
+ summary of the options recognized by the specified language
+ scanner.
+
+`-S<NEW LANGUAGE>/<BUILTIN LANGUAGE>/<FILTER COMMAND>'
+ This form specifies a new language defined in terms of a builtin
+ language and a shell command that will be used to filter the file
+ prior to passing on to the builtin language scanner.
+
+
+File: mkid.info, Node: Builtin Scanners, Next: Adding Your Own Scanner, Prev: Mkid Command Line Options, Up: Mkid
+
+Builtin Scanners
+================
+
+ If you run `mkid -S?=?' you will find bindings for a number of
+languages; unfortunately pascal, though mentioned in the list, is not
+actually supported. The supported languages are documented below (1).
+
+* Menu:
+
+* C:: C
+* Plain Text:: Plain Text
+* Assembler:: Assembler
+
+ ---------- Footnotes ----------
+
+ (1) This is not strictly true -- vhil is a supported language, but
+it is an obsolete and arcane dialect of C and should be ignored
+
+
+File: mkid.info, Node: C, Next: Plain Text, Prev: Builtin Scanners, Up: Builtin Scanners
+
+C
+-
+
+ The C scanner is probably the most popular. It scans identifiers out
+of C programs, skipping over comments and strings in the process. The
+normal `.c' and `.h' suffixes are automatically recognized as C
+language, as well as the more obscure `.y' (yacc) and `.l' (lex)
+suffixes.
+
+ The `-S' options recognized by the C scanner are:
+
+`-Sc-s<CHARACTER>'
+ Allow the specified <CHARACTER> in identifiers (some dialects of C
+ allow `$' in identifiers, so you could say `-Sc-s$' to accept that
+ dialect).
+
+`-Sc-u'
+ Don't strip leading underscores from identifier names (this is the
+ default mode of operation).
+
+`-Sc+u'
+ Do strip leading underscores from identifier names (I don't know
+ why you would want to do this in C programs, but the option is
+ available).
+
+
+File: mkid.info, Node: Plain Text, Next: Assembler, Prev: C, Up: Builtin Scanners
+
+Plain Text
+----------
+
+ The plain text scanner is designed for scanning documents. This is
+typically the scanner used when adding custom scanners, and several
+custom scanners are built in to `mkid' and defined in terms of filters
+and the text scanner. A troff scanner runs `deroff' over the file then
+feeds the result to the text scanner. A compressed man page scanner
+runs `pcat' piped into `col -b', and a TeX scanner runs `detex'.
+
+ Options:
+
+`-Stext+a<CHARACTER>'
+ Include the specified character in identifiers. By default,
+ standard C identifiers are recognized.
+
+`-Stext-a<CHARACTER>'
+ Exclude the specified character from identifiers.
+
+`-Stext+s<CHARACTER>'
+ Squeeze the specified character out of identifiers. By default, the
+ characters `'', `-', and `.' are squeezed out of identifiers.
+ This generates transformations like FRED'S==>FREDS or
+ A.S.P.C.A.==>ASPCA.
+
+`-Stext-s<CHARACTER>'
+ Do not squeeze out the specified character.
+
+
+File: mkid.info, Node: Assembler, Prev: Plain Text, Up: Builtin Scanners
+
+Assembler
+---------
+
+ Assemblers come in several flavors, so there are several options to
+control scanning of assembly code:
+
+`-Sasm-c<CHARACTER>'
+ The specified character starts a comment that extends to end of
+ line (in many assemblers this is a semicolon or number sign --
+ there is no default value for this).
+
+`-Sasm+u'
+ Strip the leading underscores off identifiers (the default
+ behavior).
+
+`-Sasm-u'
+ Do not strip the leading underscores.
+
+`-Sasm+a<CHARACTER>'
+ The specified character is allowed in identifiers.
+
+`-Sasm-a<CHARACTER>'
+ The specified character is allowed in identifiers, but any
+ identifier containing that character is ignored (often a `.' or `@'
+ will be used to indicate an internal temp label, you may want to
+ ignore these).
+
+`-Sasm+p'
+ Recognize C preprocessor directives in assembler source (default).
+
+`-Sasm-p'
+ Do not recognize C preprocessor directives in assembler source.
+
+`-Sasm+C'
+ Skip over C style comments in assembler source (default).
+
+`-Sasm-C'
+ Do not skip over C style comments in assembler source.
+
+
+File: mkid.info, Node: Adding Your Own Scanner, Next: Mkid Examples, Prev: Builtin Scanners, Up: Mkid
+
+Adding Your Own Scanner
+=======================
+
+ There are two ways to add new scanners to `mkid'. The first is to
+modify the code in `getscan.c' and add a new `scan-*.c' file with the
+code for your scanner. This is not too hard, but it requires relinking
+and installing a new version of `mkid', which might be inconvenient,
+and would lead to the proliferation of `mkid' versions.
+
+ The second technique uses the `-S<lang>/<lang>/<filter>' form of
+the `-S' option to specify a new language scanner. In this form the
+first language is the name of the new language to be defined, the
+second language is the name of an existing language scanner to be
+invoked on the output of the filter command specified as the third
+component of the `-S' option.
+
+ The filter is an arbitrary shell command. Somewhere in the filter
+string, a `%s' should occur. This `%s' is replaced by the name of the
+source file being scanned, the shell command is invoked, and whatever
+comes out on STDOUT is scanned using the builtin scanner.
+
+ For example, no scanner is provided for texinfo files (like this
+one). If I wished to index the contents of this file, but avoid
+indexing the texinfo directives, I would need a filter that stripped
+out the texinfo directives, but left the remainder of the file intact.
+I could then use the plain text scanner on the remainder. A quick way
+to specify this might be:
+
+ '-S/texinfo/text/sed s,@[a-z]*,,g < %s'
+
+ This defines a new language scanner (TEXINFO) defined in terms of a
+`sed' command to strip out texinfo directives (at signs followed by
+letters). Once the directives are stripped, the remaining text is run
+through the plain text scanner.
+
+ This is just an example, to do a better job I would actually need to
+delete some lines (such as those beginning with `@end') as well as
+deleting the `@' directives embedded in the text.
+
+
+File: mkid.info, Node: Mkid Examples, Prev: Adding Your Own Scanner, Up: Mkid
+
+Mkid Examples
+=============
+
+ The simplest example of `mkid' is something like:
+
+ mkid *.[chy]
+
+ This will build an ID database indexing all the identifiers and
+numbers in the `.c', `.h', and `.y' files in the current directory.
+Because those suffixes are already known to `mkid' as C language files,
+no other special arguments are required.
+
+ From a simple example, lets go to a more complex one. Suppose you
+want to build a database indexing the contents of all the MAN pages.
+Since `mkid' already knows how to deal with `.z' files, let's assume
+your system is using the `compress' program to store compressed
+cattable versions of the MAN pages. The `compress' program creates
+files with a `.Z' suffix, so `mkid' will have to be told how to scan
+`.Z' files. The following code shows how to combine the `find' command
+with the special scanner arguments to `mkid' to generate the required ID
+database:
+
+ cd /usr/catman
+ find . -name '*.Z' -print | mkid '-Sman/text/uncompress -c < %s' -S.Z=man -
+
+ This example first switches to the `/usr/catman' directory where the
+compressed MAN pages are stored. The `find' command then finds all the
+`.Z' files under that directory and prints their names. This list is
+piped into the `mkid' program. The `-' argument by itself (at the end
+of the line) tells `mkid' to read arguments (in this case the list of
+file names) from STDIN. The first `-S' argument defines a new language
+(MAN) in terms of the `uncompress' utility and the existing text
+scanner. The second `-S' argument tells `mkid' to treat all `.Z' files
+as language MAN. In practice, you might find the `mkid' arguments need
+to be even more complex, something like:
+
+ mkid '-Sman/text/uncompress -c < %s | col -b' -S.Z=man -
+
+ This will take the additional step of getting rid of any underlining
+and backspacing which might be present in the compressed MAN pages.
+
+
+File: mkid.info, Node: Database Query Tools, Next: Iid, Prev: Mkid, Up: Top
+
+Database Query Tools
+********************
+
+ The ID database is useless without database query tools. The
+remainder of this document describes those tools.
+
+ The `lid', `gid', `aid', `eid', and `pid' programs are all the same
+program installed with links to different names. The name used to
+invoke the program determines how it will act.
+
+ The `iid' program is an interactive query shell that sits on top of
+the other query tools.
+
+* Menu:
+
+* Common Options:: Common command line options
+* Patterns:: Identifier pattern matching
+* Lid:: Look up identifiers
+* Aid:: Case insensitive lid
+* Gid:: Grep for identifiers
+* Eid:: Edit files with matching identifiers
+* Pid:: Look up path names in database
+
+
+File: mkid.info, Node: Common Options, Next: Patterns, Prev: Database Query Tools, Up: Database Query Tools
+
+Common Options
+==============
+
+ Since many of the programs are really links to one common program, it
+is only reasonable to expect that most of the query tools would share
+common command line options. Not all options make sense for all
+programs, but they are all described here. The description of each
+program gives the options that program uses.
+
+`-f<FILE>'
+ Read the database specified by <FILE>. Normally the tools look for
+ a file named `ID' in either the current directory or in any of the
+ directories above the current directory. This means you can keep a
+ global `ID' database in the root of a large source tree and use
+ the query tools from anywhere within that tree.
+
+`-r<DIRECTORY>'
+ The query tools usually assume the file names in the database are
+ relative to the directory holding the database. The `-r' option
+ tells the tools to look for the files relative to <DIRECTORY>
+ regardless of the location of the database.
+
+`-c'
+ This is shorthand for `-r`pwd`'. It tells the query tools to assume
+ the file names are stored relative to the current working
+ directory.
+
+`-e'
+ Force the pattern arguments to be treated as regular expressions.
+ Normally the query tools attempt to guess if the patterns are
+ regular expressions or simple identifiers by looking for special
+ characters in the pattern.
+
+`-w'
+ Force the pattern arguments to be treated as simple words even if
+ they contain special regular expression characters.
+
+`-k'
+ Normally the query tools that generate lists of file names attempt
+ to compress the lists using the `csh' brace notation. This option
+ suppresses the file name compression and outputs each name in full.
+ (This is particularly useful if you are a `ksh' user and want to
+ feed the list of names to another command -- the `-k' option comes
+ from the `k' in `ksh').
+
+`-g'
+ It is possible to build the query tools so the `-k' option is the
+ default behavior. If this is the case for your system, the `-g'
+ option turns on the globbing of file names using the `csh' brace
+ notation.
+
+`-n'
+ Normally the query tools that generate lists of file names also
+ list the matching identifier at the head of the list of names.
+ This is irritating if you want just a list of names to feed to
+ another command, so the `-n' option suppresses the identifier and
+ lists only file names.
+
+`-b'
+ This option is only used by the `pid' tool. It restricts `pid' to
+ pattern match only the basename part of a file name. Normally the
+ absolute file name is matched against the pattern.
+
+`-d -o -x -a'
+ These options may be used in any combination to limit the radix of
+ numeric matches. The `-d' option will allow matches on decimal
+ numbers, `-o' on octal, and `-x' on hexadecimal numbers. The `-a'
+ option is shorthand for specifying all three. Any combination of
+ these options may be used.
+
+`-m'
+ Merge multiple lines of output into a single line. (If your query
+ matches more than one identifier the default action is to generate
+ a separate line of output for each matching identifier).
+
+`-s'
+ Search for identifiers that appear only once in the database. This
+ helps to locate identifiers that are defined but never used.
+
+`-u<NUMBER>'
+ List identifiers that conflict in the first <NUMBER> characters.
+ This could be useful porting programs to brain-dead computers that
+ refuse to support long identifiers, but your best long term option
+ is to set such computers on fire.
+
+
+File: mkid.info, Node: Patterns, Next: Lid, Prev: Common Options, Up: Database Query Tools
+
+Patterns
+========
+
+ You can attempt to match either simple identifiers or numbers in a
+query, or you can specify a regular expression pattern which may match
+many different identifiers in the database. The query programs use
+either REGEX and REGCMP or RE_COMP and RE_EXEC, depending on which one
+is available in the library on your system. These might not always
+support the exact same regular expression syntax, so consult your local
+MAN pages to find out. Any regular expression routines should support
+the following syntax:
+
+`.'
+ A dot matches any character.
+
+`[ ]'
+ Brackets match any of the characters specified within the
+ brackets. You can match any characters *except* the ones in
+ brackets by typing `^' as the first character. A range of
+ characters can be specified using `-'.
+
+`*'
+ An asterisk means repeat the previous pattern zero or more times.
+
+`^'
+ An `^' at the beginning of a pattern means the pattern must match
+ starting at the first character of the identifier.
+
+`$'
+ A `$' at the end of the pattern means the pattern must match ending
+ at the last character in the identifier.
+
+
+File: mkid.info, Node: Lid, Next: Aid, Prev: Patterns, Up: Database Query Tools
+
+Lid
+===
+
+ - Command: lid [`-f<FILE>'] [`-u<N>'] [`-r<DIR>'] [`-ewdoxamskgnc']
+ PATTERNS...
+
+ The `lid' program stands for LOOKUP IDENTIFIER. It searches the
+database for any identifiers matching the patterns and prints the names
+of the files that match each pattern. The exact format of the output
+depends on the options.
+
+
+File: mkid.info, Node: Aid, Next: Gid, Prev: Lid, Up: Database Query Tools
+
+Aid
+===
+
+ - Command: aid [`-f<FILE>'] [`-u<N>'] [`-r<DIR>'] [`-doxamskgnc']
+ PATTERNS...
+
+ The `aid' command is an abbreviation for APROPOS IDENTIFIER. The
+patterns cannot be regular expressions, but it looks for them using a
+case insensitive match, and any pattern that is a substring of an
+identifier in the database will match that identifier.
+
+ For example `aid get' might match the identifiers `fgets',
+`GETLINE', and `getchar'.
+
+
+File: mkid.info, Node: Gid, Next: Eid, Prev: Aid, Up: Database Query Tools
+
+Gid
+===
+
+ - Command: gid [`-f<FILE>'] [`-u<N>'] [`-r<DIR>'] [`-doxasc']
+ PATTERNS...
+
+ The `gid' command stands for GREP FOR IDENTIFIERS. It finds
+identifiers in the database that match the specified patterns, then
+`greps' for those identifiers in just the set of files containing
+matches. In a large source tree, this saves a fantastic amount of time.
+
+ There is an EMACS interface to this program (*note GNU Emacs
+Interface::.). If you are an EMACS user, you will probably prefer the
+EMACS interface over the `eid' tool.
+
+
+File: mkid.info, Node: Eid, Next: Pid, Prev: Gid, Up: Database Query Tools
+
+Eid
+===
+
+ - Command: eid [`-f<FILE>'] [`-u<N>'] [`-r<DIR>'] [`-doxasc']
+ PATTERNS...
+
+ The `eid' command allows you to invoke an editor on each file
+containing a matching pattern. The `EDITOR' environment variable is the
+name of the program to be invoked. If the specified editor can accept
+an initial search argument on the command line, you can use the
+`EIDARG', `EIDLDEL', and `EIDRDEL' environment variables to specify the
+form of that argument.
+
+`EDITOR'
+ The name of the editor program to invoke.
+
+`EIDARG'
+ A printf string giving the form of the argument to pass containing
+ the initial search string (the matching identifier). For `vi' it
+ should be set to `+/%s/''.
+
+`EIDLDEL'
+ A string giving the regular expression pattern that forces a match
+ at the beginning (left end) of a word. This string is inserted in
+ front of the matching identifier when composing the search
+ argument. For `vi', this should be `\<'.
+
+`EIDRDEL'
+ The matching right end word delimiter. For `vi', use `\>'.
+
+
+File: mkid.info, Node: Pid, Prev: Eid, Up: Database Query Tools
+
+Pid
+===
+
+ - Command: pid [`-f<FILE>'] [`-u<N>'] [`-r<DIR>'] [`-ebkgnc']
+ PATTERNS...
+
+ The `pid' tool is unlike all the other tools. It matches the
+patterns against the file names in the database rather than the
+identifiers in the database. Patterns are treated as shell wild card
+patterns unless the `-e' option is given, in which case full regular
+expression matching is done.
+
+ The wild card pattern is matched against the absolute path name of
+the file. Most shells treat slashes `/' and file names that start with
+dot `.' specially, `pid' does not do this. It simply attempts to match
+the absolute path name string against the wild card pattern.
+
+ The `-b' option restricts the pattern matching to the base name of
+the file (all the leading directory names are stripped prior to pattern
+matching).
+
+
+File: mkid.info, Node: Iid, Next: Other Tools, Prev: Database Query Tools, Up: Top
+
+Iid
+***
+
+ - Command: iid [`-a'] [`-c<COMMAND>'] [`-H']
+ `-a'
+ Normally `iid' uses the `lid' command to search for names.
+ If you give the `-a' option on the command line, then it will
+ use `aid' as the default search engine.
+
+ `-c<COMMAND>'
+ In normal operation, `iid' starts up and prompts you for
+ commands used to build sets of files. The `-c' option is used
+ to pass a single query command to `iid' which it then
+ executes and exits.
+
+ `-H'
+ The `-H' option prints a short help message and exits. To get
+ more help use the `help' command from inside `iid'.
+
+ The `iid' program is an interactive ID query tool. It operates by
+running the other query programs (such as `lid' and `aid') and creating
+sets of file names returned by these queries. It also provides
+operators for `anding' and `oring' these sets to create new sets.
+
+ The `PAGER' environment variable names the program `iid' uses to
+display files. If you use `emacs', you might want to set `PAGER' so it
+invokes the `emacsclient' program. Check the file `lisp/server.el' in
+the emacs source tree for documentation on this. It is useful not only
+with X windows, but also when running `iid' from an emacs shell buffer.
+There is also a somewhat spiffier version called gnuserv by Andy Norman
+(`ange%anorman@hplabs.hp.com') which appeared in `comp.emacs' sometime
+in 1989.
+
+* Menu:
+
+* Ss and Files commands:: Ss and Files commands
+* Sets:: Sets
+* Show:: Show
+* Begin:: Begin
+* Help:: Help
+* Off:: Off
+* Shell Commands as Queries:: Shell Commands as Queries
+* Shell Escape:: Shell Escape
+
+
+File: mkid.info, Node: Ss and Files commands, Next: Sets, Prev: Iid, Up: Iid
+
+Ss and Files commands
+=====================
+
+ The primary query commands are `ss' (for select sets) and `files'
+(for show file names). These commands both take a query expression as an
+argument.
+
+ - Subcommand: ss QUERY
+ The `ss' command runs a query and builds a set (or sets) of file
+ names. The result is printed as a summary of the sets constructed
+ showing how many file names are in each set.
+
+ - Subcommand: files QUERY
+ The `files' command is like the `ss' command, but rather than
+ printing a summary, it displays the full list of matching file
+ names.
+
+ - Subcommand: f QUERY
+ The `f' command is merely a shorthand notation for `files'.
+
+ Database queries are simple expressions with operators like `and'
+and `or'. Parentheses can be used to group operations. The complete set
+of operators is summarized below:
+
+`PATTERN'
+ Any pattern not recognized as one of the keywords in this table is
+ treated as an identifier to be searched for in the database. It is
+ passed as an argument to the default search program (normally
+ `lid', but `aid' is used if the `-a' option was given when `iid'
+ was started). The result of this operation is a set of file
+ names, and it is assigned a unique set number.
+
+`lid'
+ `lid' is a keyword. It is used to invoke `lid' with the list of
+ identifiers following it as arguments. This forces the use of `lid'
+ regardless of the state of the `-a' option (*note Lid::.).
+
+`aid'
+ The `aid' keyword is like the `lid' keyword, but it forces the use
+ of the `aid' program (*note Aid::.).
+
+`match'
+ The `match' operator invokes the `pid' program to do pattern
+ matching on file names rather than identifiers. The set generated
+ contains the file names that match the specified patterns (*note
+ Pid::.).
+
+`or'
+ The `or' operator takes two sets of file names as arguments and
+ generates a new set containing all the files from both sets.
+
+`and'
+ The `and' operator takes two sets of file names and generates a new
+ set containing only files from both sets.
+
+`not'
+ The `not' operator inverts a set of file names, producing the set
+ of all files not in the input set.
+
+`set number'
+ A set number consists of the letter `s' followed immediately by a
+ number. This refers to one of the sets created by a previous
+ query operation. During one `iid' session, each query generates a
+ unique set number, so any previously generated set may be used as
+ part of any new query by referring to the set number.
+
+ The `not' operator has the highest precedence with `and' coming in
+the middle and `or' having the lowest precedence. The operator names
+are recognized using case insensitive matching, so `AND', `and', and
+`aNd' are all the same as far as `iid' is concerned. If you wish to use
+a keyword as an operand to one of the query programs, you must enclose
+it in quotes. Any patterns containing shell special characters must
+also be properly quoted or escaped, since the query commands are run by
+invoking them with the shell.
+
+ Summary of query expression syntax:
+
+ A <query> is:
+ <set number>
+ <identifier>
+ lid <identifier list>
+ aid <identifier list>
+ match <wild card list>
+ <query> or <query>
+ <query> and <query>
+ not <query>
+ ( <query> )
+
+
+File: mkid.info, Node: Sets, Next: Show, Prev: Ss and Files commands, Up: Iid
+
+Sets
+====
+
+ - Subcommand: sets
+
+ The `sets' command displays all the sets created so far. Each one is
+described by the query command that generated it.
+
+
+File: mkid.info, Node: Show, Next: Begin, Prev: Sets, Up: Iid
+
+Show
+====
+
+ - Subcommand: show SET
+
+ - Subcommand: p SET
+
+ The `show' and `p' commands are equivalent. They both accept a set
+number as an argument and run the program given in the `PAGER'
+environment variable with the file names in that set as arguments.
+
+
+File: mkid.info, Node: Begin, Next: Help, Prev: Show, Up: Iid
+
+Begin
+=====
+
+ - Subcommand: begin DIRECTORY
+
+ - Subcommand: b DIRECTORY
+
+ The `begin' command (and its abbreviated version `b') is used to
+begin a new `iid' session in a different directory (which presumably
+contains a different database). It flushes all the sets created so far
+and switches to the specified directory. It is equivalent to exiting
+`iid', changing directories in the shell, and running `iid' again.
+
+
+File: mkid.info, Node: Help, Next: Off, Prev: Begin, Up: Iid
+
+Help
+====
+
+ - Subcommand: help
+
+ - Subcommand: h
+
+ - Subcommand: ?
+
+ The `help', `h', and `?' command are three different ways to ask for
+help. They all invoke the `PAGER' program to display a short help file.
+
+
+File: mkid.info, Node: Off, Next: Shell Commands as Queries, Prev: Help, Up: Iid
+
+Off
+===
+
+ - Subcommand: off
+
+ - Subcommand: quit
+
+ - Subcommand: q
+
+ These three command (or just an end of file) all cause `iid' to exit.
+
+
+File: mkid.info, Node: Shell Commands as Queries, Next: Shell Escape, Prev: Off, Up: Iid
+
+Shell Commands as Queries
+=========================
+
+ When the first word on an `iid' command is not recognized as a
+builtin `iid' command, `iid' assumes the command is a shell command
+which will write a list of file names to STDOUT. This list of file
+names is used to generate a new set of files.
+
+ Any set numbers that appear as arguments to this command are expanded
+into lists of file names prior to running the command.
+
+
+File: mkid.info, Node: Shell Escape, Prev: Shell Commands as Queries, Up: Iid
+
+Shell Escape
+============
+
+ If a command starts with a bang (`!') character, the remainder of
+the line is run as a shell command. Any set numbers that appear as
+arguments to this command are expanded into lists of file names prior to
+running the command.
+
+
+File: mkid.info, Node: Other Tools, Next: Command Index, Prev: Iid, Up: Top
+
+Other Tools
+***********
+
+ This chapter describes some support tools that work with the other ID
+programs.
+
+* Menu:
+
+* GNU Emacs Interface:: Using gid.el
+* Fid:: List identifiers in a file.
+* Idx:: Extract identifiers from source file.
+
+
+File: mkid.info, Node: GNU Emacs Interface, Next: Fid, Prev: Other Tools, Up: Other Tools
+
+GNU Emacs Interface
+===================
+
+ The source distribution comes with a file named `gid.el'. This is a
+GNU emacs interface to the `gid' tool. If you put the file where emacs
+can find it (somewhere in your `EMACSLOADPATH') and put `(autoload 'gid
+"gid" nil t)' in your `.emacs' file, you will be able to invoke the
+`gid' function using `M-x gid'.
+
+ This function prompts you with the word the cursor is on. If you want
+to search for a different pattern, simply delete the line and type the
+pattern of interest.
+
+ It runs `gid' in a `*compilation*' buffer, so the normal
+`next-error' function can be used to visit all the places the
+identifier is found (*note Compilation: (emacs)Compilation.).
+
+
+File: mkid.info, Node: Fid, Next: Idx, Prev: GNU Emacs Interface, Up: Other Tools
+
+Fid
+===
+
+ - Command: fid [`-f<FILE>'] FILE1 [FILE2]
+ `-f<FILE>'
+ Look in the named database.
+
+ `FILE1'
+ List the identifiers contained in file1 according to the
+ database.
+
+ `FILE2'
+ If a second file is given, list only the identifiers both
+ files have in common.
+
+ The `fid' program provides an inverse query. Instead of listing
+files containing some identifier, it lists the identifiers found in a
+file.
+
+
+File: mkid.info, Node: Idx, Prev: Fid, Up: Other Tools
+
+Idx
+===
+
+ - Command: idx [`-s<DIRECTORY>'] [`-r<DIRECTORY>'] [`-S<SCANARG>']
+ FILES...
+ The `-s', `-r', and `-S' arguments to `idx' are identical to the
+ same arguments on `mkid' (*note Mkid Command Line Options::.).
+
+ The `idx' command is more of a test frame for scanners than a tool
+designed to be independently useful. It takes the same scanner arguments
+as `mkid', but rather than building a database, it prints the
+identifiers found to STDOUT, one per line. You can use it to try out a
+scanner on a sample file to make sure it is extracting the identifiers
+you believe it should extract.
+
+
+File: mkid.info, Node: Command Index, Prev: Other Tools, Up: Top
+
+Command Index
+*************
+
+* Menu:
+
+* ?: Help.
+* aid: Aid.
+* b: Begin.
+* begin: Begin.
+* eid: Eid.
+* f: Ss and Files commands.
+* fid: Fid.
+* files: Ss and Files commands.
+* gid: Gid.
+* h: Help.
+* help: Help.
+* idx: Idx.
+* iid: Iid.
+* lid: Lid.
+* mkid: Mkid Command Line Options.
+* off: Off.
+* p: Show.
+* pid: Pid.
+* q: Off.
+* quit: Off.
+* sets: Sets.
+* show: Show.
+* ss: Ss and Files commands.
+
+
+
+Tag Table:
+Node: Top913
+Node: Overview1298
+Node: History2862
+Node: Mkid5027
+Node: Mkid Command Line Options6363
+Node: Scanner Arguments8124
+Node: Builtin Scanners10479
+Node: C11144
+Node: Plain Text12039
+Node: Assembler13107
+Node: Adding Your Own Scanner14295
+Node: Mkid Examples16272
+Node: Database Query Tools18249
+Node: Common Options19190
+Node: Patterns22906
+Node: Lid24148
+Node: Aid24570
+Node: Gid25101
+Node: Eid25721
+Node: Pid26845
+Node: Iid27735
+Node: Ss and Files commands29605
+Node: Sets33068
+Node: Show33308
+Node: Begin33636
+Node: Help34123
+Node: Off34404
+Node: Shell Commands as Queries34634
+Node: Shell Escape35160
+Node: Other Tools35502
+Node: GNU Emacs Interface35879
+Node: Fid36685
+Node: Idx37237
+Node: Command Index37912
+
+End Tag Table
diff --git a/mkid.texinfo b/mkid.texinfo
new file mode 100644
index 0000000..e1fcc3f
--- /dev/null
+++ b/mkid.texinfo
@@ -0,0 +1,953 @@
+\input texinfo @c -*-texinfo-*-
+@comment %**start of header (This is for running Texinfo on a region.)
+@setfilename mkid.info
+@settitle The ID Database
+@setchapternewpage odd
+@comment %**end of header (This is for running Texinfo on a region.)
+
+@include version.texi
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* mkid: (mkid). Identifier database utilities
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+
+@ifinfo
+This file documents the @code{mkid} identifier database utilities.
+
+Copyright (C) 1991 Tom Horsley
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation.
+@end ifinfo
+
+@titlepage
+@title The MKID Identifier Database, version @value{VERSION}
+@subtitle A Simple, Fast, High-Capacity Cross-Referencer
+@subtitle lid, gid, aid, eid, pid, iid
+@author by Tom Horsley
+
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1991 Tom Horsley
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation.
+@end titlepage
+
+@node Top, Overview, (dir), (dir)
+
+@menu
+* Overview:: What is an ID database and what tools manipulate it?
+* Mkid:: Mkid
+* Database Query Tools:: Database Query Tools
+* Iid:: Iid
+* Other Tools:: Other Tools
+* Command Index:: Command Index
+@end menu
+
+@node Overview, Mkid, Top, Top
+@chapter Overview
+@cindex Reference to First Chapter
+An ID database is simply a file containing a list of file names, a list of
+identifiers, and a binary relation (stored as a bit matrix) indicating which
+of the identifiers appear in each file. With this database and some tools
+to manipulate the data, a host of tasks become simpler and faster. You can
+@code{grep} through hundreds of files for a name, skipping the files that
+don't contain the name. You can search for all the memos containing
+references to a project. You can edit every file that calls some function,
+adding a new required argument. Anyone with a large software project to
+maintain, or a large set of text files to organize can benefit from the ID
+database and the tools that manipulate it.
+
+There are several programs in the ID family. The @code{mkid} program
+scans the files, finds the identifiers and builds the ID database. The
+@code{lid} and @code{aid} tools are used to generate lists of file names
+containing an identifier (perhaps to recompile every file that
+references a macro which just changed). The @code{eid} program will
+invoke an editor on each of the files containing an identifier and the
+@code{gid} program will @code{grep} for an identifier in the subset of
+files known to contain it. The @code{pid} tool is used to query the
+path names of the files in the database (rather than the contents).
+Finally, the @code{iid} tool is an interactive program supporting
+complex queries to intersect and join sets of file names.
+
+@menu
+* History:: History
+@end menu
+
+@node History, , Overview, Overview
+@section History
+Greg McGary conceived of the ideas behind mkid when he began hacking
+the UNIX kernel in 1984. He needed a navigation tool to help him find
+his way the expansive, unfamiliar landscape. The first mkid-like tools
+were built with shell scripts, and produced an ascii database that looks
+much like the output of `lid' with no arguments. It took over an hour
+on a VAX 11/750 to build a database for a 4.1BSDish kernel. Lookups were
+done with the UNIX command @code{look}, modified to handle very long lines.
+
+In 1986, Greg rewrote mkid, lid, fid and idx in C to improve
+performance. Database-build times were shortened by an order of
+magnitude. The mkid tools were first posted to @file{comp.sources.unix}
+September of 1987.
+
+Over the next few years, several versions diverged from the original
+source. Tom Horsley at Harris Computer Systems Division stepped forward
+to take over maintenance and integrated some of the fixes from divergent
+versions. He also wrote the @code{iid} program. A pre-release of
+@code{mkid2} was posted to @file{alt.sources} near the end of 1990. At
+that time Tom wrote this texinfo manual with the encouragement the net
+community. (Tom thanks Doug Scofield and Bill Leonard whom I dragooned
+into helping me poorf raed and edit --- they found several problems in
+the initial version.)
+
+In January, 1995, Greg McGary reemerged as the primary maintaner and is
+hereby launching @code{mkid-3} whose primary new feature is an efficient
+algorithm for building databases that is linear over the size of the
+input text for both time and space. (The old algorithm was quadratic
+for space and choked on very large source trees.) The code is now under
+GPL and might become a part of the GNU system. @code{Mkid-3} is an
+interim release, since several significant enhacements are in the works.
+These include an optional coupling with GNU grep, so that grep can use
+an ID database for hints; a cscope work-alike query interface;
+incremental update of the ID database; and an automatic file-tree walker
+so you need not explicitly supply every file name argument to
+the @code{mkid} program.
+
+@node Mkid, Database Query Tools, Overview, Top
+@chapter Mkid
+The @code{mkid} program builds the ID database. To do this it must scan
+each of the files included in the database. This takes some time, but
+once the work is done the query programs run very rapidly.
+
+The @code{mkid} program knows how to scan a variety of of files. For
+example, it knows how to skip over comments and strings in a C program,
+only picking out the identifiers used in the code.
+
+Identifiers are not the only thing included in the database.
+Numbers are also scanned and included in the database indexed by
+their binary value. Since the same number can be written many
+different ways (47, 0x2f, 057 in a C program for instance), this
+feature allows you to find hard coded uses of constants without
+regard to the radix used to specify them.
+
+All the places in this document where identifiers are written about
+should really mention identifiers and numbers, but that gets fairly
+clumsy after a while, so you should always keep in mind that numbers are
+included in the database as well as identifiers.
+
+@menu
+* Mkid Command Line Options:: Mkid Command Line Options
+* Builtin Scanners:: Builtin Scanners
+* Adding Your Own Scanner:: Adding Your Own Scanner
+* Mkid Examples:: Mkid Examples
+@end menu
+
+@node Mkid Command Line Options, Builtin Scanners, Mkid, Mkid
+@section Mkid Command Line Options
+@deffn Command mkid [@code{-v}] [@code{-S@var{scanarg}}] [@code{-a@var{arg-file}}] [@code{-}] [@code{-f@var{out-file}}] [@code{-u}] [@code{files}@dots{}]
+@table @code
+@item -v
+Verbose. Mkid tells you as it scans each file and indicates which scanner
+it is using. It also summarizes some statistics about the database at
+the end.
+@item -S@var{scanarg}
+The @code{-S} option is used to specify arguments to the various language
+scanners. @xref{Scanner Arguments}, for details.
+@item -a@var{arg-file}
+Name a file containing additional command line arguments (one per line). This
+may be used to specify lists of file names longer than will fit on a command
+line.
+@item -
+A simple @code{-} by itself means read arguments from stdin.
+@item -f@var{out-file}
+Specify the name of the database file to create. The default name is @code{ID}
+(in the current directory), but you may specify any name. The file names
+stored in the database will be stored relative to the directory containing
+the database, so if you move the database after creating it, you may have
+trouble finding files unless they remain in the same relative position.
+@item -u
+The @code{-u} option updates an existing database by rescanning any files
+that have changed since the database was written. Unfortunately you cannot
+incrementally add new files to a database.
+@item files
+Remaining arguments are names of files to be scanned and included in the
+database.
+@end table
+@end deffn
+
+@menu
+* Scanner Arguments:: Scanner Arguments
+@end menu
+
+@node Scanner Arguments, , Mkid Command Line Options, Mkid Command Line Options
+@subsection Scanner Arguments
+Scanner arguments all start with @code{-S}. Scanner arguments are used to tell
+@code{mkid} which language scanner to use for which files, to pass language
+specific options to the individual scanners, and to get some limited
+online help about scanner options.
+
+@code{Mkid} usually determines which language scanner to use on a file
+by looking at the suffix of the file name. The suffix starts at the last
+@samp{.} in a file name and includes the @samp{.} and all remaining
+characters (for example the suffix of @file{fred.c} is @file{.c}). Not
+all files have a suffix, and not all suffixes are bound to a specific
+language by mkid. If @code{mkid} cannot determine what language a file
+is, it will use the language bound to the @file{.default} suffix. The
+plain text scanner is normally bound to @file{.default}, but the
+@code{-S} option can be used to change any language bindings.
+
+There are several different forms for scanner options:
+@table @code
+@item -S.@var{<suffix>}=@var{<language>}
+@code{Mkid} determines which language scanner to use on a file by examining the
+file name suffix. The @samp{.} is part of the suffix and must be specified
+in this form of the @code{-S} option. For example @samp{-S.y=c} tells
+@code{mkid} to use the @samp{c} language scanner for all files ending in
+the @samp{.y} suffix.
+@item -S.@var{<suffix>}=?
+@code{Mkid} has several built in suffixes it already recognizes. Passing
+a @samp{?} will cause it to print the language it will use to scan files
+with that suffix.
+@item -S?=@var{<language>}
+This form will print which suffixes are scanned with the given language.
+@item -S?=?
+This prints all the suffix@expansion{}language bindings recognized by
+@code{mkid}.
+@item -S@var{<language>}-@var{<arg>}
+Each language scanner accepts scanner dependent arguments. This form of the
+@code{-S} option is used to pass arbitrary arguments to the language scanners.
+@item -S@var{<language>}?
+Passing a @samp{?} instead of a language option will print a brief summary
+of the options recognized by the specified language scanner.
+@item -S@var{<new language>}/@var{<builtin language>}/@var{<filter command>}
+This form specifies a new language defined in terms of a builtin language
+and a shell command that will be used to filter the file prior to passing
+on to the builtin language scanner.
+@end table
+
+@node Builtin Scanners, Adding Your Own Scanner, Mkid Command Line Options, Mkid
+@section Builtin Scanners
+If you run @code{mkid -S?=?} you will find bindings for a number of
+languages; unfortunately pascal, though mentioned in the list, is not
+actually supported. The supported languages are documented below
+@footnote{This is not strictly true --- vhil is a supported language, but
+it is an obsolete and arcane dialect of C and should be ignored}.
+
+@menu
+* C:: C
+* Plain Text:: Plain Text
+* Assembler:: Assembler
+@end menu
+
+@node C, Plain Text, Builtin Scanners, Builtin Scanners
+@subsection C
+
+The C scanner is probably the most popular. It scans identifiers out of
+C programs, skipping over comments and strings in the process. The
+normal @file{.c} and @file{.h} suffixes are automatically recognized as
+C language, as well as the more obscure @file{.y} (yacc) and @file{.l}
+(lex) suffixes.
+
+The @code{-S} options recognized by the C scanner are:
+
+@table @code
+@item -Sc-s@var{<character>}
+Allow the specified @var{<character>} in identifiers (some dialects of
+C allow @code{$} in identifiers, so you could say @code{-Sc-s$} to
+accept that dialect).
+@item -Sc-u
+Don't strip leading underscores from identifier names (this is the default
+mode of operation).
+@item -Sc+u
+Do strip leading underscores from identifier names (I don't know why you
+would want to do this in C programs, but the option is available).
+@end table
+
+@node Plain Text, Assembler, C, Builtin Scanners
+@subsection Plain Text
+The plain text scanner is designed for scanning documents. This is
+typically the scanner used when adding custom scanners, and several
+custom scanners are built in to @code{mkid} and defined in terms of filters
+and the text scanner. A troff scanner runs @code{deroff} over the file
+then feeds the result to the text scanner. A compressed man page scanner
+runs @code{pcat} piped into @code{col -b}, and a @TeX{} scanner runs
+@code{detex}.
+
+Options:
+
+@table @code
+@item -Stext+a@var{<character>}
+Include the specified character in identifiers. By default, standard
+C identifiers are recognized.
+@item -Stext-a@var{<character>}
+Exclude the specified character from identifiers.
+@item -Stext+s@var{<character>}
+Squeeze the specified character out of identifiers. By default, the
+characters @samp{'}, @samp{-}, and @samp{.} are squeezed out of identifiers.
+This generates transformations like @var{fred's}@expansion{}@var{freds} or
+@var{a.s.p.c.a.}@expansion{}@var{aspca}.
+@item -Stext-s@var{<character>}
+Do not squeeze out the specified character.
+@end table
+
+@node Assembler, , Plain Text, Builtin Scanners
+@subsection Assembler
+Assemblers come in several flavors, so there are several options to
+control scanning of assembly code:
+
+@table @code
+@item -Sasm-c@var{<character>}
+The specified character starts a comment that extends to end of line
+(in many assemblers this is a semicolon or number sign --- there is
+no default value for this).
+@item -Sasm+u
+Strip the leading underscores off identifiers (the default behavior).
+@item -Sasm-u
+Do not strip the leading underscores.
+@item -Sasm+a@var{<character>}
+The specified character is allowed in identifiers.
+@item -Sasm-a@var{<character>}
+The specified character is allowed in identifiers, but any identifier
+containing that character is ignored (often a @samp{.} or @samp{@@}
+will be used to indicate an internal temp label, you may want to
+ignore these).
+@item -Sasm+p
+Recognize C preprocessor directives in assembler source (default).
+@item -Sasm-p
+Do not recognize C preprocessor directives in assembler source.
+@item -Sasm+C
+Skip over C style comments in assembler source (default).
+@item -Sasm-C
+Do not skip over C style comments in assembler source.
+@end table
+
+@node Adding Your Own Scanner, Mkid Examples, Builtin Scanners, Mkid
+@section Adding Your Own Scanner
+
+There are two ways to add new scanners to @code{mkid}. The first is to
+modify the code in @file{getscan.c} and add a new @file{scan-*.c} file
+with the code for your scanner. This is not too hard, but it requires
+relinking and installing a new version of @code{mkid}, which might be
+inconvenient, and would lead to the proliferation of @code{mkid}
+versions.
+
+The second technique uses the @code{-S<lang>/<lang>/<filter>} form
+of the @code{-S} option to specify a new language scanner. In this form
+the first language is the name of the new language to be defined,
+the second language is the name of an existing language scanner to
+be invoked on the output of the filter command specified as the
+third component of the @code{-S} option.
+
+The filter is an arbitrary shell command. Somewhere in the filter string,
+a @code{%s} should occur. This @code{%s} is replaced by the name of the
+source file being scanned, the shell command is invoked, and whatever
+comes out on @var{stdout} is scanned using the builtin scanner.
+
+For example, no scanner is provided for texinfo files (like this one).
+If I wished to index the contents of this file, but avoid indexing the
+texinfo directives, I would need a filter that stripped out the texinfo
+directives, but left the remainder of the file intact. I could then use
+the plain text scanner on the remainder. A quick way to specify this
+might be:
+
+@example
+'-S/texinfo/text/sed s,@@[a-z]*,,g < %s'
+@end example
+
+This defines a new language scanner (@var{texinfo}) defined in terms of
+a @code{sed} command to strip out texinfo directives (at signs followed
+by letters). Once the directives are stripped, the remaining text is run
+through the plain text scanner.
+
+This is just an example, to do a better job I would actually need to
+delete some lines (such as those beginning with @code{@@end}) as well
+as deleting the @code{@@} directives embedded in the text.
+
+@node Mkid Examples, , Adding Your Own Scanner, Mkid
+@section Mkid Examples
+
+The simplest example of @code{mkid} is something like:
+
+@example
+mkid *.[chy]
+@end example
+
+This will build an ID database indexing all the
+identifiers and numbers in the @file{.c}, @file{.h}, and @file{.y} files
+in the current directory. Because those suffixes are already known to
+@code{mkid} as C language files, no other special arguments are required.
+
+From a simple example, lets go to a more complex one. Suppose you want
+to build a database indexing the contents of all the @var{man} pages.
+Since @code{mkid} already knows how to deal with @file{.z} files, let's
+assume your system is using the @code{compress} program to store
+compressed cattable versions of the @var{man} pages. The
+@code{compress} program creates files with a @code{.Z} suffix, so
+@code{mkid} will have to be told how to scan @file{.Z} files. The
+following code shows how to combine the @code{find} command with the
+special scanner arguments to @code{mkid} to generate the required ID
+database:
+
+@example
+cd /usr/catman
+find . -name '*.Z' -print | mkid '-Sman/text/uncompress -c < %s' -S.Z=man -
+@end example
+
+This example first switches to the @file{/usr/catman} directory where
+the compressed @var{man} pages are stored. The @code{find} command then
+finds all the @file{.Z} files under that directory and prints their
+names. This list is piped into the @code{mkid} program. The @code{-}
+argument by itself (at the end of the line) tells @code{mkid} to read
+arguments (in this case the list of file names) from @var{stdin}. The
+first @code{-S} argument defines a new language (@var{man}) in terms of
+the @code{uncompress} utility and the existing text scanner. The second
+@code{-S} argument tells @code{mkid} to treat all @file{.Z} files as
+language @var{man}. In practice, you might find the @code{mkid}
+arguments need to be even more complex, something like:
+
+@example
+mkid '-Sman/text/uncompress -c < %s | col -b' -S.Z=man -
+@end example
+
+This will take the additional step of getting rid of any underlining and
+backspacing which might be present in the compressed @var{man} pages.
+
+@node Database Query Tools, Iid, Mkid, Top
+@chapter Database Query Tools
+
+The ID database is useless without database query tools. The remainder
+of this document describes those tools.
+
+The @code{lid}, @code{gid},
+@code{aid}, @code{eid}, and @code{pid} programs are all the same program
+installed with links to different names. The name used to invoke the
+program determines how it will act.
+
+The @code{iid} program is an interactive query shell that sits on top
+of the other query tools.
+
+@menu
+* Common Options:: Common command line options
+* Patterns:: Identifier pattern matching
+* Lid:: Look up identifiers
+* Aid:: Case insensitive lid
+* Gid:: Grep for identifiers
+* Eid:: Edit files with matching identifiers
+* Pid:: Look up path names in database
+@end menu
+
+@node Common Options, Patterns, Database Query Tools, Database Query Tools
+@section Common Options
+
+Since many of the programs are really links to one common program, it
+is only reasonable to expect that most of the query tools would share
+common command line options. Not all options make sense for all programs,
+but they are all described here. The description of each program
+gives the options that program uses.
+
+@table @code
+@item -f@var{<file>}
+Read the database specified by @var{<file>}. Normally the tools look
+for a file named @file{ID} in either the current directory or in any
+of the directories above the current directory. This means you can keep
+a global @file{ID} database in the root of a large source tree and use
+the query tools from anywhere within that tree.
+@item -r@var{<directory>}
+The query tools usually assume the file names in the database are relative
+to the directory holding the database. The @code{-r} option tells the
+tools to look for the files relative to @var{<directory>} regardless
+of the location of the database.
+@item -c
+This is shorthand for @code{-r`pwd`}. It tells the query tools to assume
+the file names are stored relative to the current working directory.
+@item -e
+Force the pattern arguments to be treated as regular expressions.
+Normally the query tools attempt to guess if the patterns are regular
+expressions or simple identifiers by looking for special characters
+in the pattern.
+@item -w
+Force the pattern arguments to be treated as simple words even if
+they contain special regular expression characters.
+@item -k
+Normally the query tools that generate lists of file names attempt to
+compress the lists using the @code{csh} brace notation. This option
+suppresses the file name compression and outputs each name in full.
+(This is particularly useful if you are a @code{ksh} user and want to
+feed the list of names to another command --- the @code{-k} option
+comes from the @code{k} in @code{ksh}).
+@item -g
+It is possible to build the query tools so the @code{-k} option is the
+default behavior. If this is the case for your system, the @code{-g}
+option turns on the globbing of file names using the @code{csh} brace
+notation.
+@item -n
+Normally the query tools that generate lists of file names also list
+the matching identifier at the head of the list of names. This is
+irritating if you want just a list of names to feed to another command,
+so the @code{-n} option suppresses the identifier and lists only
+file names.
+@item -b
+This option is only used by the @code{pid} tool. It restricts @code{pid}
+to pattern match only the basename part of a file name. Normally the
+absolute file name is matched against the pattern.
+@item -d -o -x -a
+These options may be used in any combination to limit the radix of
+numeric matches. The @code{-d} option will allow matches on decimal
+numbers, @code{-o} on octal, and @code{-x} on hexadecimal numbers.
+The @code{-a} option is shorthand for specifying all three. Any
+combination of these options may be used.
+@item -m
+Merge multiple lines of output into a single line. (If your query
+matches more than one identifier the default action is to generate
+a separate line of output for each matching identifier).
+@item -s
+Search for identifiers that appear only once in the database. This
+helps to locate identifiers that are defined but never used.
+@item -u@var{<number>}
+List identifiers that conflict in the first @var{<number>} characters.
+This could be useful porting programs to brain-dead computers that
+refuse to support long identifiers, but your best long term option
+is to set such computers on fire.
+@end table
+
+@node Patterns, Lid, Common Options, Database Query Tools
+@section Patterns
+
+You can attempt to match either simple identifiers or numbers in a
+query, or you can specify a regular expression pattern which may
+match many different identifiers in the database. The query
+programs use either @var{regex} and @var{regcmp} or @var{re_comp}
+and @var{re_exec}, depending on which one is available in the library
+on your system. These might not always support the exact same
+regular expression syntax, so consult your local @var{man} pages
+to find out. Any regular expression routines should support the following
+syntax:
+
+@table @code
+@item .
+A dot matches any character.
+@item [ ]
+Brackets match any of the characters specified within the brackets. You
+can match any characters @emph{except} the ones in brackets by typing
+@code{^} as the first character. A range of characters can be specified
+using @code{-}.
+@item *
+An asterisk means repeat the previous pattern zero or more times.
+@item ^
+An @code{^} at the beginning of a pattern means the pattern must match
+starting at the first character of the identifier.
+@item $
+A @code{$} at the end of the pattern means the pattern must match ending
+at the last character in the identifier.
+@end table
+
+@node Lid, Aid, Patterns, Database Query Tools
+@section Lid
+
+@deffn Command lid [@code{-f@var{<file>}}] [@code{-u@var{<n>}}] [@code{-r@var{<dir>}}] [@code{-ewdoxamskgnc}] patterns@dots{}
+@end deffn
+
+The @code{lid} program stands for @var{lookup identifier}.
+It searches the database for any identifiers matching the patterns
+and prints the names of the files that match each pattern. The exact
+format of the output depends on the options.
+
+@node Aid, Gid, Lid, Database Query Tools
+@section Aid
+
+@deffn Command aid [@code{-f@var{<file>}}] [@code{-u@var{<n>}}] [@code{-r@var{<dir>}}] [@code{-doxamskgnc}] patterns@dots{}
+@end deffn
+
+The @code{aid} command is an abbreviation for @var{apropos identifier}.
+The patterns cannot be regular expressions, but it looks for them using
+a case insensitive match, and any pattern that is a substring of an
+identifier in the database will match that identifier.
+
+For example @samp{aid get} might match the identifiers @code{fgets},
+@code{GETLINE}, and @code{getchar}.
+
+@node Gid, Eid, Aid, Database Query Tools
+@section Gid
+
+@deffn Command gid [@code{-f@var{<file>}}] [@code{-u@var{<n>}}] [@code{-r@var{<dir>}}] [@code{-doxasc}] patterns@dots{}
+@end deffn
+
+The @code{gid} command stands for @var{grep for identifiers}. It finds
+identifiers in the database that match the specified patterns, then
+@code{greps} for those identifiers in just the set of files containing
+matches. In a large source tree, this saves a fantastic amount of time.
+
+There is an @var{emacs} interface to this program (@pxref{GNU Emacs Interface}).
+If you are an @var{emacs} user, you will probably prefer the @var{emacs}
+interface over the @code{eid} tool.
+
+@node Eid, Pid, Gid, Database Query Tools
+@section Eid
+
+@deffn Command eid [@code{-f@var{<file>}}] [@code{-u@var{<n>}}] [@code{-r@var{<dir>}}] [@code{-doxasc}] patterns@dots{}
+@end deffn
+
+The @code{eid} command allows you to invoke an editor on each file containing
+a matching pattern. The @code{EDITOR} environment variable is the name of the
+program to be invoked. If the specified editor can accept an initial search
+argument on the command line, you can use the @code{EIDARG}, @code{EIDLDEL},
+and @code{EIDRDEL} environment variables to specify the form of that argument.
+
+@table @code
+@item EDITOR
+The name of the editor program to invoke.
+@item EIDARG
+A printf string giving the form of the argument to pass containing the
+initial search string (the matching identifier). For @code{vi}
+it should be set to @samp{+/%s/'}.
+@item EIDLDEL
+A string giving the regular expression pattern that forces a match at
+the beginning (left end) of a word. This string is inserted in front
+of the matching identifier when composing the search argument. For @code{vi},
+this should be @samp{\<}.
+@item EIDRDEL
+The matching right end word delimiter. For @code{vi}, use @samp{\>}.
+@end table
+
+@node Pid, , Eid, Database Query Tools
+@section Pid
+
+@deffn Command pid [@code{-f@var{<file>}}] [@code{-u@var{<n>}}] [@code{-r@var{<dir>}}] [@code{-ebkgnc}] patterns@dots{}
+@end deffn
+
+The @code{pid} tool is unlike all the other tools. It matches the
+patterns against the file names in the database rather than the
+identifiers in the database. Patterns are treated as shell wild card
+patterns unless the @code{-e} option is given, in which case full
+regular expression matching is done.
+
+The wild card pattern is matched against the absolute path name of the
+file. Most shells treat slashes @samp{/} and file names that start with
+dot @samp{.} specially, @code{pid} does not do this. It simply attempts
+to match the absolute path name string against the wild card pattern.
+
+The @code{-b} option restricts the pattern matching to the base name of
+the file (all the leading directory names are stripped prior to pattern
+matching).
+
+@node Iid, Other Tools, Database Query Tools, Top
+@chapter Iid
+
+@deffn Command iid [@code{-a}] [@code{-c@var{<command>}}] [@code{-H}]
+@table @code
+@item -a
+Normally @code{iid} uses the @code{lid} command to search for names.
+If you give the @code{-a} option on the command line, then it will
+use @code{aid} as the default search engine.
+@item -c@var{<command>}
+In normal operation, @code{iid} starts up and prompts you for commands
+used to build sets of files. The @code{-c} option is used to pass a
+single query command to @code{iid} which it then executes and exits.
+@item -H
+The @code{-H} option prints a short help message and exits. To get more
+help use the @code{help} command from inside @code{iid}.
+@end table
+@end deffn
+
+The @code{iid} program is an interactive ID query tool. It operates by
+running the other query programs (such as @code{lid} and @code{aid})
+and creating sets of file names returned by these queries. It also
+provides operators for @code{anding} and @code{oring} these sets to
+create new sets.
+
+The @code{PAGER} environment variable names the program @code{iid} uses
+to display files. If you use @code{emacs}, you might want to set
+@code{PAGER} so it invokes the @code{emacsclient} program. Check the
+file @file{lisp/server.el} in the emacs source tree for documentation on
+this. It is useful not only with X windows, but also when running
+@code{iid} from an emacs shell buffer. There is also a somewhat spiffier
+version called gnuserv by Andy Norman
+(@code{ange%anorman@@hplabs.hp.com}) which appeared in @file{comp.emacs}
+sometime in 1989.
+
+@menu
+* Ss and Files commands:: Ss and Files commands
+* Sets:: Sets
+* Show:: Show
+* Begin:: Begin
+* Help:: Help
+* Off:: Off
+* Shell Commands as Queries:: Shell Commands as Queries
+* Shell Escape:: Shell Escape
+@end menu
+
+@node Ss and Files commands, Sets, Iid, Iid
+@section Ss and Files commands
+
+The primary query commands are @code{ss} (for select sets) and @code{files}
+(for show file names). These commands both take a query expression as an
+argument.
+
+@deffn Subcommand ss query
+The @code{ss} command runs a query and builds a set (or sets) of file names. The
+result is printed as a summary of the sets constructed showing how many file
+names are in each set.
+@end deffn
+
+@deffn Subcommand files query
+The @code{files} command is like the @code{ss} command, but rather than printing
+a summary, it displays the full list of matching file names.
+@end deffn
+
+@deffn Subcommand f query
+The @code{f} command is merely a shorthand notation for @code{files}.
+@end deffn
+
+Database queries are simple expressions with operators like @code{and}
+and @code{or}. Parentheses can be used to group operations. The complete
+set of operators is summarized below:
+
+@table @code
+@item @var{pattern}
+Any pattern not recognized as one of the keywords in this table is treated
+as an identifier to be searched for in the database. It is passed as an
+argument to the default search program (normally @code{lid}, but @code{aid}
+is used if the @code{-a} option was given when @code{iid} was started).
+The result of this operation is a set of file names, and it is assigned a
+unique set number.
+@item lid
+@code{lid} is a keyword. It is used to invoke @code{lid} with the list of
+identifiers following it as arguments. This forces the use of @code{lid}
+regardless of the state of the @code{-a} option (@pxref{Lid}).
+@item aid
+The @code{aid} keyword is like the @code{lid} keyword, but it forces the
+use of the @code{aid} program (@pxref{Aid}).
+@item match
+The @code{match} operator invokes the @code{pid} program to do pattern
+matching on file names rather than identifiers. The set generated contains
+the file names that match the specified patterns (@pxref{Pid}).
+@item or
+The @code{or} operator takes two sets of file names as arguments and generates
+a new set containing all the files from both sets.
+@item and
+The @code{and} operator takes two sets of file names and generates a new
+set containing only files from both sets.
+@item not
+The @code{not} operator inverts a set of file names, producing the set of
+all files not in the input set.
+@item set number
+A set number consists of the letter @code{s} followed immediately by a number.
+This refers to one of the sets created by a previous query operation. During
+one @code{iid} session, each query generates a unique set number, so any
+previously generated set may be used as part of any new query by referring
+to the set number.
+@end table
+
+The @code{not} operator has the highest precedence with @code{and}
+coming in the middle and @code{or} having the lowest precedence. The
+operator names are recognized using case insensitive matching, so
+@code{AND}, @code{and}, and @code{aNd} are all the same as far as
+@code{iid} is concerned. If you wish to use a keyword as an operand to
+one of the query programs, you must enclose it in quotes. Any patterns
+containing shell special characters must also be properly quoted or
+escaped, since the query commands are run by invoking them with the
+shell.
+
+Summary of query expression syntax:
+
+@example
+A <query> is:
+ <set number>
+ <identifier>
+ lid <identifier list>
+ aid <identifier list>
+ match <wild card list>
+ <query> or <query>
+ <query> and <query>
+ not <query>
+ ( <query> )
+@end example
+
+@node Sets, Show, Ss and Files commands, Iid
+@section Sets
+
+@deffn Subcommand sets
+@end deffn
+
+The @code{sets} command displays all the sets created so far. Each one
+is described by the query command that generated it.
+
+@node Show, Begin, Sets, Iid
+@section Show
+
+@deffn Subcommand show set
+@end deffn
+
+@deffn Subcommand p set
+@end deffn
+
+The @code{show} and @code{p} commands are equivalent. They both accept
+a set number as an argument and run the program given in the @code{PAGER}
+environment variable with the file names in that set as arguments.
+
+@node Begin, Help, Show, Iid
+@section Begin
+
+@deffn Subcommand begin directory
+@end deffn
+
+@deffn Subcommand b directory
+@end deffn
+
+The @code{begin} command (and its abbreviated version @code{b}) is used
+to begin a new @code{iid} session in a different directory (which presumably
+contains a different database). It flushes all the sets created so far
+and switches to the specified directory. It is equivalent to exiting @code{iid},
+changing directories in the shell, and running @code{iid} again.
+
+@node Help, Off, Begin, Iid
+@section Help
+
+@deffn Subcommand help
+@end deffn
+
+@deffn Subcommand h
+@end deffn
+
+@deffn Subcommand ?
+@end deffn
+
+The @code{help}, @code{h}, and @code{?} command are three different ways to
+ask for help. They all invoke the @code{PAGER} program to display a short
+help file.
+
+@node Off, Shell Commands as Queries, Help, Iid
+@section Off
+
+@deffn Subcommand off
+@end deffn
+
+@deffn Subcommand quit
+@end deffn
+
+@deffn Subcommand q
+@end deffn
+
+These three command (or just an end of file) all cause @code{iid} to exit.
+
+@node Shell Commands as Queries, Shell Escape, Off, Iid
+@section Shell Commands as Queries
+
+When the first word on an @code{iid} command is not recognized as a
+builtin @code{iid} command, @code{iid} assumes the command is a shell
+command which will write a list of file names to @var{stdout}. This list
+of file names is used to generate a new set of files.
+
+Any set numbers that appear as arguments to this command are expanded
+into lists of file names prior to running the command.
+
+@node Shell Escape, , Shell Commands as Queries, Iid
+@section Shell Escape
+
+If a command starts with a bang (@code{!}) character, the remainder of
+the line is run as a shell command. Any set numbers that appear as
+arguments to this command are expanded into lists of file names prior to
+running the command.
+
+@node Other Tools, Command Index, Iid, Top
+@chapter Other Tools
+
+This chapter describes some support tools that work with the other ID
+programs.
+
+@menu
+* GNU Emacs Interface:: Using gid.el
+* Fid:: List identifiers in a file.
+* Idx:: Extract identifiers from source file.
+@end menu
+
+@node GNU Emacs Interface, Fid, Other Tools, Other Tools
+@section GNU Emacs Interface
+
+The source distribution comes with a file named @file{gid.el}. This is
+a GNU emacs interface to the @code{gid} tool. If you put the file where
+emacs can find it (somewhere in your @code{EMACSLOADPATH}) and put
+@code{(autoload 'gid "gid" nil t)} in your @file{.emacs} file, you will
+be able to invoke the @code{gid} function using @kbd{M-x gid}.
+
+This function prompts you with the word the cursor is on. If you want
+to search for a different pattern, simply delete the line and type the
+pattern of interest.
+
+It runs @code{gid} in a @code{*compilation*} buffer, so the normal
+@code{next-error} function can be used to visit all the places the
+identifier is found (@pxref{Compilation,,,emacs,The GNU Emacs Manual}).
+
+@node Fid, Idx, GNU Emacs Interface, Other Tools
+@section Fid
+
+@deffn Command fid [@code{-f@var{<file>}}] file1 [file2]
+@table @code
+@item -f@var{<file>}
+Look in the named database.
+@item @var{file1}
+List the identifiers contained in file1 according to the database.
+@item @var{file2}
+If a second file is given, list only the identifiers both files have
+in common.
+@end table
+@end deffn
+
+The @code{fid} program provides an inverse query. Instead of listing
+files containing some identifier, it lists the identifiers found in
+a file.
+
+@node Idx, , Fid, Other Tools
+@section Idx
+
+@deffn Command idx [@code{-s@var{<directory>}}] [@code{-r@var{<directory>}}] [@code{-S@var{<scanarg>}}] files@dots{}
+The @code{-s}, @code{-r}, and @code{-S} arguments to @code{idx}
+are identical to the same arguments on @code{mkid}
+(@pxref{Mkid Command Line Options}).
+@end deffn
+
+The @code{idx} command is more of a test frame for scanners than a tool
+designed to be independently useful. It takes the same scanner arguments
+as @code{mkid}, but rather than building a database, it prints the
+identifiers found to @var{stdout}, one per line. You can use it to try
+out a scanner on a sample file to make sure it is extracting the
+identifiers you believe it should extract.
+
+@node Command Index, , Other Tools, Top
+@unnumbered Command Index
+
+@printindex fn
+
+@contents
+@bye
diff --git a/scanners.c b/scanners.c
new file mode 100644
index 0000000..9020a1c
--- /dev/null
+++ b/scanners.c
@@ -0,0 +1,1209 @@
+/* scanners.c -- file & directory name manipulations
+ Copyright (C) 1986, 1995 Greg McGary
+ VHIL portions Copyright (C) 1988 Tom Horsley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "strxtra.h"
+#include "token.h"
+#include "alloc.h"
+#include "scanners.h"
+
+extern char const *program_name;
+
+static char const *get_token_VHIL (FILE *input_FILE, int *flags);
+static char const *get_token_c (FILE *input_FILE, int *flags);
+static void set_args_c (char const *lang_name, int op, char const *arg);
+static void set_ctype_c (char const *chars, int type);
+static void clear_ctype_c (char const *chars, int type);
+static void usage_c (char const *lang_name);
+
+static char const *get_token_asm (FILE *input_FILE, int *flags);
+static void set_ctype_asm (char const *chars, int type);
+static void clear_ctype_asm (char const *chars, int type);
+static void usage_asm (char const *lang_name);
+static void set_args_asm (char const *lang_name, int op, char const *arg);
+
+static char const *get_token_text (FILE *input_FILE, int *flags);
+static void set_ctype_text (char const *chars, int type);
+static void clear_ctype_text (char const *chars, int type);
+static void usage_text (char const *lang_name);
+static void set_args_text (char const *lang_name, int op, char const *arg);
+
+/****************************************************************************/
+
+struct language
+{
+ char const *lang_name;
+ char const *(*lang_get_token) (FILE *input_FILE, int *flags);
+ void (*lang_set_args) (char const *lang_name, int op, char const *arg);
+ char const *lang_filter;
+ struct language *lang_next;
+};
+
+struct suffix
+{
+ char const *suff_suffix;
+ char const *suff_lang_name;
+ struct language *suff_language;
+ struct suffix *suff_next;
+};
+
+static struct suffix *get_suffix_entry (char const *suffix);
+static struct language *get_lang_entry (char const *lang_name);
+static void usage_scan (void);
+
+struct language languages[] =
+{
+ /* must be sorted for bsearch(3) */
+ { "C", get_token_c, set_args_c, NULL },
+ { "TeX", get_token_text, set_args_text, NULL },
+ { "VHIL", get_token_VHIL, set_args_c, NULL },
+ { "asm", get_token_asm, set_args_asm, NULL },
+/*{ "elisp", get_token_elisp, set_args_elisp, NULL },*/
+ { "gzip", NULL, NULL, "zcat %s" },
+ { "roff", get_token_text, set_args_text, "sed '/^\\.so/d' < %s | deroff" },
+ { "text", get_token_text, set_args_text, NULL },
+};
+
+/*
+ This is a rather incomplete list of default associations
+ between suffixes and languages. You may add more to the
+ default list, or you may define them dynamically with the
+ `-S<suff>=<lang>' argument to mkid(1) and idx(1). e.g. to
+ associate a `.ada' suffix with the Ada language, use
+ `-S.ada=ada'
+*/
+struct suffix suffixes[] =
+{
+ { "", "text" },
+ { ".1", "roff" },
+ { ".2", "roff" },
+ { ".3", "roff" },
+ { ".4", "roff" },
+ { ".5", "roff" },
+ { ".6", "roff" },
+ { ".7", "roff" },
+ { ".8", "roff" },
+ { ".C", "C" },
+ { ".H", "C" },
+ { ".Z", "gzip" },
+ { ".c", "C" },
+ { ".cc", "C" },
+ { ".cpp", "C" },
+ { ".cxx", "C" },
+ { ".doc", "text" },
+/*{ ".el", "elisp" },*/
+ { ".gz", "gzip" },
+ { ".h", "C" },
+ { ".hh", "C" },
+ { ".hpp", "C" },
+ { ".hxx", "C" },
+ { ".l", "C" },
+ { ".lex", "C" },
+ { ".ltx", "TeX" },
+ { ".p", "pas" },
+ { ".pas", "pas" },
+ { ".s", "asm" },
+ { ".S", "asm" },
+ { ".tex", "TeX" },
+ { ".x", "VHIL" },
+ { ".y", "C" },
+ { ".yacc", "C" },
+ { ".z", "gzip" },
+};
+
+void
+init_scanners (void)
+{
+ struct language *lang;
+ struct language *lang_N = &languages[(sizeof(languages)/sizeof(languages[0])) - 1];
+ struct suffix *suff;
+ struct suffix *suff_N = &suffixes[(sizeof(suffixes)/sizeof(suffixes[0])) - 1];
+
+ for (lang = languages; lang <= lang_N; ++lang)
+ lang->lang_next = lang + 1;
+ lang_N->lang_next = NULL;
+
+ for (suff = suffixes; suff <= suff_N; ++suff) {
+ lang = get_lang_entry (suff->suff_lang_name);
+ if (lang)
+ suff->suff_language = lang;
+ suff->suff_next = suff + 1;
+ }
+ suff_N->suff_next = NULL;
+}
+
+/* Return a suffix table entry for the given suffix. */
+static struct suffix *
+get_suffix_entry (char const *suffix)
+{
+ struct suffix *stp;
+
+ if (suffix == NULL)
+ suffix = "";
+
+ for (stp = suffixes; stp; stp = stp->suff_next)
+ if (strequ (stp->suff_suffix, suffix))
+ return stp;
+ return stp;
+}
+
+static struct language *
+get_lang_entry (char const *lang_name)
+{
+ struct language *ltp;
+
+ if (lang_name == NULL)
+ lang_name = "";
+
+ for (ltp = languages; ltp->lang_next; ltp = ltp->lang_next)
+ if (ltp->lang_name == lang_name || strequ (ltp->lang_name, lang_name))
+ return ltp;
+ return ltp;
+}
+
+char const *
+get_lang_name (char const *suffix)
+{
+ struct suffix *stp;
+
+ stp = get_suffix_entry (suffix);
+ if (stp->suff_next == NULL)
+ return NULL;
+ return stp->suff_language->lang_name;
+}
+
+char const *
+get_filter (char const *suffix)
+{
+ struct suffix *stp;
+
+ stp = get_suffix_entry (suffix);
+ if (stp->suff_next == NULL)
+ return NULL;
+ return stp->suff_language->lang_filter;
+}
+
+char const *(*
+get_scanner (char const *lang)
+ ) (FILE *input_FILE, int *flags)
+{
+ struct language *ltp;
+
+ ltp = get_lang_entry (lang);
+ if (ltp->lang_next == NULL)
+ return NULL;
+ return ltp->lang_get_token;
+}
+
+void
+set_scan_args (int op, char *arg)
+{
+ struct language *ltp, *ltp2;
+ struct suffix *stp;
+ char *lhs;
+ char *lhs2;
+ int count = 0;
+
+ lhs = arg;
+ while (isalnum (*arg) || *arg == '.')
+ arg++;
+
+ if (strequ (lhs, "?=?"))
+ {
+ for (stp = suffixes; stp->suff_next; stp = stp->suff_next)
+ {
+ printf ("%s%s=%s", (count++ > 0) ? ", " : "", stp->suff_suffix, stp->suff_language->lang_name);
+ if (stp->suff_language->lang_filter)
+ printf (" (%s)", stp->suff_language->lang_filter);
+ }
+ if (count)
+ putchar ('\n');
+ return;
+ }
+
+ if (strnequ (lhs, "?=", 2))
+ {
+ lhs += 2;
+ ltp = get_lang_entry (lhs);
+ if (ltp->lang_next == NULL)
+ {
+ printf ("No scanner for language `%s'\n", lhs);
+ return;
+ }
+ for (stp = suffixes; stp->suff_next; stp = stp->suff_next)
+ if (stp->suff_language == ltp)
+ {
+ printf ("%s%s=%s", (count++ > 0) ? ", " : "", stp->suff_suffix, ltp->lang_name);
+ if (stp->suff_language->lang_filter)
+ printf (" (%s)", stp->suff_language->lang_filter);
+ }
+ if (count)
+ putchar ('\n');
+ return;
+ }
+
+ if (strequ (arg, "=?"))
+ {
+ lhs[strlen (lhs) - 2] = '\0';
+ stp = get_suffix_entry (lhs);
+ if (stp->suff_next == NULL)
+ {
+ printf ("No scanner assigned to suffix `%s'\n", lhs);
+ return;
+ }
+ printf ("%s=%s", stp->suff_suffix, stp->suff_language->lang_name);
+ if (stp->suff_language->lang_filter)
+ printf (" (%s)", stp->suff_language->lang_filter);
+ printf ("\n");
+ return;
+ }
+
+ if (*arg == '=')
+ {
+ *arg++ = '\0';
+
+ ltp = get_lang_entry (arg);
+ if (ltp->lang_next == NULL)
+ {
+ fprintf (stderr, "%s: Language undefined: %s\n", program_name, arg);
+ return;
+ }
+ stp = get_suffix_entry (lhs);
+ if (stp->suff_next == NULL)
+ {
+ stp->suff_suffix = lhs;
+ stp->suff_language = ltp;
+ stp->suff_next = CALLOC (struct suffix, 1);
+ }
+ else if (!strequ (arg, stp->suff_language->lang_name))
+ {
+ fprintf (stderr, "%s: Note: `%s=%s' overrides `%s=%s'\n", program_name, lhs, arg, lhs, stp->suff_language->lang_name);
+ stp->suff_language = ltp;
+ }
+ return;
+ }
+ else if (*arg == '/')
+ {
+ *arg++ = '\0';
+ ltp = get_lang_entry (lhs);
+ if (ltp->lang_next == NULL)
+ {
+ ltp->lang_name = lhs;
+ ltp->lang_get_token = get_token_text;
+ ltp->lang_set_args = set_args_text;
+ ltp->lang_filter = NULL;
+ ltp->lang_next = CALLOC (struct language, 1);
+ }
+ lhs2 = arg;
+ arg = strchr (arg, '/');
+ if (arg == NULL)
+ ltp2 = ltp;
+ else
+ {
+ *arg++ = '\0';
+ ltp2 = get_lang_entry (lhs2);
+ if (ltp2->lang_next == NULL)
+ {
+ fprintf (stderr, "%s: language %s not defined.\n", program_name, lhs2);
+ ltp2 = ltp;
+ }
+ }
+ ltp->lang_get_token = ltp2->lang_get_token;
+ ltp->lang_set_args = ltp2->lang_set_args;
+ if (ltp->lang_filter && (!strequ (arg, ltp->lang_filter)))
+ fprintf (stderr, "%s: Note: `%s/%s' overrides `%s/%s'\n", program_name, lhs, arg, lhs, ltp->lang_filter);
+ ltp->lang_filter = arg;
+ return;
+ }
+
+ if (op == '+')
+ {
+ switch (op = *arg++)
+ {
+ case '+':
+ case '-':
+ case '?':
+ break;
+ default:
+ usage_scan ();
+ }
+ for (ltp = languages; ltp->lang_next; ltp = ltp->lang_next)
+ (*ltp->lang_set_args) (NULL, op, arg);
+ return;
+ }
+
+ if (*arg == '-' || *arg == '+' || *arg == '?')
+ {
+ op = *arg;
+ *arg++ = '\0';
+
+ ltp = get_lang_entry (lhs);
+ if (ltp->lang_next == NULL)
+ {
+ fprintf (stderr, "%s: Language undefined: %s\n", program_name, lhs);
+ return;
+ }
+ (*ltp->lang_set_args) (lhs, op, arg);
+ return;
+ }
+
+ usage_scan ();
+}
+
+static void
+usage_scan (void)
+{
+ fprintf (stderr, "Usage: %s [-S<suffix>=<lang>] [+S(+|-)<arg>] [-S<lang>(+|-)<arg>] [-S<lang>/<lang>/<filter>]\n", program_name);
+ exit (1);
+}
+
+/*************** C & C++ ****************************************************/
+
+#define I1 0x0001 /* 1st char of an identifier [a-zA-Z_] */
+#define DG 0x0002 /* decimal digit [0-9] */
+#define NM 0x0004 /* extra chars in a hex or long number [a-fA-FxXlL] */
+#define C1 0x0008 /* C comment introduction char: / */
+#define C2 0x0010 /* C comment termination char: * */
+#define Q1 0x0020 /* single quote: ' */
+#define Q2 0x0040 /* double quote: " */
+#define ES 0x0080 /* escape char: \ */
+#define NL 0x0100 /* newline: \n */
+#define EF 0x0200 /* EOF */
+#define SK 0x0400 /* Make these chars valid for names within strings */
+#define VH 0x0800 /* VHIL comment introduction char: # */
+#define WS 0x1000 /* White space characters */
+
+/*
+ character class membership macros:
+*/
+#define ISDIGIT(c) ((rct)[c] & (DG)) /* digit */
+#define ISNUMBER(c) ((rct)[c] & (DG|NM)) /* legal in a number */
+#define ISEOF(c) ((rct)[c] & (EF)) /* EOF */
+#define ISID1ST(c) ((rct)[c] & (I1)) /* 1st char of an identifier */
+#define ISIDREST(c) ((rct)[c] & (I1|DG)) /* rest of an identifier */
+#define ISSTRKEEP(c) ((rct)[c] & (I1|DG|SK)) /* keep contents of string */
+#define ISSPACE(c) ((rct)[c] & (WS)) /* white space character */
+/*
+ The `BORING' classes should be skipped over
+ until something interesting comes along...
+*/
+#define ISBORING(c) (!((rct)[c] & (EF|NL|I1|DG|Q1|Q2|C1|VH))) /* fluff */
+#define ISCBORING(c) (!((rct)[c] & (EF|C2))) /* comment fluff */
+#define ISVBORING(c) (!((rct)[c] & (EF|NL))) /* vhil comment fluff */
+#define ISQ1BORING(c) (!((rct)[c] & (EF|NL|Q1|ES))) /* char const fluff */
+#define ISQ2BORING(c) (!((rct)[c] & (EF|NL|Q2|ES))) /* quoted str fluff */
+
+static short ctype_c[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, NL, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, Q2, 0, 0, 0, 0, Q1,
+/*050*/ 0, 0, C2, 0, 0, 0, 0, C1,
+/*060*/ DG, DG, DG, DG, DG, DG, DG, DG,
+/*070*/ DG, DG, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, ES, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+};
+
+static int eat_underscore = 1;
+static int scan_VHIL = 0;
+
+static char const *
+get_token_VHIL (FILE *input_FILE, int *flags)
+{
+ if (!scan_VHIL)
+ set_args_c ("vhil", '+', "v");
+ return get_token_c (input_FILE, flags);
+}
+
+/*
+ Grab the next identifier the C source
+ file opened with the handle `input_FILE'.
+ This state machine is built for speed, not elegance.
+*/
+static char const *
+get_token_c (FILE *input_FILE, int *flags)
+{
+ static char input_buffer[BUFSIZ];
+ static int new_line = 1;
+ short *rct = &ctype_c[1];
+ int c;
+ char *id = input_buffer;
+
+top:
+ c = getc (input_FILE);
+ if (new_line)
+ {
+ new_line = 0;
+ if (c == '.')
+ {
+ /* Auto-recognize vhil code when you see a '.' in column 1.
+ also ignore lines that start with a '.' */
+ if (!scan_VHIL)
+ set_args_c ("vhil", '+', "v");
+ while (ISVBORING (c))
+ c = getc (input_FILE);
+ new_line = 1;
+ goto top;
+ }
+ if (c != '#')
+ goto next;
+ c = getc (input_FILE);
+ if (scan_VHIL && ISSPACE (c))
+ {
+ while (ISVBORING (c))
+ c = getc (input_FILE);
+ new_line = 1;
+ goto top;
+ }
+ while (ISBORING (c))
+ c = getc (input_FILE);
+ if (!ISID1ST (c))
+ goto next;
+ id = input_buffer;
+ *id++ = c;
+ while (ISIDREST (c = getc (input_FILE)))
+ *id++ = c;
+ *id = '\0';
+ if (strequ (input_buffer, "include"))
+ {
+ while (c == ' ' || c == '\t')
+ c = getc (input_FILE);
+ if (c == '\n')
+ {
+ new_line = 1;
+ goto top;
+ }
+ id = input_buffer;
+ if (c == '"')
+ {
+ c = getc (input_FILE);
+ while (c != '\n' && c != EOF && c != '"')
+ {
+ *id++ = c;
+ c = getc (input_FILE);
+ }
+ *flags = TOK_STRING;
+ }
+ else if (c == '<')
+ {
+ c = getc (input_FILE);
+ while (c != '\n' && c != EOF && c != '>')
+ {
+ *id++ = c;
+ c = getc (input_FILE);
+ }
+ *flags = TOK_STRING;
+ }
+ else if (ISID1ST (c))
+ {
+ *id++ = c;
+ while (ISIDREST (c = getc (input_FILE)))
+ *id++ = c;
+ *flags = TOK_NAME;
+ }
+ else
+ {
+ while (c != '\n' && c != EOF)
+ c = getc (input_FILE);
+ new_line = 1;
+ goto top;
+ }
+ while (c != '\n' && c != EOF)
+ c = getc (input_FILE);
+ new_line = 1;
+ *id = '\0';
+ return input_buffer;
+ }
+ if (strnequ (input_buffer, "if", 2)
+ || strequ (input_buffer, "define")
+ || strequ (input_buffer, "elif") /* ansi C */
+ || (scan_VHIL && strequ (input_buffer, "elsif"))
+ || strequ (input_buffer, "undef"))
+ goto next;
+ while ((c != '\n') && (c != EOF))
+ c = getc (input_FILE);
+ new_line = 1;
+ goto top;
+ }
+
+next:
+ while (ISBORING (c))
+ c = getc (input_FILE);
+
+ switch (c)
+ {
+ case '"':
+ id = input_buffer;
+ *id++ = c = getc (input_FILE);
+ for (;;)
+ {
+ while (ISQ2BORING (c))
+ *id++ = c = getc (input_FILE);
+ if (c == '\\')
+ {
+ *id++ = c = getc (input_FILE);
+ continue;
+ }
+ else if (c != '"')
+ goto next;
+ break;
+ }
+ *--id = '\0';
+ id = input_buffer;
+ while (ISSTRKEEP (*id))
+ id++;
+ if (*id || id == input_buffer)
+ {
+ c = getc (input_FILE);
+ goto next;
+ }
+ *flags = TOK_STRING;
+ if (eat_underscore && input_buffer[0] == '_' && input_buffer[1])
+ return &input_buffer[1];
+ else
+ return input_buffer;
+
+ case '\'':
+ c = getc (input_FILE);
+ for (;;)
+ {
+ while (ISQ1BORING (c))
+ c = getc (input_FILE);
+ if (c == '\\')
+ {
+ c = getc (input_FILE);
+ continue;
+ }
+ else if (c == '\'')
+ c = getc (input_FILE);
+ goto next;
+ }
+
+ case '/':
+ c = getc (input_FILE);
+ if (c == '/')
+ { /* Cope with C++ comment */
+ while (ISVBORING (c))
+ c = getc (input_FILE);
+ new_line = 1;
+ goto top;
+ }
+ else if (c != '*')
+ goto next;
+ c = getc (input_FILE);
+ for (;;)
+ {
+ while (ISCBORING (c))
+ c = getc (input_FILE);
+ c = getc (input_FILE);
+ if (c == '/')
+ {
+ c = getc (input_FILE);
+ goto next;
+ }
+ else if (ISEOF (c))
+ {
+ new_line = 1;
+ return NULL;
+ }
+ }
+
+ case '\n':
+ new_line = 1;
+ goto top;
+
+ case '#':
+ if (!scan_VHIL)
+ {
+ /* Auto-recognize vhil when find a # in the middle of a line. */
+ set_args_c ("vhil", '+', "v");
+ }
+ c = getc (input_FILE);
+ while (ISVBORING (c))
+ c = getc (input_FILE);
+ new_line = 1;
+ goto top;
+ default:
+ if (ISEOF (c))
+ {
+ new_line = 1;
+ return NULL;
+ }
+ id = input_buffer;
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (input_FILE)))
+ *id++ = c;
+ }
+ else if (ISDIGIT (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (input_FILE)))
+ *id++ = c;
+ }
+ else
+ fprintf (stderr, "junk: `\\%3o'", c);
+ ungetc (c, input_FILE);
+ *id = '\0';
+ *flags |= TOK_LITERAL;
+ return input_buffer;
+ }
+}
+
+static void
+set_ctype_c (char const *chars, int type)
+{
+ short *rct = &ctype_c[1];
+
+ while (*chars)
+ rct[*chars++] |= type;
+}
+
+static void
+clear_ctype_c (char const *chars, int type)
+{
+ short *rct = &ctype_c[1];
+
+ while (*chars)
+ rct[*chars++] &= ~type;
+}
+
+static void
+usage_c (char const *lang_name)
+{
+ fprintf (stderr, "Usage: %s does not accept %s scanner arguments\n", program_name, lang_name);
+ exit (1);
+}
+
+static char document_c[] = "\
+The C scanner arguments take the form -Sc<arg>, where <arg>\n\
+is one of the following: (<cc> denotes one or more characters)\n\
+ (+|-)u . . . . (Do|Don't) strip a leading `_' from ids in strings.\n\
+ (+|-)s<cc> . . Allow <cc> in string ids, and (keep|ignore) those ids.\n\
+ -v . . . . . . Skip vhil comments.";
+
+static void
+set_args_c (char const *lang_name, int op, char const *arg)
+{
+ if (op == '?')
+ {
+ puts (document_c);
+ return;
+ }
+ switch (*arg++)
+ {
+ case 'u':
+ eat_underscore = (op == '+');
+ break;
+ case 's':
+ if (op == '+')
+ set_ctype_c (arg, SK);
+ else
+ clear_ctype_c (arg, SK);
+ break;
+ case 'v':
+ set_ctype_c ("$", I1);
+ set_ctype_c ("#", VH);
+ set_ctype_c (" \t", WS);
+ scan_VHIL = 1;
+ break;
+ default:
+ if (lang_name)
+ usage_c (lang_name);
+ break;
+ }
+}
+
+#undef I1
+#undef DG
+#undef NM
+#undef C1
+#undef C2
+#undef Q1
+#undef Q2
+#undef ES
+#undef NL
+#undef EF
+#undef SK
+#undef VH
+#undef WS
+#undef ISDIGIT
+#undef ISNUMBER
+#undef ISEOF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISSTRKEEP
+#undef ISSPACE
+#undef ISBORING
+#undef ISCBORING
+#undef ISVBORING
+#undef ISQ1BORING
+#undef ISQ2BORING
+
+/*************** Assembly ***************************************************/
+
+#define I1 0x01 /* 1st char of an identifier [a-zA-Z_] */
+#define NM 0x02 /* digit [0-9a-fA-FxX] */
+#define NL 0x04 /* newline: \n */
+#define CM 0x08 /* assembler comment char: usually # or | */
+#define IG 0x10 /* ignore `identifiers' with these chars in them */
+#define C1 0x20 /* C comment introduction char: / */
+#define C2 0x40 /* C comment termination char: * */
+#define EF 0x80 /* EOF */
+
+/* Assembly Language character classes */
+#define ISID1ST(c) ((rct)[c] & (I1))
+#define ISIDREST(c) ((rct)[c] & (I1|NM))
+#define ISNUMBER(c) ((rct)[c] & (NM))
+#define ISEOF(c) ((rct)[c] & (EF))
+#define ISCOMMENT(c) ((rct)[c] & (CM))
+#define ISBORING(c) (!((rct)[c] & (EF|NL|I1|NM|CM|C1)))
+#define ISCBORING(c) (!((rct)[c] & (EF|NL)))
+#define ISCCBORING(c) (!((rct)[c] & (EF|C2)))
+#define ISIGNORE(c) ((rct)[c] & (IG))
+
+static char ctype_asm[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, NL, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*050*/ 0, 0, C2, 0, 0, 0, 0, C1,
+/*060*/ NM, NM, NM, NM, NM, NM, NM, NM,
+/*070*/ NM, NM, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, 0, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+
+};
+
+static int cpp_on_asm = 1;
+
+/*
+ Grab the next identifier the assembly language
+ source file opened with the handle `input_FILE'.
+ This state machine is built for speed, not elegance.
+*/
+static char const *
+get_token_asm (FILE *input_FILE, int *flags)
+{
+ static char input_buffer[BUFSIZ];
+ char *rct = &ctype_asm[1];
+ int c;
+ char *id = input_buffer;
+ static int new_line = 1;
+
+top:
+ c = getc (input_FILE);
+ if (cpp_on_asm > 0 && new_line)
+ {
+ new_line = 0;
+ if (c != '#')
+ goto next;
+ while (ISBORING (c))
+ c = getc (input_FILE);
+ if (!ISID1ST (c))
+ goto next;
+ id = input_buffer;
+ *id++ = c;
+ while (ISIDREST (c = getc (input_FILE)))
+ *id++ = c;
+ *id = '\0';
+ if (strequ (input_buffer, "include"))
+ {
+ while (c != '"' && c != '<')
+ c = getc (input_FILE);
+ id = input_buffer;
+ *id++ = c = getc (input_FILE);
+ while ((c = getc (input_FILE)) != '"' && c != '>')
+ *id++ = c;
+ *id = '\0';
+ *flags = TOK_STRING;
+ return input_buffer;
+ }
+ if (strnequ (input_buffer, "if", 2)
+ || strequ (input_buffer, "define")
+ || strequ (input_buffer, "undef"))
+ goto next;
+ while (c != '\n')
+ c = getc (input_FILE);
+ new_line = 1;
+ goto top;
+ }
+
+next:
+ while (ISBORING (c))
+ c = getc (input_FILE);
+
+ if (ISCOMMENT (c))
+ {
+ while (ISCBORING (c))
+ c = getc (input_FILE);
+ new_line = 1;
+ }
+
+ if (ISEOF (c))
+ {
+ new_line = 1;
+ return NULL;
+ }
+
+ if (c == '\n')
+ {
+ new_line = 1;
+ goto top;
+ }
+
+ if (c == '/')
+ {
+ if ((c = getc (input_FILE)) != '*')
+ goto next;
+ c = getc (input_FILE);
+ for (;;)
+ {
+ while (ISCCBORING (c))
+ c = getc (input_FILE);
+ c = getc (input_FILE);
+ if (c == '/')
+ {
+ c = getc (input_FILE);
+ break;
+ }
+ else if (ISEOF (c))
+ {
+ new_line = 1;
+ return NULL;
+ }
+ }
+ goto next;
+ }
+
+ id = input_buffer;
+ if (eat_underscore && c == '_' && !ISID1ST (c = getc (input_FILE)))
+ {
+ ungetc (c, input_FILE);
+ return "_";
+ }
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (input_FILE)))
+ *id++ = c;
+ }
+ else if (ISNUMBER (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (input_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, "junk: `%c'", c);
+ else
+ fprintf (stderr, "junk: `\\%03o'", c);
+ goto next;
+ }
+
+ *id = '\0';
+ for (id = input_buffer; *id; id++)
+ if (ISIGNORE (*id))
+ goto next;
+ ungetc (c, input_FILE);
+ *flags |= TOK_LITERAL;
+ return input_buffer;
+}
+
+static void
+set_ctype_asm (char const *chars, int type)
+{
+ char *rct = &ctype_asm[1];
+
+ while (*chars)
+ rct[*chars++] |= type;
+}
+
+static void
+clear_ctype_asm (char const *chars, int type)
+{
+ char *rct = &ctype_asm[1];
+
+ while (*chars)
+ rct[*chars++] &= ~type;
+}
+
+static void
+usage_asm (char const *lang_name)
+{
+ fprintf (stderr, "Usage: %s -S%s([-c<cc>] [-u] [(+|-)a<cc>] [(+|-)p] [(+|-)C])\n", program_name, lang_name);
+ exit (1);
+}
+
+static char document_asm[] = "\
+The Assembler scanner arguments take the form -Sasm<arg>, where\n\
+<arg> is one of the following: (<cc> denotes one or more characters)\n\
+ -c<cc> . . . . <cc> introduce(s) a comment until end-of-line.\n\
+ (+|-)u . . . . (Do|Don't) strip a leading `_' from ids.\n\
+ (+|-)a<cc> . . Allow <cc> in ids, and (keep|ignore) those ids.\n\
+ (+|-)p . . . . (Do|Don't) handle C-preprocessor directives.\n\
+ (+|-)C . . . . (Do|Don't) handle C-style comments. (/* */)";
+
+static void
+set_args_asm (char const *lang_name, int op, char const *arg)
+{
+ if (op == '?')
+ {
+ puts (document_asm);
+ return;
+ }
+ switch (*arg++)
+ {
+ case 'a':
+ set_ctype_asm (arg, I1 | ((op == '-') ? IG : 0));
+ break;
+ case 'c':
+ set_ctype_asm (arg, CM);
+ break;
+ case 'u':
+ eat_underscore = (op == '+');
+ break;
+ case 'p':
+ cpp_on_asm = (op == '+');
+ break;
+ case 'C':
+ if (op == '+')
+ {
+ set_ctype_asm ("/", C1);
+ set_ctype_asm ("*", C2);
+ }
+ else
+ {
+ clear_ctype_asm ("/", C1);
+ clear_ctype_asm ("*", C2);
+ }
+ break;
+ default:
+ if (lang_name)
+ usage_asm (lang_name);
+ break;
+ }
+}
+
+#undef I1
+#undef NM
+#undef NL
+#undef CM
+#undef IG
+#undef C1
+#undef C2
+#undef EF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISNUMBER
+#undef ISEOF
+#undef ISCOMMENT
+#undef ISBORING
+#undef ISCBORING
+#undef ISCCBORING
+#undef ISIGNORE
+
+/*************** Text *******************************************************/
+
+#define I1 0x01 /* 1st char of an identifier [a-zA-Z_] */
+#define NM 0x02 /* digit [0-9a-fA-FxX] */
+#define SQ 0x04 /* squeeze these out (.,',-) */
+#define EF 0x80 /* EOF */
+
+/* Text character classes */
+#define ISID1ST(c) ((rct)[c] & (I1))
+#define ISIDREST(c) ((rct)[c] & (I1|NM|SQ))
+#define ISNUMBER(c) ((rct)[c] & (NM))
+#define ISEOF(c) ((rct)[c] & (EF))
+#define ISBORING(c) (!((rct)[c] & (I1|NM|EF)))
+#define ISIDSQUEEZE(c) ((rct)[c] & (SQ))
+
+static char ctype_text[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, 0, 0, 0, 0, 0, SQ,
+/*050*/ 0, 0, 0, 0, 0, SQ, SQ, 0,
+/*060*/ NM, NM, NM, NM, NM, NM, NM, NM,
+/*070*/ NM, NM, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, 0, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+};
+
+/*
+ Grab the next identifier the text source file opened with the
+ handle `input_FILE'. This state machine is built for speed, not
+ elegance.
+*/
+static char const *
+get_token_text (FILE *input_FILE, int *flags)
+{
+ static char input_buffer[BUFSIZ];
+ char *rct = &ctype_text[1];
+ int c;
+ char *id = input_buffer;
+
+top:
+ c = getc (input_FILE);
+ while (ISBORING (c))
+ c = getc (input_FILE);
+ if (ISEOF (c))
+ return NULL;
+ id = input_buffer;
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (input_FILE)))
+ if (!ISIDSQUEEZE (c))
+ *id++ = c;
+ }
+ else if (ISNUMBER (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (input_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, "junk: `%c'", c);
+ else
+ fprintf (stderr, "junk: `\\%03o'", c);
+ goto top;
+ }
+
+ *id = '\0';
+ ungetc (c, input_FILE);
+ *flags |= TOK_LITERAL;
+ return input_buffer;
+}
+
+static void
+set_ctype_text (char const *chars, int type)
+{
+ char *rct = &ctype_text[1];
+
+ while (*chars)
+ rct[*chars++] |= type;
+}
+
+static void
+clear_ctype_text (char const *chars, int type)
+{
+ char *rct = &ctype_text[1];
+
+ while (*chars)
+ rct[*chars++] &= ~type;
+}
+
+static void
+usage_text (char const *lang_name)
+{
+ fprintf (stderr, "Usage: %s -S%s([(+|-)a<cc>] [(+|-)s<cc>]\n", program_name, lang_name);
+ exit (1);
+}
+
+static char document_text[] = "\
+The Text scanner arguments take the form -Stext<arg>, where\n\
+<arg> is one of the following: (<cc> denotes one or more characters)\n\
+ (+|-)a<cc> . . Include (or exculde) <cc> in ids.\n\
+ (+|-)s<cc> . . Squeeze (or don't squeeze) <cc> out of ids.";
+
+static void
+set_args_text (char const *lang_name, int op, char const *arg)
+{
+ if (op == '?')
+ {
+ puts (document_text);
+ return;
+ }
+ switch (*arg++)
+ {
+ case 'a':
+ if (op == '+')
+ set_ctype_text (arg, I1);
+ else
+ clear_ctype_text (arg, I1);
+ break;
+ case 's':
+ if (op == '+')
+ set_ctype_text (arg, SQ);
+ else
+ clear_ctype_text (arg, SQ);
+ break;
+ default:
+ if (lang_name)
+ usage_text (lang_name);
+ break;
+ }
+}
+
+#undef I1
+#undef NM
+#undef SQ
+#undef EF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISNUMBER
+#undef ISEOF
+#undef ISBORING
+#undef ISIDSQUEEZE
diff --git a/scanners.h b/scanners.h
new file mode 100644
index 0000000..e7c1753
--- /dev/null
+++ b/scanners.h
@@ -0,0 +1,30 @@
+/* scanners.h -- defs for interface to scanners.c
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _scanners_h_
+#define _scanners_h_
+
+typedef char *(*get_token_t) (FILE*, int*);
+
+char const *get_lang_name (char const *suffix);
+char const *get_filter (char const *suffix);
+char const *(*get_scanner (char const *lang_name)) (FILE *input_FILE, int *flags);
+void set_scan_args (int op, char *arg);
+void init_scanners (void);
+
+#endif /* not _scanners_h_ */
diff --git a/stamp-h.in b/stamp-h.in
new file mode 100644
index 0000000..7198698
--- /dev/null
+++ b/stamp-h.in
@@ -0,0 +1 @@
+Wed Feb 15 16:05:26 EST 1995
diff --git a/strcasecmp.c b/strcasecmp.c
new file mode 100644
index 0000000..8a93ef2
--- /dev/null
+++ b/strcasecmp.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific written prior permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcasecmp.c 5.5 (Berkeley) 11/24/87";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison. The mappings are
+p * based upon ascii character sequences.
+ */
+static unsigned char charmap[] = {
+ '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+ '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+ '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+ '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+ '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+ '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+ '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+ '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+ '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+ '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+ '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+ '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+ '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+ '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+ '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+ '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+ '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+ '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+ '\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+ '\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
+ '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+ '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+int
+strcasecmp(s1, s2)
+ char *s1, *s2;
+{
+ register unsigned char u1, u2;
+
+ for (;;) {
+ u1 = (unsigned char) *s1++;
+ u2 = (unsigned char) *s2++;
+ if (charmap[u1] != charmap[u2]) {
+ return charmap[u1] - charmap[u2];
+ }
+ if (u1 == '\0') {
+ return 0;
+ }
+ }
+}
+
diff --git a/strxtra.h b/strxtra.h
new file mode 100644
index 0000000..64bf088
--- /dev/null
+++ b/strxtra.h
@@ -0,0 +1,35 @@
+/* strxtra.c -- convenient string manipulation macros
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _strxtra_h_
+#define _strxtra_h_
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#define strequ(s1, s2) (strcmp((s1), (s2)) == 0)
+#define strnequ(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
+#define strcaseequ(s1, s2) (strcasecmp((s1), (s2)) == 0)
+#define strncaseequ(s1, s2, n) (strncasecmp((s1), (s2), (n)) == 0)
+#ifndef HAVE_STRDUP
+#define strdup(s) (strcpy(calloc(1, strlen(s)+1), (s)))
+#endif
+#define strndup(s, n) (strncpy(calloc(1, (n)+1), (s), (n)))
+
+#endif /* not _strxtra_h_ */
diff --git a/token.c b/token.c
new file mode 100644
index 0000000..5c92804
--- /dev/null
+++ b/token.c
@@ -0,0 +1,50 @@
+/* token.c -- misc. access functions for mkid database tokens
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include "token.h"
+
+unsigned int
+tok_flags (char const *buf)
+{
+ return *(unsigned char const *)&buf[strlen (buf) + 1];
+}
+
+#define TOK_COUNT_ADDR(buf) ((unsigned char const *)(TOK_FLAGS_ADDR (buf) + 1))
+#define TOK_HITS_ADDR(buf) ((unsigned char const *)(TOK_COUNT_ADDR (buf) + 2))
+
+unsigned short
+tok_count (char const *buf)
+{
+ unsigned char const *flags = (unsigned char const *)&buf[strlen (buf) + 1];
+ unsigned char const *addr = flags + 1;
+ unsigned short count = *addr;
+ if (*flags & TOK_SHORT_COUNT)
+ count += (*++addr << 8);
+ return count;
+}
+
+unsigned char const *
+tok_hits_addr (char const *buf)
+{
+ unsigned char const *flags = (unsigned char const *)&buf[strlen (buf) + 1];
+ unsigned char const *addr = flags + 2;
+ if (*flags & TOK_SHORT_COUNT)
+ addr++;
+ return addr;
+}
diff --git a/token.h b/token.h
new file mode 100644
index 0000000..b3d90d5
--- /dev/null
+++ b/token.h
@@ -0,0 +1,40 @@
+/* token.h -- defs for interface to token.c
+ Copyright (C) 1986, 1995 Greg McGary
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _token_h_
+#define _token_h_
+
+/* token flags (struct token is in mkid.c) */
+#define TOK_VECTOR 0x01 /* 1 = hits are stored as a vector
+ 0 = hits are stored as a 8-way tree of bits
+ mkid chooses whichever is more compact.
+ vector is more compact for tokens with few hits */
+#define TOK_NUMBER 0x02 /* occurs as a number */
+#define TOK_NAME 0x04 /* occurs as a name */
+#define TOK_STRING 0x08 /* occurs in a string */
+#define TOK_LITERAL 0x10 /* occurs as a literal */
+#define TOK_COMMENT 0x20 /* occurs in a comment */
+#define TOK_UNUSED_1 0x40
+#define TOK_SHORT_COUNT 0x80 /* count is two bytes */
+
+#define tok_string(buf) (buf)
+unsigned int tok_flags (char const *buf);
+unsigned short tok_count (char const *buf);
+unsigned char const *tok_hits_addr (char const *buf);
+
+#endif /* _token_h_ */