diff options
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | INSTALL | 118 | ||||
-rw-r--r-- | Makefile.in | 264 | ||||
-rw-r--r-- | NEWS | 25 | ||||
-rw-r--r-- | README | 23 | ||||
-rw-r--r-- | THANKS | 17 | ||||
-rw-r--r-- | TODO | 51 | ||||
-rw-r--r-- | acconfig.h | 27 | ||||
-rw-r--r-- | aclocal.m4 | 68 | ||||
-rw-r--r-- | alloc.h | 28 | ||||
-rw-r--r-- | alloca.c | 492 | ||||
-rw-r--r-- | bitops.c | 116 | ||||
-rw-r--r-- | bitops.h | 31 | ||||
-rw-r--r-- | config.h.in | 140 | ||||
-rwxr-xr-x | configure | 2013 | ||||
-rw-r--r-- | configure.in | 77 | ||||
-rw-r--r-- | depend.out | 40 | ||||
-rw-r--r-- | fid.1 | 26 | ||||
-rw-r--r-- | fid.c | 191 | ||||
-rw-r--r-- | filenames.c | 603 | ||||
-rw-r--r-- | filenames.h | 38 | ||||
-rw-r--r-- | getopt.c | 748 | ||||
-rw-r--r-- | getopt1.c | 180 | ||||
-rw-r--r-- | gid.el | 22 | ||||
-rw-r--r-- | idarg.h | 33 | ||||
-rw-r--r-- | idfile.c | 184 | ||||
-rw-r--r-- | idfile.h | 55 | ||||
-rwxr-xr-x | idtest | 32 | ||||
-rw-r--r-- | idx.c | 88 | ||||
-rw-r--r-- | iid.1 | 235 | ||||
-rw-r--r-- | iid.c | 2301 | ||||
-rw-r--r-- | iid.help | 92 | ||||
-rw-r--r-- | iid.y | 1334 | ||||
-rwxr-xr-x | install-sh | 238 | ||||
-rw-r--r-- | lid.1 | 211 | ||||
-rw-r--r-- | lid.c | 1390 | ||||
-rw-r--r-- | misc.c | 134 | ||||
-rw-r--r-- | misc.h | 31 | ||||
-rw-r--r-- | mkid.1 | 187 | ||||
-rw-r--r-- | mkid.c | 1091 | ||||
-rw-r--r-- | mkid.info | 1094 | ||||
-rw-r--r-- | mkid.texinfo | 953 | ||||
-rw-r--r-- | scanners.c | 1209 | ||||
-rw-r--r-- | scanners.h | 30 | ||||
-rw-r--r-- | stamp-h.in | 1 | ||||
-rw-r--r-- | strcasecmp.c | 77 | ||||
-rw-r--r-- | strxtra.h | 35 | ||||
-rw-r--r-- | token.c | 50 | ||||
-rw-r--r-- | token.h | 40 |
49 files changed, 16802 insertions, 0 deletions
@@ -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. @@ -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 @@ -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: @@ -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. @@ -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) ... @@ -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 +]) @@ -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 @@ -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). @@ -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 */ @@ -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)) @@ -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_ */ @@ -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 @@ -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); +} @@ -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). @@ -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. @@ -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 @@ -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). @@ -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 */ @@ -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; +} @@ -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_ */ @@ -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). @@ -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 = ¤t_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 = ¤t_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_ */ @@ -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; +} @@ -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_ */ |