From 168082589012dc2653f955ab9e62e0b653d76d2f Mon Sep 17 00:00:00 2001 From: kevin Date: Sun, 4 Feb 1996 20:35:04 +0000 Subject: [PATCH] Initial revision --- COPYING | 339 +++ ChangeLog | 1328 ++++++++++ INSTALL | 167 ++ Makefile.am | 6 + Makefile.in | 140 + NEWS | 51 + README | 47 + TODO | 5 + acconfig.h | 36 + config.h.in | 171 ++ configure | 2774 ++++++++++++++++++++ configure.in | 89 + doc/Makefile.am | 4 + doc/Makefile.in | 121 + doc/find.info | 113 + doc/find.info-1 | 1581 +++++++++++ doc/find.info-2 | 1069 ++++++++ doc/find.texi | 2206 ++++++++++++++++ doc/perm.texi | 481 ++++ doc/texinfo.tex | 4365 ++++++++++++++++++++++++++++++ find/Makefile.am | 14 + find/Makefile.in | 160 ++ find/defs.h | 332 +++ find/find.1 | 455 ++++ find/find.c | 541 ++++ find/fstype.c | 386 +++ find/parser.c | 1884 +++++++++++++ find/pred.c | 1525 +++++++++++ find/tree.c | 433 +++ find/util.c | 158 ++ find/version.c | 1 + install-sh | 238 ++ lib/Makefile.am | 19 + lib/Makefile.in | 136 + lib/alloca.c | 492 ++++ lib/dirname.c | 70 + lib/error.c | 119 + lib/fileblocks.c | 66 + lib/filemode.c | 237 ++ lib/fnmatch.c | 200 ++ lib/fnmatch.h | 67 + lib/getline.c | 126 + lib/getopt.c | 748 ++++++ lib/getopt.h | 129 + lib/getopt1.c | 180 ++ lib/idcache.c | 210 ++ lib/listfile.c | 273 ++ lib/memcmp.c | 32 + lib/memset.c | 29 + lib/mktime.c | 502 ++++ lib/modechange.c | 341 +++ lib/modechange.h | 55 + lib/modetype.h | 81 + lib/nextelem.c | 106 + lib/pathmax.h | 53 + lib/regex.c | 5244 +++++++++++++++++++++++++++++++++++++ lib/regex.h | 487 ++++ lib/savedir.c | 131 + lib/stpcpy.c | 32 + lib/strdup.c | 43 + lib/strftime.c | 469 ++++ lib/strspn.c | 40 + lib/strstr.c | 44 + lib/strtol.c | 186 ++ lib/wait.h | 41 + lib/xgetcwd.c | 78 + lib/xmalloc.c | 95 + lib/xstrdup.c | 36 + locate/Makefile.am | 42 + locate/Makefile.in | 222 ++ locate/bigram.c | 115 + locate/code.c | 214 ++ locate/frcode.c | 170 ++ locate/locate.1 | 78 + locate/locate.c | 402 +++ locate/locatedb.5 | 106 + locate/locatedb.h | 42 + locate/updatedb.1 | 78 + locate/updatedb.sh | 148 ++ mkinstalldirs | 32 + stamp-h.in | 1 + testsuite/Makefile.am | 36 + testsuite/Makefile.in | 118 + testsuite/config/unix.exp | 109 + testsuite/inputs/eof.xi | 5 + testsuite/inputs/eofstr.xi | 5 + testsuite/inputs/files.xi | 22 + testsuite/inputs/files0.xi | Bin 0 -> 753 bytes testsuite/inputs/quotes.xi | 5 + testsuite/xargs.gnu/0n3.exp | 1 + testsuite/xargs.gnu/0n3.xo | 8 + testsuite/xargs.gnu/nothing.exp | 1 + testsuite/xargs.gnu/nothing.xo | 1 + testsuite/xargs.gnu/r.exp | 1 + testsuite/xargs.posix/hithere.exp | 1 + testsuite/xargs.posix/hithere.xo | 2 + testsuite/xargs.posix/n3.exp | 1 + testsuite/xargs.posix/n3.xo | 8 + testsuite/xargs.posix/quotes.exp | 1 + testsuite/xargs.posix/quotes.xo | 2 + testsuite/xargs.posix/s47.exp | 1 + testsuite/xargs.posix/s47.xo | 18 + testsuite/xargs.posix/s470.exp | 1 + testsuite/xargs.posix/s470.xo | 2 + testsuite/xargs.posix/s48.exp | 1 + testsuite/xargs.posix/s48.xo | 22 + testsuite/xargs.posix/s6.exp | 1 + testsuite/xargs.sysv/eEOF.exp | 1 + testsuite/xargs.sysv/eEOF.xo | 1 + testsuite/xargs.sysv/eof.exp | 1 + testsuite/xargs.sysv/eof.xo | 1 + testsuite/xargs.sysv/iARG.exp | 1 + testsuite/xargs.sysv/iARG.xo | 22 + testsuite/xargs.sysv/iquotes.exp | 1 + testsuite/xargs.sysv/iquotes.xo | 5 + testsuite/xargs.sysv/l1n4.exp | 1 + testsuite/xargs.sysv/l1n4.xo | 6 + testsuite/xargs.sysv/l2.exp | 1 + testsuite/xargs.sysv/l2.xo | 11 + xargs/Makefile.am | 9 + xargs/Makefile.in | 156 ++ xargs/xargs.1 | 112 + xargs/xargs.c | 918 +++++++ 123 files changed, 35687 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 acconfig.h create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in create mode 100644 doc/Makefile.am create mode 100644 doc/Makefile.in create mode 100644 doc/find.info create mode 100644 doc/find.info-1 create mode 100644 doc/find.info-2 create mode 100644 doc/find.texi create mode 100644 doc/perm.texi create mode 100644 doc/texinfo.tex create mode 100644 find/Makefile.am create mode 100644 find/Makefile.in create mode 100644 find/defs.h create mode 100644 find/find.1 create mode 100644 find/find.c create mode 100644 find/fstype.c create mode 100644 find/parser.c create mode 100644 find/pred.c create mode 100644 find/tree.c create mode 100644 find/util.c create mode 100644 find/version.c create mode 100755 install-sh create mode 100644 lib/Makefile.am create mode 100644 lib/Makefile.in create mode 100644 lib/alloca.c create mode 100644 lib/dirname.c create mode 100644 lib/error.c create mode 100644 lib/fileblocks.c create mode 100644 lib/filemode.c create mode 100644 lib/fnmatch.c create mode 100644 lib/fnmatch.h create mode 100644 lib/getline.c create mode 100644 lib/getopt.c create mode 100644 lib/getopt.h create mode 100644 lib/getopt1.c create mode 100644 lib/idcache.c create mode 100644 lib/listfile.c create mode 100644 lib/memcmp.c create mode 100644 lib/memset.c create mode 100644 lib/mktime.c create mode 100644 lib/modechange.c create mode 100644 lib/modechange.h create mode 100644 lib/modetype.h create mode 100644 lib/nextelem.c create mode 100644 lib/pathmax.h create mode 100644 lib/regex.c create mode 100644 lib/regex.h create mode 100644 lib/savedir.c create mode 100644 lib/stpcpy.c create mode 100644 lib/strdup.c create mode 100644 lib/strftime.c create mode 100644 lib/strspn.c create mode 100644 lib/strstr.c create mode 100644 lib/strtol.c create mode 100644 lib/wait.h create mode 100644 lib/xgetcwd.c create mode 100644 lib/xmalloc.c create mode 100644 lib/xstrdup.c create mode 100644 locate/Makefile.am create mode 100644 locate/Makefile.in create mode 100644 locate/bigram.c create mode 100644 locate/code.c create mode 100644 locate/frcode.c create mode 100644 locate/locate.1 create mode 100644 locate/locate.c create mode 100644 locate/locatedb.5 create mode 100644 locate/locatedb.h create mode 100644 locate/updatedb.1 create mode 100644 locate/updatedb.sh create mode 100755 mkinstalldirs create mode 100644 stamp-h.in create mode 100644 testsuite/Makefile.am create mode 100644 testsuite/Makefile.in create mode 100644 testsuite/config/unix.exp create mode 100644 testsuite/inputs/eof.xi create mode 100644 testsuite/inputs/eofstr.xi create mode 100644 testsuite/inputs/files.xi create mode 100644 testsuite/inputs/files0.xi create mode 100644 testsuite/inputs/quotes.xi create mode 100644 testsuite/xargs.gnu/0n3.exp create mode 100644 testsuite/xargs.gnu/0n3.xo create mode 100644 testsuite/xargs.gnu/nothing.exp create mode 100644 testsuite/xargs.gnu/nothing.xo create mode 100644 testsuite/xargs.gnu/r.exp create mode 100644 testsuite/xargs.posix/hithere.exp create mode 100644 testsuite/xargs.posix/hithere.xo create mode 100644 testsuite/xargs.posix/n3.exp create mode 100644 testsuite/xargs.posix/n3.xo create mode 100644 testsuite/xargs.posix/quotes.exp create mode 100644 testsuite/xargs.posix/quotes.xo create mode 100644 testsuite/xargs.posix/s47.exp create mode 100644 testsuite/xargs.posix/s47.xo create mode 100644 testsuite/xargs.posix/s470.exp create mode 100644 testsuite/xargs.posix/s470.xo create mode 100644 testsuite/xargs.posix/s48.exp create mode 100644 testsuite/xargs.posix/s48.xo create mode 100644 testsuite/xargs.posix/s6.exp create mode 100644 testsuite/xargs.sysv/eEOF.exp create mode 100644 testsuite/xargs.sysv/eEOF.xo create mode 100644 testsuite/xargs.sysv/eof.exp create mode 100644 testsuite/xargs.sysv/eof.xo create mode 100644 testsuite/xargs.sysv/iARG.exp create mode 100644 testsuite/xargs.sysv/iARG.xo create mode 100644 testsuite/xargs.sysv/iquotes.exp create mode 100644 testsuite/xargs.sysv/iquotes.xo create mode 100644 testsuite/xargs.sysv/l1n4.exp create mode 100644 testsuite/xargs.sysv/l1n4.xo create mode 100644 testsuite/xargs.sysv/l2.exp create mode 100644 testsuite/xargs.sysv/l2.xo create mode 100644 xargs/Makefile.am create mode 100644 xargs/Makefile.in create mode 100644 xargs/xargs.1 create mode 100644 xargs/xargs.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b0de6e5 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1328 @@ +Thu Nov 3 09:23:33 1994 David J. MacKenzie + + * Version 4.1. + + * locate/Makefile.am: Move updatedb from LIBSCRIPTS to SCRIPTS. + + * Makefile.am (distname): Change distribution name from find to + findutils. + + * testsuite/config/unix.exp: Don't abuse xfail; simulate it correctly. + + * locate/Makefile.am (CLEANFILES): Fix typo. + +Wed Nov 2 15:11:52 1994 David J. MacKenzie + + * The big 4 0. + + * lib/listfile.c find/defs.h (list_file): Take a stream arg. + * find/pred.c (pred_ls): pass it. + * find/parser.c pred.c defs.h (parse_fls, pred_fls): New functions. + +Tue Oct 25 16:09:04 1994 David J. MacKenzie + + * find/pred.c (pred_fprintf): Flush output after \c. From Chapman + Flack. + + * find/parser.c (insert_fprintf): Warn about unrecognized \ and % + sequences. + +Tue Oct 18 00:03:10 1994 David J. MacKenzie + + * find/defs.h parser.c pred.c tree.c util.c: Globally change + "victim" to "primary". + + * find/parser.c (insert_fprintf): For 'c' format, don't lose the + need_stat information. From Chapman Flack. + + * doc/find.texi perm.texi: New files. + * configure.in: Configure the doc directory. + + * find/pred.c (pred_regex): Check that the regex matched the whole + file name. + +Wed Oct 12 17:13:47 1994 David J. MacKenzie (djm@duality.gnu.ai.mit.edu) + + * find/find.c (main): Tell what the invalid arg is. + From Kaveh Ghazi. + +Fri Oct 7 12:33:24 1994 David MacKenzie + + * find/parser.c: Add -mount as an alias for -xdev. + From Klaus.Steinberger@physik.uni-muenchen.de (Klaus Steinberger). + + * lib/modechange.c: Make umask_value unsigned short. + + * xargs/xargs.c: Use symbolic constants in longopts. + From Chapman Flack. + +Wed Oct 5 11:23:09 1994 David MacKenzie + + * xargs/xargs.c (main, read_line, read_string, do_exec): Pass + along the lengths of the args. + (main): Calculate length of replace_pat. + (push_arg, do_insert): Use those lengths instead of calculating + them. + +Tue Oct 4 10:02:05 1994 David MacKenzie + + * locate/updatedb.sh Makefile.in: Add substitutions to get + the transformed program names. + + * xargs/xargs.c: Put back the global variables for now. + Rename some variables. Increase default args_per_exec. + Use boolean where applicable. + (main): Reduce default arg_max by 2048 for POSIX.2. + (read_string): Don't check EOF string. + (read_line, read_string): Take initial args size into account. + +Sat Oct 1 17:43:13 1994 David MacKenzie + + * find/pred.c (launch): Use pid_t. + + * xargs/xargs.c (EOF_STR): Define and use. + [__STDC__]: Delcare xrealloc and xmalloc using void *. + * find/defs.h: Likewise. + + * find/defs.h: Only declare stpcpy if !HAVE_STPCPY. + + * xargs/xargs.c: Replace most global variables with structure + pointers passed as arguments. Use pid_t. + * lib/wait.h: Include sys/wait.h if HAVE_SYS_WAIT_H. + * configure.in: Call AC_TYPE_MODE_T and AC_HEADER_SYS_WAIT. + + * xargs/xargs.c: Improve paging performance and memory + fragmentation by building command arguments in a pre-allocated + buffer and re-implementing the child pid list as an expandable + array. From tsi@gpu.srv.ualberta.ca (Marc Aurele La France). + +Thu Sep 29 11:38:07 1994 David J. MacKenzie (djm@geech.gnu.ai.mit.edu) + + * xargs/xargs.c [__STDC__]: Prototype declarations. + +Wed Sep 28 11:25:53 1994 David J. MacKenzie (djm@duality.gnu.ai.mit.edu) + + * find/fstype.c [AFS, __STDC__]: Fix definition of _VICEIOCTL. + +Tue Sep 27 08:14:27 1994 David MacKenzie + + * find/fstype.c (fstype_to_string): Add more cases. Use + INITMOUNTNAMES if defined. + * find/defs.h: Change boolean typedef from char to int. + * configure.in: Check for mktime. + +Tue Sep 27 01:20:28 1994 Kaveh R. Ghazi (ghazi@noc.rutgers.edu) + + * configure.in: Add AC_HEADER_STAT. + * lib/listfile.c, lib/modetype.h: Add STAT_MACROS_BROKEN. + + * find/pred.c: Move the inclusion of defs.h ahead of the first + test of _POSIX_VERSION. + + * lib/xgetcwd.c: Remove _POSIX_VERSION, rely only on HAVE_GETCWD. + +Mon Sep 26 16:43:01 1994 David MacKenzie + + * configure.in: Add AC_CONFIG_HEADER. + * find/*.c locate/*.c xargs/*.c: Include config.h. + * locate/updatedb.sh: Add --version; --old -> --old-format. + +Sun Sep 25 23:43:37 1994 David MacKenzie + + * find/* [__STDC__]: Prototype declarations. + + * locate/updatedb.sh: Account for renaming code and frcode. + + * find/find.c (process_path): Store dev and ino of directories in + current branch to avoid symlink loops. From DJ Delorie + . + (process_dir): If following symlinks, don't cd to ..; instead, + cd to the starting directory and then to the parent directory. + (main) [HAVE_FCHDIR]: Save the dev, ino of the starting directory. + (process_top_path) [HAVE_FCHDIR]: Use it. + * find/pred.c (launch) [HAVE_FCHDIR]: Likewise. + * defs.h [HAVE_FCHDIR]: Declare starting_desc instead of starting_dir. + * configure.in: Check for dev_t, ino_t, fchdir, fcntl.h. + +Fri Sep 23 11:55:38 1994 David MacKenzie + + * lib/listfile.c: Change #ifdef S_IFLNK to #ifdef S_ISLNK. + From Andreas Luik . + +Thu Sep 22 11:42:40 1994 David MacKenzie + + * locate/locate.c (last_literal_end): Dynamically allocate enough + memory for the subpattern. + +Wed Sep 21 06:12:56 1994 David MacKenzie + + * locate/locate.c (locate): Warn if database is >8 days old. + From Ian Lance Taylor. + + * xargs/xargs.c (do_exec), find/pred.c (launch): Set SIGCHLD to + default. From tsi@gpu.srv.ualberta.ca (Marc Aurele La France). + * find/find.c pred.c util.c lib/listfile.c: Remove fflush(stdout) + calls before error. error does it, and doesn't trash errno. + From tsi@gpu.srv.ualberta.ca (Marc Aurele La France). + + * find/fstype.c (filesystem_type_uncached): Don't trust mtab dev + number on HPUX. From Andreas Luik . + (filesystem_type_uncached): Don't cache unknown file system + types. From casper@fwi.uva.nl (Casper Dik). + + * locate/updatedb.sh: Collect results in temp file and rename it + atomically. From Andreas Luik . + + * xargs/xargs.c (parse_num): Print a long using %ld. From Jim + Meyering. + + * find/defs.h find.c parser.c pred.c util.c, lib/nextelem.c: + Emulate strchr and strrchr with index and rindex, not vice versa. + + Remove man directory; move man pages to the directories of the + programs they document. + + * locate/frcode.c: Renamed from code.c. + * locate/frcode.c (put_short): Renamed from puthalfword. + * locate/locate.c (get_short): Renamed from gethalfword. + (last_literal_end): Renamed from patprep. + (locate): Recognize old-format databases too. + * locate/locatedb.h: Add defines for old-format databases. + * locate/bigram.c locate/code.c: Put back programs to create + old-format databases. + * locate/updatedb.sh: Take --old option to use them. + +Tue Sep 20 15:41:11 1994 David MacKenzie + + * configure.in: Update for Autoconf v2. + * find/pred.c lib/savedir.c: Use new symbols for dir header. + * locate/updatedb.sh: Add --help option. + +Sun Feb 13 11:21:58 1994 Jim Meyering (meyering@comco.com) + + * man/Makefile.in [man1ext, man5ext]: Set man5ext (not man1ext) to 5. + +Sun Aug 1 22:30:55 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * bigram.c: File removed. + * getline.c, memcmp.c, locatedb.h, updatedb.1, locatedb.5: New files. + * updatedb.sh: Take command line options. + Don't do bigram compression. + * code.c, locate.c: Don't do bigram compression. + Write and read counts in network byte order. + Handle arbitrarily long paths. + Use a magic number at the start of the databases. + +Thu Jul 29 20:44:53 1993 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * Makefile.in (config.status): Run config.status --recheck, not + configure, to get the right args passed. + +Thu Jul 22 12:53:12 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * listfile.c (list_file): Print inode as a long. + +Wed Jul 14 14:14:45 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * fstype.c [STDC_HEADERS]: Include stdlib.h. + + * Move unistd.h include from parser.c and pred.c to defs.h. + +Wed Jun 30 14:14:47 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * updatedb.sh: Construct PRUNEREGEX from PRUNEPATHS with sed. + Prune /afs. Change NFSUSER to NETUSER and NFSPATHS to NETPATHS. + +Tue Jun 29 12:19:58 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * pred.c (pred_fprintf): Abort in switch if `c' is not A, C, or T. + +Mon Jun 28 00:18:52 1993 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * fstype.c (in_afs) [AFS]: New function, derived from code by + Sanjay Ramamurthy . + (filesystem_type_uncached) [AFS]: Call it if the fs type is + otherwise unknown. + + * parser.c (parse_size): Recognize b and w suffixes for dd + compatibility. + + * code.c (puthalfword): New function. + (main): Call it. + * locate.c (gethalfword): New function. + (locate): Call it. + From ifado!wb@germany.eu.net (Wilhelm B. Kloke). + + * listfile.c: Include pathmax.h. + (get_link_name): Always allocate PATH_MAX + 1 bytes for + readlink buffers. + * pred.c (pred_fprintf, insert_lname): Call get_link_name. + + * fstype.c (filesystem_type, filesystem_type_uncached), + listfile.c (list_file): Take an arg for the path to access. + * pred.c (pred_ls, pred_fstype, pred_fprintf): Pass it. + + * find.c (process_dir): Renamed from scan_directory. + + Changes from jrs@world.std.com (Rick Sladkey) to chdir into + subdirectories instead of using string concatenation, for speed: + * find.c (process_top_path): New function. + (main): Call it, and xgetcwd. + (process_path, scan_directory): Take new arg, the pathname + relative to ".". Use it and pass it on. + * pred.c (pred_and, pred_comma, pred_negate, pred_or, + pred_xtype, pred_fprintf, pred_empty, insert_lname): + access rel_pathname instead of pathname. + (launch): chdir to starting_dir. + * defs.h: Declare rel_pathname and starting_dir. + * find.c: Define them. + + * xgetcwd.c: New file. + + * updatedb.sh: Recognize -fstype NFS as well as nfs. + * locate.c (patprep): Skip trailing character classes correctly. + From luik@pharao.stgt.sub.org (Andreas Luik). + + * parser.c (parse_group): Make gid a gid_t, not short or int. + (parse_nogroup): Cast gid to unsigned when using it as an array index. + (parse_user, parse_nouser): Similar changes for uid. + * defs.h: Use uid_t and gid_t. + + * parser.c (parse_help): New function. + (parse_table): Add --version, -help, and --help options. + Rename struct parser_table_t to struct parser_table. + (parse_version): Exit after printing message, on + stdout not stderr. + + * xargs.c, locate.c (main, usage): Add --version and --help + options. + +Wed Mar 31 22:39:57 1993 Jim Meyering (meyering@comco.com) + + * parser.c: Define isascii macro to be 1 also if STDC_HEADERS. + * xargs.c: Ditto. + +Wed Mar 31 16:04:07 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * pred.c (pred_fprintf): If curdepth is 0, don't nuke + segment->text; nuke cp. + +Mon Mar 29 15:57:20 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * Version 3.8. + +Fri Mar 26 16:36:59 1993 David J. MacKenzie (djm@hal.gnu.ai.mit.edu) + + * pred.c (pred_ilname, pred_iname, pred_ipath): New functions. + (pred_table): Add them. + (insert_lname): New function. + (pred_lname): Call it. + * parser.c (parse_ilname, parse_iname, parse_ipath, + parse_iregex): New functions. + (parse_table): Add them. + (insert_regex): New function. + (parse_regex): Call it. + + * fstype.c (filesystem_type): Cache previous result. + (filesystem_type_uncached): New function. + pred.c (pred_fstype, pred_fprintf): Adjust callers to not cache. + + * parser.c: Don't define const. + + * fstype.c [FSTYPE_STATFS] (fstype_to_string): #ifdef + MOUNT_PC for 386bsd. + +Thu Mar 25 18:32:24 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * parser.c (parse_regex): If ignore_case, set up a translate + table for the regex. + + * defs.h: Include string.h or strings.h. + * find.c fstype.c parser.c pred.c util.c: Don't. + + * nextelem.c [index]: Don't redefine. + +Wed Mar 24 17:47:10 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * xargs.c (wait_for_proc): Exit with a nonrunnable command's exit + status, not the wait status value. From + Andreas Schwab . + + * parser.c (make_segment, insert_fprintf), pred.c + (pred_fprintf): Add '%F' to print filesystem type. + + * parser.c (parse_fprintf): Check if second arg is missing. + +Tue Mar 23 13:18:08 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * pred.c (pred_fprintf): For %P, don't move past an assumed + slash if the ARGV element ends with one, because in that case + we didn't add one. + + * parser.c (parse_printf): Check for missing arg. + From smj@cats.com (Steve James). + + * parser.c: Add #ifdef around atol decl for Linux. + +Fri Dec 11 08:17:07 1992 Jim Meyering (meyering@comco.com) + + * defs.h: Remove dcl of process_path. + * find.c: Put dcl of process_path here. Make a few functions + and file-scope variables static. + * parser.c, tree.c: Declare most functions static. + + * locate.c: Use `required_argument' macro in dcl of longopts. + * bigram.c, code.c, locate.c, xargs.c: Make most functions and + file-scope variables static. + + * parser.c, xargs.c: Guard ctype.h macros with isascii. + + * find.c: Add declarations for opt_expr and mark_stat. + * tree.c (set_new_parent): Add empty default clause to enum swicth. + * locate.c (main): Parenthesize for gcc -Wall. + * xargs.c (push_arg): Parenthesize for gcc -Wall. + +Tue Nov 24 08:04:36 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * find.c, fstype.c, parser.c, pred.c, util.c, listfile.c, + nextelem.c, xargs.c, bigram.c, code.c, locate.c: Use + HAVE_STRING_H, not USG. + + * pred.c: Use SYSDIR and NDIR instead of USG. + Define direct as dirent, not vice-versa. + +Fri Oct 9 02:15:17 1992 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu) + + * xargs.c (main): Set orig_arg_max before possibly cutting + down arg_max. + +Thu Sep 10 19:25:35 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * parser.c: Always declare getgrent and getpwent. + +Mon Aug 24 12:54:16 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * xargs.c: Include sys/types.h before unistd.h. Use ARG_MAX + if it's defined. + * find.c, nextelem.c: Add missing decls. + From bde@runx.oz.au (Bruce Evans). + +Thu Jul 23 15:06:07 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Version 3.7. + +Tue Jul 14 00:16:52 1992 David J. MacKenzie (djm@apple-gunkies.gnu.ai.mit.edu) + + * pathmax.h: New file. + * bigram.c, code.c, locate.c: Use it. Use xmalloc instead of malloc. + +Sat Jul 11 22:31:46 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * nextelem.c: New file. + * locate.c (main): Use it to support a database path. + +Fri Jul 3 02:12:09 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * listfile.c, pred.c: Change FOO_MISSING to HAVE_FOO. + + * parser.c [_POSIX_SOURCE]: Define endpwent and endgrent as empty. + * listfile.c [!HAVE_ST_RDEV]: Print blanks. + From Jeffrey Siegal (jbs@congruent.com). + + * locate.c (locate): Check for EOF at top of loop, not middle. + * updatedb.sh: Remove duplication hack. + From Jay Plett. + +Wed Jun 10 15:04:23 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * pred.c (pred_amin, pred_atime, pred_cmin, pred_ctime, + pred_mmin, pred_mtime, pred_used): Cast l_val to time_t before + comparing it to a time_t. From fpm@crash.cts.com (Frank Maclachlan). + + * locate.c (locate): Take the database path as an arg. + (main): Take an option to specify the database path. + (usage): New function. + + * updatedb.sh: Don't read from and write to the file-list file + in the same statement. + +Thu Jun 4 15:27:07 1992 David J. MacKenzie (djm@geech.gnu.ai.mit.edu) + + * Version 3.6. + +Wed May 20 00:05:13 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * xargs.c: Include sys/param.h before limits.h, not after. + + * listfile.c: If we include a header file specifically to get + major et al., assume we have them. + +Tue May 12 01:09:33 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * locate.c (locate): Don't give fnmatch FNM_PERIOD (it's useless). + + * parser.c (parse_path): New function. + * pred.c (pred_path): New function. + * tree.c (opt_expr): Move -path like -name. + + * updatedb.sh: Duplicate the last entry in the file list so it + doesn't get ignored. + +Mon May 11 22:24:40 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * updatedb.sh: Allow many vars to be overridden in the environment. + + * locate.c, updatedb.sh: FCODES -> LOCATE_DB. + +Wed Apr 22 15:24:00 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * updatedb.sh: Use binprefix when calling find. + + * locate.c (locate): Use LOCATE_DB environ variable if set. + + * find.c (scan_directory): Allow for dirs having negative + st_size (NFS mount points on VAX 4.3BSD+NFS). + From metcalf@catfish.lcs.mit.edu (Chris Metcalf). + +Sat Apr 18 15:42:52 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * fstype.c: Rename FS_* to FSTYPE_*. Support Dynix's + name for the mount table. + +Sun Mar 8 23:21:25 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * listfile.c (list_file): Allow a slop factor for deciding what + is in the future. + +Tue Feb 25 16:24:15 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * Version 3.5. + +Sat Feb 22 08:43:01 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * tree.c (set_new_parent): Initialize need_stat field. + + * defs.h (struct predicate): Define p_name unconditionally. + +Fri Feb 21 15:28:43 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * tree.c (opt_expr): Preserve expression precedence when + rearranging expression tree. + (set_new_parent): New function. + (mark_stat): Set need_stat whether or not it's been set + earlier in the expression. + All from Tim Wood. + +Mon Feb 17 10:20:38 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * updatedb.sh: Use current value of TMPDIR if already set. + From Dana Jacobsen (jacobsd@cs.orst.edu). + + * pred.c: Include pwd.h and grp.h after unistd.h, not before. + Apparently needed on ISC 2.2. From Juha Takala . + +Thu Feb 13 10:52:31 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * modetype.h: Don't define S_IFMT, because doing so breaks pred_type + on plain POSIX.1 systems. + +Sat Feb 8 00:58:13 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * Version 3.4. + +Fri Feb 7 21:35:58 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * defs.h: Don't declare process_path. + * find.c [DEBUG_STAT] (debug_stat): New function. + (main) [DEBUG_STAT]: Call it. + (process_path): Return a value. + (scan_directory): Count number of subdirs seen so far. + * parser.c (pred_and, pred_close, pred_comma, pred_negate, + pred_open, pred_or): Don't need stat. + * util.c (get_new_pred_chk_op): Implicit AND doesn't need stat. + +Fri Jan 17 21:51:18 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * Version 3.3. + +Tue Dec 24 02:16:49 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * wait.h, listfile.c, xargs.c, bigram.c, code.c, locate.c: + Change POSIX ifdefs to HAVE_UNISTD_H and _POSIX_VERSION. + +Wed Dec 18 16:37:17 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * listfile.c: Use MAJOR_IN_MKDEV and MAJOR_IN_SYSMACROS to + find major and minor. + + * code.c, bigram.c, locate.c, xargs.c: Use LIMITS_H_MISSING + instead of LIMITS_MISSING. + +Sat Dec 7 06:13:26 1991 David J. MacKenzie (djm at frob.eng.umd.edu) + + * parser.c (parse_prune): No stat needed for prune. + (insert_fprintf, make_segment): Possibly no stat needed, + depending on which information is printed. + + * xargs.c (usage): Document long-named options as starting + with -- instead of +. + + * parser.c [!POSIX]: Declare getgrent and getpwent. + + * fstype.c (filesystem_type): Only check MNTTYPE_IGNORE if + it's defined. From Rainer Orth. + + * Add leaf node optimization suggested by Matthew Farwell + . Add -noleaf option to disable it. + Adapt parts of Tim Wood's work to current sources. + * find.c (scan_directory): New function, from code in process_path. + (process_path): Take an arg indicating whether the pathname + is a leaf non-directory, instead of a redundant current level. + * parser.c (parse_noleaf): New function. + (parse_print, parse_name, etc.): Set need_stat = false. + +Wed Nov 2 13:34:32 1990 Tim Wood at home (tim at axolotl.UUCP) + + * pred.c: Call stat() if an AND, OR or NOT node tells you to. + * find.c: Limit calls to stat(). + +Wed Oct 3 17:30:39 1990 Tim Wood at home (tim at axolotl.UUCP) + + * tree.c (opt_expr): New function to rearrange predicates within + a conjunction/disjunction so that non-inode (-name, -regex) ones + are executed first. + * pred.c: Make some supporting tree structure changes for opt_expr(). + +Sat Dec 7 00:36:06 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * xargs.c (do_exec): Simplify test for which exit + status to use if exec fails. + +Fri Dec 6 18:24:06 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * listfile.c (list_file): POSIX_ME_HARDER -> POSIXLY_CORRECT. + +Thu 24 Oct 1991 21:33:21 Jim Meyering (meyering at churchy.gnu.ai.mit.edu) + + * pred.c (pred_fprintf): Don't print "\n" unless it's in the + format string. + +Mon Oct 21 22:30:35 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * defs.h, parser.c, pred.c: Rename some types that conflict + with reserved POSIX.1 namespace (ended in _t). + +Thu Oct 17 22:39:06 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * xargs.c: Don't determine memory.h based on POSIX, which + doesn't mention it. + +Sat Oct 5 16:11:05 1991 Jim Meyering (meyering at churchy.gnu.ai.mit.edu) + + * parser.c (parse_perm): Parse new `-perm +mode' notation. + * pred.c (pred_perm): Interpret same. + +Fri Sep 13 14:58:27 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu) + + * xargs.c [POSIX]: Always use sysconf to get ARG_MAX. + +Thu Sep 5 23:57:06 1991 David J. MacKenzie (djm at apple-gunkies) + + * bigram.c, code.c (main): Make path_max int, not unsigned. + * locate.c (main): Check for pathconf failure. + +Thu Sep 5 11:54:44 1991 Jim Meyering (meyering at churchy.gnu.ai.mit.edu) + + * parser.c (insert_fprintf): Add `\\' escape and fixed `%%' + interpretation. + * pred.c (pred_fprintf): fixed off-by-one indexing problem + when handling [gGuU] printf formats. + +Wed Aug 28 20:53:57 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * Version 3.2. + +Mon Aug 26 18:57:32 1991 David J. MacKenzie (djm at pogo.gnu.ai.mit.edu) + + * bigram.c, code.c: Fix handling of PATH_MAX. + Check for anomalous input line lengths. + From Bruce Evans. + +Fri Aug 23 11:00:18 1991 David J. MacKenzie (djm at apple-gunkies) + + * pred.c (pred_fprintf): Round block number up to get K. + +Thu Aug 22 10:46:30 1991 David J. MacKenzie (djm at apple-gunkies) + + * pred.c (pred_fprintf, pred_lname) [_AIX]: Allocate PATH_MAX + byte for link object since st_size is wrong. + + * listfile.c (list_file): Don't convert blocks to kilobytes if + env. var POSIX_ME_HARDER is defined. + + * fstype.c [FS_AIX_STATFS]: New case. + + * configure: Make sure the sys/mount.h is the 4.4BSD version + with grep instead of just testing whether it exists. + + * listfile.c (list_file): Add 1 to number of 512-byte blocks + before dividing to get 1K blocks (so we round up, not down). + +Wed Aug 21 13:02:46 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * Version 3.1. + + * parser.c (parse_fprintf), pred.c (pred_fprintf): Add %k + conversion to print 1K blocks. + + * listfile.c: Print counts of 1K blocks, for consistency with + new fileutils release. Bad timing, there. + + * Version 3.0. + + * pred.c [VOID_CLOSEDIR]: Fake a return value for closedir, + which returns void on some systems, like Sequents. + * configure: Check sys/dir.h for 'void closedir'. + +Thu Aug 15 16:07:46 1991 David J. MacKenzie (djm at frob) + + * modetype.h: Define POSIX.1 stat stuff if missing. + * pred.c, parser.c, find.c, fstype.c: Don't define it. + * updatedb.sh: Use a variable substitution method like configure's. + * Makefile.in: Add datadir variable to separate programs from + data file. + + * parser.c, pred.c: Rename -fulldays to -daystart. + + * defs.h, find.c, parser.c, pred.c: Add many new predicates + from Jay Plett (jay@princeton.edu). + +Wed Aug 14 14:37:06 1991 David J. MacKenzie (djm at bleen) + + * parser.c (parse_size), pred.c (pred_size), defs.h (struct + size_t): Allow `k' to follow number to measure size in Kbytes. + * parser.c (parse_size, get_num, insert_num), defs.h (struct + size_t): Use enum comparison_type instead of short. + +Fri Aug 9 00:49:32 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * xargs.c (read_line): Use isblank, to support POSIX locales. + + * fstype [FS_STATVFS]: New code for SVR4, from slootman@dri.nl + (Paul Slootman). + * configure: Figure out when to use it. + + * configure: Check for st_blocks in struct stat. + * listfile.c (ST_NBLOCKS): New macro. + (list_file): Print file's block count. + Use S_ISLNK instead of S_IFLNK. + * fileblocks.c: New file, from fileutils. + +Thu Aug 8 17:20:19 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * parser.c, pred.c [CACHE_IDS]: Optional code to turn uid and + gid lookups into table lookups. + +Wed Aug 7 00:22:29 1991 David J. MacKenzie (djm at wheat-chex) + + * configure, Makefile.in's: Support +srcdir option via VPATH. + Clean up clean targets. + +Sat Jul 20 01:11:51 1991 David J. MacKenzie (djm at apple-gunkies) + + * configure: Filter /etc and /usr/etc from path. + + * xargs.c (wait_for_proc): Fix handling of child exit status. + (main): Exit with a nonzero status if any child did. + + * pred.c (launch): Flush stdout and stderr before forking. + + * fstype.c (filesystem_type) [FS_MNTENT]: Skip entries of + type "ignore". + +Fri Jul 19 22:53:42 1991 David J. MacKenzie (djm at bleen) + + * pred.c, locate.c: Use fnmatch instead of glob_match. + * fnmatch.c, fnmatch.h: New files. + + * Many files: Use string.h if STDC_HEADERS, as well as if USG. + + * locate.c, code.c, bigram.c: Possibly use pathconf to get + PATH_MAX. Use malloc to allocate path arrays. + + * xargs.c: Possibly use sysconf to get ARG_MAX. + (env_size): Make definition unconditional. + (main): Do arg_max adjustment that can't be done with + preprocessor now that ARG_MAX might be a function call. + (do_exec): Exit with status 126 or 127 after failed exec, for + POSIX.2 draft 11.1. + + * xargs.c, pred.c, listfile.c: Use POSIX, not UNISTD_MISSING. + * wait.h: Use POSIX, not WAIT_MACROS_MISSING. + + * COPYING: Use version 2. Update all files. + + * Replace Makefile and lib/Makefile with Makefile.in, + lib/Makefile.in and configure. Update README. + +Fri Apr 5 12:49:09 1991 David J. MacKenzie (djm at apple-gunkies) + + * Version 2.2. + +Fri Mar 15 20:44:45 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * xargs.c (main): Always run the command if some args are left over. + Rename some variables. + +Fri Jan 18 03:35:57 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * bigram.c, code.c, locate.c: Use LIMITS_MISSING, not + _POSIX_SOURCE, to decide whether to include limits.h. + + * parser.c, pred.c, listfile.c: Use POSIX, not _POSIX_SOURCE, + to decide whether to declare getpwuid and getgrgid. + + * xargs.c: Use POSIX, not _POSIX_SOURCE, to determine whether + to include memory.h. + +Sat Jan 12 04:12:34 1991 David J. MacKenzie (djm at wookumz.ai.mit.edu) + + * defs.h, find.c, parser.c, pred.c: Remove -permmask option. + +Thu Jan 10 04:32:52 1991 David J. MacKenzie (djm at albert.ai.mit.edu) + + * wait.h: Include sys/types.h to get pid_t. + + * xargs.c [USG && !STDC_HEADERS]: Only include memory.h if not + _POSIX_SOURCE. + +Tue Jan 1 23:53:32 1991 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Version 2.1. + +Wed Dec 26 03:25:51 1990 David J. MacKenzie (djm at apple-gunkies) + + * locate.c, bigram.c, code.c: Attempt to get max. path length + on more kinds of systems, incl. POSIX. + + * pred.c, listfile.c, xargs.c: Get some decls from unistd.h, + if available. + + * find.c, defs.h: Make `cur_day_start' a time_t, not long. + +Fri Dec 21 01:49:12 1990 David J. MacKenzie (djm at egypt) + + * defs.h, parser.c: Remove unused field from `struct exec_t'. + + * xargs.c: Add +no-run-if-empty option to cause the command to + not be run if the input is empty. + + * defs.h (struct exec_t): Change the array of offsets into an + array of `struct path_arg'. + * parser.c (insert_exec_ok): Fill in new fields, to allow "{}" + to be substituted (multiple times) anywhere in an arg to -exec + or -ok. + * pred.c (pred_exec): Add code to substitute "{}" within args. + (pred_ok): After prompting, just run pred_exec. + +Thu Dec 20 02:32:09 1990 David J. MacKenzie (djm at egypt) + + * fstype.c (filesystem_type) [FS_MNTENT]: Allow for optional + "0x" at front of "dev=" mount option, which amd puts there but + Sun automounter doesn't. + +Sat Dec 15 19:01:12 1990 David J. MacKenzie (djm at egypt) + + * find.c (main), util.c (usage): Make directory args optional, + defaulting to ".". + +Sat Dec 15 18:36:29 1990 David J. MacKenzie (djm at apple-gunkies) + + * listfile.c: Define major and minor if not defined (as in POSIX). + +Mon Dec 3 01:04:35 1990 David J. MacKenzie (djm at alborz) + + * find.c, fstype.c, parser.c, pred.c, util.c: Flush stdout before + writing to stderr, in case they have been redirected to the + same file descriptor. + + * pred.c (launch): Use POSIX wait macros from wait.h. + + * xargs.c (print_xargs): Read from tty_stream, not stdin. + +Tue Nov 20 16:48:24 1990 David J. MacKenzie (djm at apple-gunkies) + + * Version 2.0. + + * fstype.c [FS_USG_STATFS]: New code. + [FS_STATFS]: For symlinks, statfs the directory the link is in + instead of the link. + * Various files: Conditionalize some declarations on + STDC_HEADERS or _POSIX_SOURCE. + +Fri Nov 16 12:24:43 1990 David J. MacKenzie (djm at egypt) + + * modetype.h: New file. + parser.c, pred.c: Use it. + +Thu Nov 15 18:05:54 1990 David J. MacKenzie (djm at apple-gunkies) + + * xmalloc.c: New file from fileutils. + + * fstype.c (fstype_to_string): Add case for MFS. + (filesystem_type): Take a pathname as a second arg. + [FS_STATFS] return "unknown" instead of exiting if statfs + fails because of ENOENT. + * pred.c (pred_fstype): Pass the pathname. Set current_dev. + * find.c (process_path): Make root_dev local again. + +Mon Nov 12 02:54:00 1990 David J. MacKenzie (djm at apple-gunkies) + + * pred.c (pred_fstype): Free old fs type. + + * fstype.c, pred.c (pred_fstype), parser.c (parse_fstype): + Reread the file system type info. every time a filesystem + mount point is crossed, to allow for automounting. + + * xstrdup.c: New file from fileutils. + + * find.c (process_path): Rename root_dev to current_dev and + make it global, for -fstype. + + * wait.h: New file taken from xargs.c. + + * xargs.c: Make limits.h vs. sys/param.h conditional on + LIMITS_MISSING instead of USG, to accomodate SVR2. + +Thu Nov 8 11:52:22 1990 David J. MacKenzie (djm at apple-gunkies) + + * parser.c, pred.c, listfile.c: If not _POSIX_SOURCE, declare + getpwuid and getgrgid. Use them. + + * listfile.c: If not _POSIX_SOURCE, define S_ISDIR and + S_ISBLK. Use them. + + * find.c: Use S_ISDIR instead of S_IFDIR, and define if not + _POSIX_SOURCE. + + * Makefile: Define AR and RANLIB and pass to child makes. + lib/Makefile: Use them. + + * xargs.c (WIFSIGNALED): Redefine so it works. + +Mon Nov 5 00:02:01 1990 David J. MacKenzie (djm at apple-gunkies) + + * find.c (process_path): For -xdev, process filesystem + mountpoints (but don't descend them), instead of skipping them + entirely. + + * find.c, parser.c, defs.h: Add -follow predicate. + + * xargs.c: Change ifdefs to support STDC POSIX systems. + +Sat Nov 3 20:18:05 1990 David J. MacKenzie (djm at apple-gunkies) + + * xargs.c (do_exec): Child process exits with status 255, not + 127, if command can't be run. + +Fri Nov 2 02:11:42 1990 David J. MacKenzie (djm at apple-gunkies) + + * xargs.c: Exit with status 127 if running commmand fails, as + required by POSIX. + + * fstype.c: Support -fstype for Ultrix (-DFS_GETMNT). + Sun/BSD code is now -DFS_MNTENT. + +Thu Nov 1 13:06:01 1990 David J. MacKenzie (djm at egypt) + + * Reorganize into subdirectories and add xargs. Rewrite Makefiles. + + * find.c (process_path, main): Allow a maxdepth of 0, meaning + only process command line args. + + * parser.c, pred.c: Add -print0 predicate. + + * xargs.c: Add -0 option and long options. Move standard + library functions into separate files. Use error instead of + fatal and fprintf/perror. Use POSIX macros for examining exit + status from wait. + (read_string): New function. + +Fri Sep 21 10:21:09 1990 David J. MacKenzie (djm at apple-gunkies) + + * find.c (process_path): Take DEPTH as an arg instead of ROOT, + and change callers. + +Thu Sep 20 23:58:47 1990 David J. MacKenzie (djm at apple-gunkies) + + * parser.c (parse_maxdepth): New function. + * find.c (process_path): If -maxdepth was given, don't go + more than that many levels deep. + * defs.h: Declare maxdepth. + +Wed Sep 12 02:12:31 1990 David J. MacKenzie (djm at apple-gunkies) + + * parser.c: Add -not as synonym for !. + +Sun Aug 26 06:16:08 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * Makefile (TAGS): New target. + +Sun Aug 12 00:32:01 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * xargs.c (main): Tell getopt to not permute. + +Sat Aug 4 21:43:45 1990 David J. MacKenzie (djm at pogo.ai.mit.edu) + + * parser.c (parse_perm), pred.c (pred_perm): Always compare + bits 07777. + + * locate.c, Makefile: Rename 'fastfind' program to 'locate', + following comment in POSIX.2 draft 10 rationale for find. + +Wed Jul 25 18:45:03 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * listfile.c (getuser, getgroup): Make uid and gid unsigned + short, not int. + +Mon Jul 16 13:40:13 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * defs.h: Don't declare fprintf and printf, in case they have + prototypes in stdio.h (important for functions that use stdarg). + +Sun Jul 15 23:39:39 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * parser.c (parse_and): New function, for compatibility. + +Wed Jul 4 00:17:57 1990 David J. MacKenzie (djm at apple-gunkies) + + * find.c (main): Only enclose expressions that produce no side + effects within `( ... )'. + +Tue Jul 3 01:59:39 1990 David J. MacKenzie (djm at apple-gunkies) + + * parser.c (strspn): Stop when end of string reached. + + * Version 1.2. + + * Move version number from Makefile to new file version.c. + * parser.c: Recognize new -version predicate. + + * find.c (main): If no predicates that produce output are + given, default to -print if the entire expression is true, not + just the last part of an alternation. + * Print the names of predicates with invalid arguments. + +Mon Jul 2 23:48:17 1990 David J. MacKenzie (djm at apple-gunkies) + + * pred.c: Don't check for invalid comparison types in numeric + predicate functions. + +Thu Jun 28 00:34:57 1990 David J. MacKenzie (djm at apple-gunkies) + + * parser.c (parse_regex): Set fastmap and translate before + compiling regex. + +Mon Jun 25 18:08:59 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * fastfind.c (fastfind): Initialize count to 0. + + * lib/updatedb.sh: Only do regex comparison on directories, + for speed. + + * listfile.c (list_file): Truncate user and group name to 8 chars. + +Sun Jun 24 13:51:27 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Version 1.1. + + * Makefile [DISTFILES]: Add COPYING. + +Fri Jun 22 03:54:27 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Version 1.0. + +Tue Jun 19 03:55:28 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * lib/updatedb.sh: Prune entries that match PRUNEREGEX. + Split up finding files from computing bigrams. + Use redirection instead of nonportable grep -s to detect sort + failure. Optionally search network filesystems as well as + local ones. + + * pred.c (pred_regex): Match against full pathname instead of + just last element. + * util.c (basename): Return "/", not "", if given "/". + + * find.c (process_path): Fix error in handling "/" directory. + +Mon Jun 18 01:49:16 1990 David J. MacKenzie (djm at apple-gunkies) + + * parser.c [STRSPN_MISSING] (strspn): New function. + +Sun Jun 17 13:54:09 1990 David J. MacKenzie (djm at apple-gunkies) + + * listfile.c: New file. + * parser.c (parse_ls): New function. + * pred.c (pred_ls): New function. + + * find.c (main): Remove interface to fastfind, to prevent + conflict with POSIX syntax. + * util.c (usage): Remove fastfind syntax from message. + * fastfind.c (main): New function. + * Makefile: Make fastfind a separate program. + + * find.c (main): Print correct message if a predicate arg is + missing. + + * parser.c (insert_exec_ok): Make args that start with a ';' but + contain other characters not terminate the command. + +Fri Jun 15 00:33:45 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * fstype.c: If MOUNTED isn't defined but MNT_MNTTAB is, use it + instead. True for HP/UX, at least. + +Thu Jun 14 10:10:25 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * savedir.c: New file; now find won't run out of file + descriptors in deep trees. + * find.c (process_path): Use savedir. + +Sat Jun 9 03:15:21 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * parser.c (parse_permmask): Allow symbolic mode masks. + (parse_perm): Free 'struct change' when done with it. + (get_oct): Function removed. + + * find.c (process_path): Allow arbitrarily-long filenames. + More efficient string copying. Initialize perm_mask to 07777 + instead of -1. + +Thu Jun 7 04:22:42 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Makefile, find.c: Use DIRENT to control whether + is used. + +Thu May 31 04:46:11 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * parser.c (parse_regex): New function. + * pred.c (pred_regex): New function. + + * fstype.c (read_mtab): If mtab entry has a "dev=" option + (like in SunOS 4.1), use it, so there is no need to stat the + special file later on. + (xatoi, strstr): New functions. + +Mon May 21 01:04:42 1990 David J. MacKenzie (djm at abyss) + + * lib/updatedb.sh: Put BINDIR in PATH. + + * fstype.c: Do nothing if MNTENT_MISSING is defined. + + * fstype.c: New file. + * parser.c (parse_fstype): New function. + * pred.c (pred_fstype): New function. + + * parser.c (parse_newer): Failure to stat -newer file is a + fatal error. + + * pred.c (pred_ok): Flush output before reading. Use getchar + instead of scanf. + + * pred.c (pred_prune): Return false if -depth given. + * find.c: Apply the predicates to the dir when -depth and + -prune are given. + +Sun May 20 19:55:30 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * pred.c (pred_prune): Set new global var `stop_at_current_level'. + * find.c (process_path): Test and reset it. + +Fri May 18 01:56:17 1990 David J. MacKenzie (djm at abyss) + + * modechange.c, modechange.h: New files. + * parser.c (parse_perm): Use mode_compile and mode_adjust to + parse arg, to allow symbolic mode for POSIX. + +Thu May 17 02:07:44 1990 David J. MacKenzie (djm at abyss) + + * parser.c (get_oct): Don't consider an empty string a valid number. + + * parser.c (parse_perm): If arg starts with '-', set flag bit + for special comparison (POSIX). + * pred.c (pred_perm): If flag bit set, compare s[ug]id & + sticky bits as well, and return true if the given perms are + set, ignoring other bits. + + * find.c: New global var `exit_status'. Use it. (POSIX) + * parser.c: Set `exit_status' if lstat on -newer file fails. + + * fastfind.c: New file. + * find.c (main): Call fastfind if given only 1 arg. + * util.c (usage): Update message. + * lib/{Makefile,updatedb.sh,bigram.c,code.c}: New files. + * Makefile: Add 'all' and 'install' targets. + +Wed May 16 23:23:35 1990 David J. MacKenzie (djm at abyss) + + * parser.c (parse_nogroup, parse_nouser): Implement. + * pred.c (pred_nogroup, pred_nouser): Implement. + +Mon May 14 00:09:35 1990 David J. MacKenzie (djm at abyss) + + * find.c: Add variable `stay_on_filesystem' for -xdev. + (process_path): Take an arg determining whether this call is + the root of a tree. Use lstat instead of stat. If + stay_on_filesystem, don't process a dir on a different + filesystem. + + * parser.c (parse_newer): Use lstat instead of stat. Is this right? + (parse_xdev): Set stay_on_filesystem. + + * parser.c: Add dummy parse_nogroup, parse_nouser, + parse_prune, and parse_xdev; to be written later. + * pred.c: Add dummy pred_nogroup, pred_nouser, pred_prune. + + * find.c: Support System V directory library/headers. + + * find.c (process_path): Don't continue with a file that stat + fails on. + + * defs.h, parser.c, pred.c: Change 'u_long' and 'u_short' to + 'unsigned long' and 'unsigned short'. + * find.c, defs.h: Remove 'convert_glob' variable. + * parser.c (parse_fullregex): Function removed. + (parse_name): Remove regular expression code. + (parse_type): Recognize sockets. + Add code to check for missing arguments to many parse_* functions. + * pred.c (pred_name): Use glob_match instead of regex. + +Sun May 13 17:45:09 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Replace fprintf, simple_error, and mem_error with error and + usage. + + * Fix string header includes for USG. + +Tue Mar 27 12:40:29 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * defs.h: Change some #defines to enums. + +Sun Mar 25 22:08:58 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * find.c (main): Don't take basename of argv[0]. + + * util.c (xmalloc): New function. + * find.c, parser.c, utils.c: Use xmalloc instead of malloc. + + * pred.c: Remove emulation of regex for BSD and use GNU + library version in regcmp.c instead. + * parser.c: Remove emulation of regcmp for BSD and use GNU + library version in regcmp.c instead. + * Makefile: Link with regex.o and regcmp.o. + Add a DISTFILES macro and dist target. + + * Indent source code. Move RCS logs to this file. + +Wed Mar 21 09:30:18 1990 David J. MacKenzie (djm at pogo.ai.mit.edu) + + * xargs.c: Indent. Comment and rename some global variables. + (main): Use getopt to parse options. Open new global var + `tty_stream' to /dev/tty if querying requested. + (print_args): Read response from tty_stream, not stdin. + (xmalloc): New function. + Global: Use xmalloc instead of malloc. + (usage): Revise message. + +87/02/22 20:01:20 20:01:20 cire (Eric B. Decker) + + * pred.c: added guts to pred_size + +87/02/22 00:59:42 00:59:42 cire (Eric B. Decker) + + * pred.c: added guts to perm and permmask. + +87/02/21 23:02:21 23:02:21 cire (Eric B. Decker) + + * pred.c: made pred_name only look at the last component of + the path. + +87/02/21 22:26:47 22:26:47 cire (Eric B. Decker) + + * pred.c: added guts to name. useds regex and regcmp to do + regular expression handling. + +87/02/21 00:17:21 00:17:21 cire (Eric B. Decker) + + * pred.c: added predicate newer + +87/02/20 11:40:07 11:40:07 cire (Eric B. Decker) + + * pred.c: added guts to pred_ok + +87/02/19 23:52:37 23:52:37 cire (Eric B. Decker) + + * pred.c: finished exec. + +87/02/22 20:01:09 20:01:09 cire (Eric B. Decker) + + * parser.c: added guts to parse_size + +87/02/22 00:59:16 00:59:16 cire (Eric B. Decker) + + * parser.c: added guts of perm and permmask. added getoct + routine for perm and permmask + +87/02/21 23:32:50 23:32:50 cire (Eric B. Decker) + + * parser.c: added -fre, -fullregex predicate to turn off + globbing conversion + +87/02/21 23:01:01 23:01:01 cire (Eric B. Decker) + + * parser.c: reworked name so the regexpr pattern includes $ at + the end to force globbing to work correctly. End of the + pattern refers to the end of the filename. + +87/02/21 22:25:34 22:25:34 cire (Eric B. Decker) + + * parser.c: added guts to name. uses a conversion from + globbing to regexp format. uses regex and regcmp to actually + to the comparison. + +87/02/21 00:17:11 00:17:11 cire (Eric B. Decker) + + * parser.c: added predicate newer + +87/02/20 11:39:35 11:39:35 cire (Eric B. Decker) + + * parser.c: added ok guts. consolidated exec and ok to using + insert_exec_ok + +87/02/19 00:20:54 00:20:54 cire (Eric B. Decker) + + * parser.c: minor bug in -fulldays predicate parser. It + should have set the flag full_days to true. + +87/02/22 00:58:32 00:58:32 cire (Eric B. Decker) + + * find.c: changed where we are setting perm_mask to -1. need + to make sure that this happens before every apply_predicate. + +87/02/21 23:32:11 23:32:11 cire (Eric B. Decker) + + * find.c: added error checking for no paths. better error + message if illegal ordering. + +87/02/21 22:19:58 22:19:58 cire (Eric B. Decker) + + * find.c: added global convert_glob + +87/02/22 20:00:12 20:00:12 cire (Eric B. Decker) + + * defs.h: added definition of BLKSIZE for size + +87/02/21 22:19:25 22:19:25 cire (Eric B. Decker) + + * defs.h: added global convert_glob for name diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..0338fbc --- /dev/null +++ b/INSTALL @@ -0,0 +1,167 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. 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 awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any 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 files + that `configure' created (so you can compile the package for a + different kind of computer), type `make distclean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Using a Different Build Directory +================================= + + You can compile the package in a different directory from the one +containing the source code. Doing so allows you to compile it on more +than one kind of computer at the same time. To do this, 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 `..'. + +Installation Names +================== + + 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'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Save the results of the tests in FILE instead of `config.cache'. + Set FILE to `/dev/null' to disable caching, for debugging + `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3063ab1 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,6 @@ +DIST_OTHER = COPYING ChangeLog TODO install-sh config.h.in stamp-h.in +SUBDIRS = lib find xargs locate doc testsuite +CONFIG_HEADER = config.h + +distname: + echo findutils-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q find/version.c` > $@ diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..5d0bda0 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,140 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +ACCONFIG = acconfig.h +SOURCES = +DIST_CONF = Makefile.am Makefile.in README INSTALL NEWS \ + configure configure.in ${ACLOCAL} ${ACCONFIG} ${CONFIG_TOP} \ + ${CONFIG_BOT} mkinstalldirs +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +DIST_OTHER = COPYING ChangeLog TODO install-sh config.h.in stamp-h.in +SUBDIRS = lib find xargs locate doc testsuite +CONFIG_HEADER = config.h + +all:: ${ALL} + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all install install-info uninstall check tags TAGS info dvi:: + for subdir in $(SUBDIRS); do \ + echo making $@ in $$subdir ; \ + (cd $$subdir; $(MAKE) $@); \ + done + +mostlyclean: mostlyclean-recursive mostlyclean-local + +clean: clean-recursive clean-local + +distclean: distclean-recursive + $(MAKE) distclean-local + +realclean: realclean-recursive + $(MAKE) realclean-local + +mostlyclean-recursive clean-recursive distclean-recursive realclean-recursive: + for subdir in $(SUBDIRS); do \ + (cd $$subdir; $(MAKE) `echo $@ | sed s/-recursive//`); \ + done + +mostlyclean-local: + +clean-local: mostlyclean-local + +distclean-local: clean-local + rm -f Makefile config.cache config.log config.status + rm -f ${CONFIG_HEADER} stamp-h + +realclean-local: distclean-local + +# For an explanation of the following Makefile rules, see node +# `Automatic Remaking' in GNU Autoconf documentation. +Makefile: Makefile.in config.status + CONFIG_FILES=$@ CONFIG_HEADERS= ./config.status +config.status: configure + ./config.status --recheck +${srcdir}/configure: configure.in ${ACLOCAL} + cd $(srcdir); autoconf + +${CONFIG_HEADER}: stamp-h +stamp-h: ${CONFIG_HEADER}.in config.status + CONFIG_FILES= CONFIG_HEADERS=${CONFIG_HEADER} ./config.status +${srcdir}/${CONFIG_HEADER}.in: stamp-h.in +${srcdir}/stamp-h.in: configure.in ${ACLOCAL} ${ACCONFIG} ${CONFIG_TOP} ${CONFIG_BOT} + cd $(srcdir); autoheader + date > $(srcdir)/stamp-h.in + +dist: $(DIST_FILES) $(DIST_DIRS) distname + rm -rf `cat distname`; mkdir `cat distname` + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file `cat distname`/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file `cat distname`/$$file;}; \ + done + for subdir in $(SUBDIRS); do \ + (cd $$subdir; $(MAKE) subdir=$$subdir $@); done + chmod -R a+r `cat distname` + tar -chozf `cat distname`.tar.gz `cat distname` + rm -rf `cat distname` distname + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +distname: + echo findutils-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q find/version.c` > $@ diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..c047834 --- /dev/null +++ b/NEWS @@ -0,0 +1,51 @@ +Major changes in release 4.1: + +* Distribution renamed to findutils. +* updatedb is now a user command, installed in $exec_prefix/bin + instead of $exec_prefix/libexec. +* A few problems in Makefiles and testsuite corrected. + +Major changes in release 4.0: + +* Documentation: +** Texinfo manual. +** Man page for updatedb. +** Man page for the locate database formats. + +* find: +** Takes less CPU time on long paths, because it uses chdir to descend + trees, so it does fewer inode lookups. +** Does not get trapped in symbolic link loops when -follow is given. +** Supports "-fstype afs" if you have /afs and /usr/afsws/include + and you configure using the --with-afs option. +** New action -fls FILE; like -ls but writes to FILE. + +* locate: +** Supports a new database format, which is 8-bit clean and + allows machines with diffent byte orderings and integer sizes to + share the databases. The new locate can also detect and read the + old database format automatically. The new databases are typically + 30% or more larger than the old ones (due to allowing all 8 bits in + file names). Search times are approximately the same, or faster on + some systems. +** Warns if a file name database is more than 8 days old. + +* updatedb: +** Takes command-line options. + +* xargs: +** Performance improved 10-20%. +** The EOF string is not used when -0 is given. +** Now has a test suite. Some minor bugs fixed as a result. + +Major changes in release 3.8: + +* case insensitive versions of -lname, -name, -path, -regex: + -ilname, -iname, -ipath, -iregex +* %F directive for -printf, -fprintf to print file system type + +Major changes in release 3.7: + +* locate can search multiple databases +* locate has an option to specify the database path +* updatedb no longer goes into an infinite loop with some versions of tail diff --git a/README b/README new file mode 100644 index 0000000..beff140 --- /dev/null +++ b/README @@ -0,0 +1,47 @@ +This package contains the GNU find, xargs, and locate programs. find +and xargs comply with POSIX 1003.2, as far as I know. They also +support some additional options, some borrowed from Unix and some +unique to GNU. + +See the file NEWS for a list of major changes in the current release. + +See the file INSTALL for compilation and installation instructions. + +Special configure options: + +--with-afs + Make find support "-fstype afs". Requires /afs, /usr/afsws/lib, and +/usr/afsws/include. configure doesn't add AFS support +automatically because it adds considerably to find's size, and the +AFS libraries need -lucb on Solaris, which breaks find. + +--enable-id-cache + Make tables of used UIDs and GIDs at startup instead of using +getpwuid or getgrgid when needed. Speeds up -nouser and -nogroup +unless you are running NIS or Hesiod, which make password and group +calls very expensive. + +To gain speed, GNU find avoids statting files whenever possible. +It does this by: +1. Checking the number of links to directories and not statting files +that it knows aren't directories until it encounters a test or action +that needs the stat info. +2. Rearranging the command line, where possible, so that it can do tests +that don't require a stat before tests that do, in hopes that the +latter will be skipped because of an OR or AND. (But it only does +this where it will leave the output unchanged.) + +The locate program and its helper programs are derived (heavily +modified) from James Woods' public domain fast-find code, which is +also distributed with the 4.3BSD find. Because POSIX.2 requires `find +foo' to have the same effect as `find foo -print', the fast-find +searching has been moved to a separate program, `locate'; the same +thing has been done in 4.4BSD. If you use locate, you should run the +included `updatedb' script from cron periodically (typically nightly). + +The `Makefile.am' files are used by an experimental program called +AutoMake that is under development. It's not ready for general use +yet, so don't worry about them. + +Mail suggestions and bug reports for these programs to +bug-gnu-utils@prep.ai.mit.edu. diff --git a/TODO b/TODO new file mode 100644 index 0000000..1dff586 --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +To do: + +Eliminate unnecessary strcpy calls in xargs. + +Use mode_t and AC_TYPE_MODE_T. diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..e9d4a96 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,36 @@ +/* Entries for config.h.in that aren't automatically generated. */ + +/* Define if you have the Andrew File System. */ +#undef AFS + +/* Define If you want find -nouser and -nogroup to make tables of + used UIDs and GIDs at startup instead of using getpwuid or + getgrgid when needed. Speeds up -nouser and -nogroup unless you + are running NIS or Hesiod, which make password and group calls + very expensive. */ +#undef CACHE_IDS + +/* Define to use SVR4 statvfs to get filesystem type. */ +#undef FSTYPE_STATVFS + +/* Define to use SVR3.2 statfs to get filesystem type. */ +#undef FSTYPE_USG_STATFS + +/* Define to use AIX3 statfs to get filesystem type. */ +#undef FSTYPE_AIX_STATFS + +/* Define to use 4.3BSD getmntent to get filesystem type. */ +#undef FSTYPE_MNTENT + +/* Define to use 4.4BSD and OSF1 statfs to get filesystem type. */ +#undef FSTYPE_STATFS + +/* Define to use Ultrix getmnt to get filesystem type. */ +#undef FSTYPE_GETMNT + +/* Define to `unsigned long' if doesn't define. */ +#undef dev_t + +/* Define to `unsigned long' if doesn't define. */ +#undef ino_t + diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..bf72e53 --- /dev/null +++ b/config.h.in @@ -0,0 +1,171 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +#undef _ALL_SOURCE +#endif + +/* Define if using alloca.c. */ +#undef C_ALLOCA + +/* Define if the closedir function returns void instead of int. */ +#undef CLOSEDIR_VOID + +/* 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 to `int' if doesn't define. */ +#undef gid_t + +/* Define if you have alloca, as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define if you have and it should be used (not on Ultrix). */ +#undef HAVE_ALLOCA_H + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the getmntent function. */ +#undef HAVE_GETMNTENT + +/* Define if your struct stat has st_blocks. */ +#undef HAVE_ST_BLOCKS + +/* Define if your struct stat has st_rdev. */ +#undef HAVE_ST_RDEV + +/* Define if you have the strftime function. */ +#undef HAVE_STRFTIME + +/* Define if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define if your struct tm has tm_zone. */ +#undef HAVE_TM_ZONE + +/* Define if you don't have tm_zone but do have the external array + tzname. */ +#undef HAVE_TZNAME + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define if major, minor, and makedev are declared in . */ +#undef MAJOR_IN_MKDEV + +/* Define if major, minor, and makedev are declared in . */ +#undef MAJOR_IN_SYSMACROS + +/* Define if on MINIX. */ +#undef _MINIX + +/* Define to `int' if doesn't define. */ +#undef pid_t + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +#undef _POSIX_1_SOURCE + +/* Define if you need to in order for stat and other things to work. */ +#undef _POSIX_SOURCE + +/* Define to `unsigned' if 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 the `S_IS*' macros in do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define if you have the Andrew File System. */ +#undef AFS + +/* Define If you want find -nouser and -nogroup to make tables of + used UIDs and GIDs at startup instead of using getpwuid or + getgrgid when needed. Speeds up -nouser and -nogroup unless you + are running NIS or Hesiod, which make password and group calls + very expensive. */ +#undef CACHE_IDS + +/* Define to use SVR4 statvfs to get filesystem type. */ +#undef FSTYPE_STATVFS + +/* Define to use SVR3.2 statfs to get filesystem type. */ +#undef FSTYPE_USG_STATFS + +/* Define to use AIX3 statfs to get filesystem type. */ +#undef FSTYPE_AIX_STATFS + +/* Define to use 4.3BSD getmntent to get filesystem type. */ +#undef FSTYPE_MNTENT + +/* Define to use 4.4BSD and OSF1 statfs to get filesystem type. */ +#undef FSTYPE_STATFS + +/* Define to use Ultrix getmnt to get filesystem type. */ +#undef FSTYPE_GETMNT + +/* Define to `unsigned long' if doesn't define. */ +#undef dev_t + +/* Define to `unsigned long' if doesn't define. */ +#undef ino_t + +/* Define if you have the fchdir function. */ +#undef HAVE_FCHDIR + +/* Define if you have the getcwd function. */ +#undef HAVE_GETCWD + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define if you have the header file. */ +#undef HAVE_NDIR_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_DIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_NDIR_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the sun library (-lsun). */ +#undef HAVE_LIBSUN diff --git a/configure b/configure new file mode 100755 index 0000000..7383893 --- /dev/null +++ b/configure @@ -0,0 +1,2774 @@ +#!/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: +ac_help="$ac_help + --enable-id-cache cache all UIDs & GIDs; avoid if using NIS or Hesiod" +ac_help="$ac_help + --with-afs support -fstype afs" + +# 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=find/pred.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 + + + + +# Check whether --enable-id-cache or --disable-id-cache was given. +enableval="$enable_id_cache" +if test -n "$enableval"; then + cat >> confdefs.h <<\EOF +#define CACHE_IDS 1 +EOF + +fi + +# Check whether --with-afs or --without-afs was given. +withval="$with_afs" +if test -n "$withval"; then + cat >> confdefs.h <<\EOF +#define AFS 1 +EOF + + CPPFLAGS="$CPPFLAGS -I/usr/afsws/include" + LIBS="$LIBS -L/usr/afsws/lib -L/usr/afsws/lib/afs -lsys -lrx -llwp" +fi + +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. + echo 's,\\,\\\\,g; s,\$,$$,g' > conftestsed + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + + +# 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 <&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 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 < +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 < +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 + +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' + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # 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_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&4 +else + echo "$ac_t""no" 1>&4 +fi + +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 for AIX""... $ac_c" 1>&4 +cat > conftest.$ac_ext <&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + echo "$ac_t""yes" 1>&4; cat >> confdefs.h <<\EOF +#define _ALL_SOURCE 1 +EOF + +else + rm -rf conftest* + echo "$ac_t""no" 1>&4 +fi +rm -f conftest* + + +ac_safe=`echo "minix/config.h" | tr './\055' '___'` +echo $ac_n "checking for minix/config.h""... $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 +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 + MINIX=yes +else + echo "$ac_t""no" 1>&4 +MINIX= +fi + +if test "$MINIX" = yes; then + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + cat >> confdefs.h <<\EOF +#define _POSIX_1_SOURCE 2 +EOF + + cat >> confdefs.h <<\EOF +#define _MINIX 1 +EOF + +fi + +echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&4 +if test -d /etc/conf/kconfig.d && + grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1 +then + echo "$ac_t""yes" 1>&4 + ISC=yes # If later tests want to check for ISC. + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + if test "$GCC" = yes; then + CC="$CC -posix" + else + CC="$CC -Xp" + fi +else + echo "$ac_t""no" 1>&4 + ISC= +fi + + +echo $ac_n "checking for -lsun""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_lib_sun'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + ac_save_LIBS="$LIBS" +LIBS="$LIBS -lsun " +cat > conftest.$ac_ext <&4 + ac_tr_lib=HAVE_LIB`echo sun | tr '[a-z]' '[A-Z]'` + cat >> confdefs.h <&4 +fi + + +for ac_hdr in fcntl.h string.h limits.h unistd.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 +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 <&4 +fi +done + +# 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 </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 < +#include +#include +#include +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 +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 +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 < +#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 whether sys/types.h defines makedev""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_header_sys_types_h_makedev'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +int main() { return 0; } +int t() { +return makedev(0, 0); +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + ac_cv_header_sys_types_h_makedev=yes +else + rm -rf conftest* + ac_cv_header_sys_types_h_makedev=no +fi +rm -f conftest* + + +fi +echo "$ac_t""$ac_cv_header_sys_types_h_makedev" 1>&4 + +if test $ac_cv_header_sys_types_h_makedev = no; then +ac_safe=`echo "sys/mkdev.h" | tr './\055' '___'` +echo $ac_n "checking for sys/mkdev.h""... $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 +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 + cat >> confdefs.h <<\EOF +#define MAJOR_IN_MKDEV 1 +EOF + +else + echo "$ac_t""no" 1>&4 +fi + + + if test $ac_cv_header_sys_mkdev_h = no; then +ac_safe=`echo "sys/sysmacros.h" | tr './\055' '___'` +echo $ac_n "checking for sys/sysmacros.h""... $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 +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 + cat >> confdefs.h <<\EOF +#define MAJOR_IN_SYSMACROS 1 +EOF + +else + echo "$ac_t""no" 1>&4 +fi + + fi +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | tr './\055' '___'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#include <$ac_hdr> +int main() { return 0; } +int t() { +DIR *dirp = 0; +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$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 <&4 +fi +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for -ldir""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_lib_dir'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + ac_save_LIBS="$LIBS" +LIBS="$LIBS -ldir " +cat > conftest.$ac_ext <&4 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&4 +fi + +else +echo $ac_n "checking for -lx""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_lib_x'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + ac_save_LIBS="$LIBS" +LIBS="$LIBS -lx " +cat > conftest.$ac_ext <&4 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&4 +fi + +fi + +echo $ac_n "checking whether stat file-mode macros are broken""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_header_stat_broken'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#include +#ifdef S_ISBLK +# if S_ISBLK (S_IFDIR) +You lose. +# endif +# ifdef S_IFCHR +# if S_ISBLK (S_IFCHR) +You lose. +# endif +# endif +#endif + +#ifdef S_ISLNK +# if S_ISLNK (S_IFREG) +You lose. +# endif +#endif + +#ifdef S_ISSOCK +# if S_ISSOCK (S_IFREG) +You lose. +# endif +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "You lose" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_header_stat_broken=yes +else + rm -rf conftest* + ac_cv_header_stat_broken=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_header_stat_broken" 1>&4 +if test $ac_cv_header_stat_broken = yes; then + cat >> confdefs.h <<\EOF +#define STAT_MACROS_BROKEN 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 < +#include +#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 + + +echo $ac_n "checking how to get filesystem type""... $ac_c" 1>&4 +fstype=no +# The order of these tests is important. +cat > conftest.$ac_ext < +#include +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* + cat >> confdefs.h <<\EOF +#define FSTYPE_STATVFS 1 +EOF + fstype=SVR4 +else + echo "$ac_err" >&5 +fi +rm -f conftest* +if test $fstype = no; then +cat > conftest.$ac_ext < +#include +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* + cat >> confdefs.h <<\EOF +#define FSTYPE_USG_STATFS 1 +EOF + fstype=SVR3 +else + echo "$ac_err" >&5 +fi +rm -f conftest* +fi +if test $fstype = no; then +cat > conftest.$ac_ext < +#include +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* + cat >> confdefs.h <<\EOF +#define FSTYPE_AIX_STATFS 1 +EOF + fstype=AIX +else + echo "$ac_err" >&5 +fi +rm -f conftest* +fi +if test $fstype = no; then +cat > conftest.$ac_ext < +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* + cat >> confdefs.h <<\EOF +#define FSTYPE_MNTENT 1 +EOF + fstype=4.3BSD +else + echo "$ac_err" >&5 +fi +rm -f conftest* +fi +if test $fstype = no; then +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "f_type;" >/dev/null 2>&1; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define FSTYPE_STATFS 1 +EOF + fstype=4.4BSD/OSF1 +fi +rm -f conftest* + +fi +if test $fstype = no; then +cat > conftest.$ac_ext < +#include +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* + cat >> confdefs.h <<\EOF +#define FSTYPE_GETMNT 1 +EOF + fstype=Ultrix +else + echo "$ac_err" >&5 +fi +rm -f conftest* +fi +echo "$ac_t""$fstype" 1>&4 + + +echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_type_uid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "uid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_uid_t=yes +else + rm -rf conftest* + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_uid_t" 1>&4 +if test $ac_cv_type_uid_t = no; then + cat >> confdefs.h <<\EOF +#define uid_t int +EOF + + cat >> confdefs.h <<\EOF +#define gid_t int +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 < +#if STDC_HEADERS +#include +#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 + +echo $ac_n "checking for pid_t""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&4 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking for ino_t""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_type_ino_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "ino_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_ino_t=yes +else + rm -rf conftest* + ac_cv_type_ino_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_ino_t" 1>&4 +if test $ac_cv_type_ino_t = no; then + cat >> confdefs.h <<\EOF +#define ino_t unsigned long +EOF + +fi + +echo $ac_n "checking for dev_t""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_type_dev_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "dev_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_dev_t=yes +else + rm -rf conftest* + ac_cv_type_dev_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_dev_t" 1>&4 +if test $ac_cv_type_dev_t = no; then + cat >> confdefs.h <<\EOF +#define dev_t unsigned long +EOF + +fi + +echo $ac_n "checking for st_blocks in struct stat""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_struct_st_blocks'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#include +int main() { return 0; } +int t() { +struct stat s; s.st_blocks; +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + ac_cv_struct_st_blocks=yes +else + rm -rf conftest* + ac_cv_struct_st_blocks=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_struct_st_blocks" 1>&4 +if test $ac_cv_struct_st_blocks = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_BLOCKS 1 +EOF + +else + LIBOBJS="$LIBOBJS fileblocks.o" +fi + +echo $ac_n "checking for st_rdev in struct stat""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_struct_st_rdev'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#include +int main() { return 0; } +int t() { +struct stat s; s.st_rdev; +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + ac_cv_struct_st_rdev=yes +else + rm -rf conftest* + ac_cv_struct_st_rdev=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_struct_st_rdev" 1>&4 +if test $ac_cv_struct_st_rdev = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_RDEV 1 +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#include +int main() { return 0; } +int t() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_struct_tm" 1>&4 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking for tm_zone in struct tm""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_struct_tm_zone'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#include <$ac_cv_struct_tm> +int main() { return 0; } +int t() { +struct tm tm; tm.tm_zone; +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + ac_cv_struct_tm_zone=yes +else + rm -rf conftest* + ac_cv_struct_tm_zone=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_struct_tm_zone" 1>&4 +if test "$ac_cv_struct_tm_zone" = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_TM_ZONE 1 +EOF + +else + echo $ac_n "checking for tzname""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_var_tzname'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < +#ifndef tzname /* For SGI. */ +extern char *tzname[]; /* RS6000 and others reject char **tzname. */ +#endif +int main() { return 0; } +int t() { +atoi(*tzname); +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + ac_cv_var_tzname=yes +else + rm -rf conftest* + ac_cv_var_tzname=no +fi +rm -f conftest* + +fi + echo "$ac_t""$ac_cv_var_tzname" 1>&4 + if test $ac_cv_var_tzname = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_TZNAME 1 +EOF + + fi +fi + +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 <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 + + + +for ac_func in memcmp memset mktime stpcpy strdup strftime strspn strstr strtol +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 < /* 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 + : +else + echo "$ac_t""no" 1>&4 +LIBOBJS="$LIBOBJS ${ac_func}.o" +fi + +done + +for ac_func in fchdir getcwd 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 < /* 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 <&4 +fi +done + +# strftime is in -lintl on SCO UNIX. +echo $ac_n "checking for -lintl""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_lib_intl'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + ac_save_LIBS="$LIBS" +LIBS="$LIBS -lintl " +cat > conftest.$ac_ext <&4 + LIBS="$LIBS -lintl" +else + echo "$ac_t""no" 1>&4 +fi + +echo $ac_n "checking for strftime""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_func_strftime'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < /* Arbitrary system header to define __stub macros. */ +/* Override any gcc2 internal prototype to avoid an error. */ +char strftime(); + +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_strftime) || defined (__stub___strftime) +choke me +#else +strftime(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func_strftime=yes" +else + rm -rf conftest* + eval "ac_cv_func_strftime=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'strftime`\" = yes"; then + echo "$ac_t""yes" 1>&4 + cat >> confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +else + echo "$ac_t""no" 1>&4 +fi + +echo $ac_n "checking for vprintf""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < /* Arbitrary system header to define __stub macros. */ +/* Override any gcc2 internal prototype to avoid an error. */ +char vprintf(); + +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_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&4 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&4 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < /* Arbitrary system header to define __stub macros. */ +/* Override any gcc2 internal prototype to avoid an error. */ +char _doprnt(); + +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__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&4 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&4 +fi + +fi + +# 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 < +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 < +# 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 <&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 < /* 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 < /* 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 < /* 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 < 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 <&4 +if eval "test \"`echo '${'ac_cv_lib_sun'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + ac_save_LIBS="$LIBS" +LIBS="$LIBS -lsun " +cat > conftest.$ac_ext <&4 + LIBS="$LIBS -lsun" +else + echo "$ac_t""no" 1>&4 +echo $ac_n "checking for -lseq""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_lib_seq'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + ac_save_LIBS="$LIBS" +LIBS="$LIBS -lseq " +cat > conftest.$ac_ext <&4 + LIBS="$LIBS -lseq" +else + echo "$ac_t""no" 1>&4 +fi + +fi + +echo $ac_n "checking for getmntent""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_func_getmntent'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + cat > conftest.$ac_ext < /* Arbitrary system header to define __stub macros. */ +/* Override any gcc2 internal prototype to avoid an error. */ +char getmntent(); + +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_getmntent) || defined (__stub___getmntent) +choke me +#else +getmntent(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func_getmntent=yes" +else + rm -rf conftest* + eval "ac_cv_func_getmntent=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'getmntent`\" = yes"; then + echo "$ac_t""yes" 1>&4 + cat >> confdefs.h <<\EOF +#define HAVE_GETMNTENT 1 +EOF + +else + echo "$ac_t""no" 1>&4 +fi + +echo $ac_n "checking whether closedir returns void""... $ac_c" 1>&4 +if eval "test \"`echo '${'ac_cv_func_closedir_void'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&4 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else +cat > conftest.$ac_ext < +#include <$ac_header_dirent> +int closedir(); main() { exit(closedir(opendir(".")) != 0); } +EOF +eval $ac_link +if test -s conftest && (./conftest; exit) 2>/dev/null; then + ac_cv_func_closedir_void=no +else + ac_cv_func_closedir_void=yes +fi +fi +rm -fr conftest* +fi +echo "$ac_t""$ac_cv_func_closedir_void" 1>&4 +if test $ac_cv_func_closedir_void = yes; then + cat >> confdefs.h <<\EOF +#define CLOSEDIR_VOID 1 +EOF + +fi + + +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 </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 lib/Makefile find/Makefile xargs/Makefile \ +locate/Makefile doc/Makefile testsuite/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%@INCLUDES@%$INCLUDES%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@RANLIB@%$RANLIB%g +s%@SET_MAKE@%$SET_MAKE%g +s%@LIBOBJS@%$LIBOBJS%g +s%@ALLOCA@%$ALLOCA%g + +CEOF +EOF +cat >> $CONFIG_STATUS <> $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 <> $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 + + +date > stamp-h +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..ae56fba --- /dev/null +++ b/configure.in @@ -0,0 +1,89 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(find/pred.c) +AC_CONFIG_HEADER(config.h) + +AC_SUBST(INCLUDES)dnl +AC_ARG_ENABLE(id-cache, +[ --enable-id-cache cache all UIDs & GIDs; avoid if using NIS or Hesiod], + AC_DEFINE(CACHE_IDS)) +AC_ARG_WITH(afs, +[ --with-afs support -fstype afs], +[ AC_DEFINE(AFS) + CPPFLAGS="$CPPFLAGS -I/usr/afsws/include" + LIBS="$LIBS -L/usr/afsws/lib -L/usr/afsws/lib/afs -lsys -lrx -llwp"]) +AC_ARG_PROGRAM + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_RANLIB +AC_PROG_MAKE_SET + +dnl Try to get a POSIX.1 environment. +AC_AIX +AC_MINIX +AC_ISC_POSIX + +dnl Checks for libraries. +AC_CHECK_LIB(sun, getpwnam) + +dnl Checks for header files. +AC_CHECK_HEADERS(fcntl.h string.h limits.h unistd.h) +AC_HEADER_STDC +AC_HEADER_MAJOR +AC_HEADER_DIRENT +AC_HEADER_STAT +AC_HEADER_SYS_WAIT + +AC_MSG_CHECKING(how to get filesystem type) +fstype=no +# The order of these tests is important. +AC_TRY_CPP([#include +#include ], AC_DEFINE(FSTYPE_STATVFS) fstype=SVR4) +if test $fstype = no; then +AC_TRY_CPP([#include +#include ], AC_DEFINE(FSTYPE_USG_STATFS) fstype=SVR3) +fi +if test $fstype = no; then +AC_TRY_CPP([#include +#include ], AC_DEFINE(FSTYPE_AIX_STATFS) fstype=AIX) +fi +if test $fstype = no; then +AC_TRY_CPP([#include ], AC_DEFINE(FSTYPE_MNTENT) fstype=4.3BSD) +fi +if test $fstype = no; then +AC_EGREP_HEADER(f_type;, sys/mount.h, AC_DEFINE(FSTYPE_STATFS) fstype=4.4BSD/OSF1) +fi +if test $fstype = no; then +AC_TRY_CPP([#include +#include ], AC_DEFINE(FSTYPE_GETMNT) fstype=Ultrix) +fi +AC_MSG_RESULT($fstype) + +dnl Checks for typedefs, structures, and compiler characteristics. + +AC_TYPE_UID_T +AC_TYPE_SIZE_T +AC_TYPE_PID_T +AC_CHECK_TYPE(ino_t, unsigned long) +AC_CHECK_TYPE(dev_t, unsigned long) +AC_STRUCT_ST_BLOCKS +AC_STRUCT_ST_RDEV +AC_STRUCT_TM +AC_STRUCT_TIMEZONE +AC_C_CONST + +dnl Checks for library functions. + +AC_REPLACE_FUNCS(memcmp memset mktime stpcpy strdup strftime strspn strstr strtol) +AC_CHECK_FUNCS(fchdir getcwd strerror) +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF +AC_FUNC_ALLOCA +AC_FUNC_GETMNTENT +AC_FUNC_CLOSEDIR_VOID + +AC_OUTPUT(Makefile lib/Makefile find/Makefile xargs/Makefile \ +locate/Makefile doc/Makefile testsuite/Makefile, + date > stamp-h) diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..8fb1297 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,4 @@ +TEXINFOS = find.texi +DIST_OTHER = perm.texi texinfo.tex + +find.info find.dvi: perm.texi diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..fcaa8f0 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,121 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +MAKEINFO = makeinfo +TEXI2DVI = texi2dvi + +TEXFILES = *.aux *.cp *.cps *.dvi *.fn *.fns *.ky *.log *.pg *.toc *.tp *.vr + +INFOS = find.info* +INFO_DEPS = find.info +DVIS = find.dvi +SOURCES = +DIST_CONF = Makefile.am Makefile.in +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +TEXINFOS = find.texi +DIST_OTHER = perm.texi texinfo.tex + +all:: ${ALL} + +.SUFFIXES: .texi .info .dvi + +.texi.info: + $(MAKEINFO) -I$(srcdir) $< + +.texi.dvi: + TEXINPUTS=$(srcdir):$$TEXINPUTS $(TEXI2DVI) $< + +info:: $(INFO_DEPS) + +dvi:: $(DVIS) + +install:: install-info + +install-info: $(INFO_DEPS) + $(top_srcdir)/mkinstalldirs $(infodir) + cd $(srcdir); for file in *.info*; do \ + $(INSTALL_DATA) $$file $(infodir)/$$file; \ + done + +uninstall:: uninstall-info + +uninstall-info: + cd $(srcdir); for file in *.info*; do \ + rm -f $(infodir)/$$file; \ + done + +mostlyclean: + rm -f *.o core + +clean: mostlyclean + rm -f $(PROGRAMS) $(LIBPROGRAMS) $(LIBFILES) $(TEXFILES) $(CLEANFILES) + +distclean: clean + rm -f Makefile *.tab.c $(DISTCLEANFILES) + rm -f config.cache config.log config.status ${CONFIG_HEADER} stamp-h + +realclean: distclean + rm -f TAGS $(INFOS) + +dist: $(DIST_FILES) $(DIST_DIRS) + -mkdir ../`cat ../distname`/$(subdir) + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file;}; \ + done + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +find.info find.dvi: perm.texi diff --git a/doc/find.info b/doc/find.info new file mode 100644 index 0000000..c8acfba --- /dev/null +++ b/doc/find.info @@ -0,0 +1,113 @@ +This is Info file find.info, produced by Makeinfo-1.55 from the input +file find.texi. + +START-INFO-DIR-ENTRY +* Finding Files: (find). Listing and operating on files + that match certain criteria. +END-INFO-DIR-ENTRY + + This file documents the GNU utilities for finding files that match +certain criteria and performing various operations on them. + + Copyright (C) 1994 Free Software Foundation, Inc. + + 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 approved by the Foundation. + + +Indirect: +find.info-1: 1097 +find.info-2: 50752 + +Tag Table: +(Indirect) +Node: Top1097 +Node: Introduction1909 +Node: Scope3080 +Node: Overview5000 +Node: find Expressions6758 +Node: Finding Files8379 +Node: Name8845 +Node: Base Name Patterns9469 +Node: Full Name Patterns10008 +Node: Fast Full Name Search10993 +Node: Shell Pattern Matching13148 +Node: Links14989 +Node: Symbolic Links15419 +Node: Hard Links16367 +Node: Time17403 +Node: Age Ranges17918 +Node: Comparing Timestamps18851 +Node: Size20130 +Node: Type20945 +Node: Owner21757 +Node: Permissions22570 +Node: Contents23255 +Node: Directories24423 +Node: Filesystems26897 +Node: Combining Primaries With Operators28169 +Node: Actions29470 +Node: Print File Name30017 +Node: Print File Information30621 +Node: Escapes32856 +Node: Format Directives33516 +Node: Name Directives34214 +Node: Ownership Directives34745 +Node: Size Directives35170 +Node: Location Directives35466 +Node: Time Directives36018 +Node: Time Formats36886 +Node: Time Components37313 +Node: Date Components37806 +Node: Combined Time Formats38616 +Node: Run Commands39100 +Node: Single File39453 +Node: Multiple Files40303 +Node: Unsafe File Name Handling42106 +Node: Safe File Name Handling43656 +Node: Limiting Command Size44342 +Node: Interspersing File Names46097 +Node: Querying47158 +Node: Adding Tests48074 +Node: Common Tasks50336 +Node: Viewing And Editing50752 +Node: Archiving51263 +Node: Cleaning Up52895 +Node: Strange File Names54456 +Node: Fixing Permissions56086 +Node: Classifying Files56645 +Node: Databases57363 +Node: Database Locations57977 +Node: Database Formats59501 +Node: New Database Format60152 +Node: Sample Database61714 +Node: Old Database Format62353 +Node: File Permissions64083 +Node: Mode Structure64651 +Node: Symbolic Modes66779 +Node: Setting Permissions67777 +Node: Copying Permissions70314 +Node: Changing Special Permissions71115 +Node: Conditional Executability72951 +Node: Multiple Changes73568 +Node: Umask and Protection75216 +Node: Numeric Modes76305 +Node: Reference78135 +Node: Invoking find78433 +Node: Invoking locate79480 +Node: Invoking updatedb80163 +Node: Invoking xargs80932 +Node: Primary Index84002 + +End Tag Table diff --git a/doc/find.info-1 b/doc/find.info-1 new file mode 100644 index 0000000..dc78df6 --- /dev/null +++ b/doc/find.info-1 @@ -0,0 +1,1581 @@ +This is Info file find.info, produced by Makeinfo-1.55 from the input +file find.texi. + +START-INFO-DIR-ENTRY +* Finding Files: (find). Listing and operating on files + that match certain criteria. +END-INFO-DIR-ENTRY + + This file documents the GNU utilities for finding files that match +certain criteria and performing various operations on them. + + Copyright (C) 1994 Free Software Foundation, Inc. + + 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 approved by the Foundation. + + +File: find.info, Node: Top, Next: Introduction, Up: (dir) + + This file documents the GNU utilities for finding files that match +certain criteria and performing various actions on them. This is +edition 1.1, for `find' version 4.1. + +* Menu: + +* Introduction:: Summary of the tasks this manual describes. +* Finding Files:: Finding files that match certain criteria. +* Actions:: Doing things to files you have found. +* Common Tasks:: Solutions to common real-world problems. +* Databases:: Maintaining file name databases. +* File Permissions:: How to control access to files. +* Reference:: Summary of how to invoke the programs. +* Primary Index:: The components of `find' expressions. + + +File: find.info, Node: Introduction, Next: Finding Files, Prev: Top, Up: Top + +Introduction +************ + + This manual shows how to find files that meet criteria you specify, +and how to perform various actions on the files that you find. The +principal programs that you use to perform these tasks are `find', +`locate', and `xargs'. Some of the examples in this manual use +capabilities specific to the GNU versions of those programs. + + GNU `find' was originally written by Eric Decker, with enhancements +by David MacKenzie, Jay Plett, and Tim Wood. GNU `xargs' was +originally written by Mike Rendell, with enhancements by David +MacKenzie. GNU `locate' and its associated utilities were originally +written by James Woods, with enhancements by David MacKenzie. The idea +for `find -print0' and `xargs -0' came from Dan Bernstein. Many other +people have contributed bug fixes, small improvements, and helpful +suggestions. Thanks! + + Mail suggestions and bug reports for these programs to +`bug-gnu-utils@prep.ai.mit.edu'. Please include the version number, +which you can get by running `find --version'. + +* Menu: + +* Scope:: +* Overview:: +* find Expressions:: + + +File: find.info, Node: Scope, Next: Overview, Up: Introduction + +Scope +===== + + For brevity, the word "file" in this manual means a regular file, a +directory, a symbolic link, or any other kind of node that has a +directory entry. A directory entry is also called a "file name". A +file name may contain some, all, or none of the directories in a path +that leads to the file. These are all examples of what this manual +calls "file names": + + parser.c + README + ./budget/may-94.sc + fred/.cshrc + /usr/local/include/termcap.h + + A "directory tree" is a directory and the files it contains, all of +its subdirectories and the files they contain, etc. It can also be a +single non-directory file. + + These programs enable you to find the files in one or more directory +trees that: + + * have names that contain certain text or match a certain pattern; + + * are links to certain files; + + * were last used during a certain period of time; + + * are within a certain size range; + + * are of a certain type (regular file, directory, symbolic link, + etc.); + + * are owned by a certain user or group; + + * have certain access permissions; + + * contain text that matches a certain pattern; + + * are within a certain depth in the directory tree; + + * or some combination of the above. + + Once you have found the files you're looking for (or files that are +potentially the ones you're looking for), you can do more to them than +simply list their names. You can get any combination of the files' +attributes, or process the files in many ways, either individually or in +groups of various sizes. Actions that you might want to perform on the +files you have found include, but are not limited to: + + * view or edit + + * store in an archive + + * remove or rename + + * change access permissions + + * classify into groups + + This manual describes how to perform each of those tasks, and more. + + +File: find.info, Node: Overview, Next: find Expressions, Prev: Scope, Up: Introduction + +Overview +======== + + The principal programs used for making lists of files that match +given criteria and running commands on them are `find', `locate', and +`xargs'. An additional command, `updatedb', is used by system +administrators to create databases for `locate' to use. + + `find' searches for files in a directory hierarchy and prints +information about the files it found. It is run like this: + + find [FILE...] [EXPRESSION] + +Here is a typical use of `find'. This example prints the names of all +files in the directory tree rooted in `/usr/src' whose name ends with +`.c' and that are larger than 100 Kilobytes. + find /usr/src -name '*.c' -size +100k -print + + `locate' searches special file name databases for file names that +match patterns. The system administrator runs the `updatedb' program +to create the databases. `locate' is run like this: + + locate [OPTION...] PATTERN... + +This example prints the names of all files in the default file name +database whose name ends with `Makefile' or `makefile'. Which file +names are stored in the database depends on how the system +administrator ran `updatedb'. + locate '*[Mm]akefile' + + The name `xargs', pronounced EX-args, means "combine arguments." +`xargs' builds and executes command lines by gathering together +arguments it reads on the standard input. Most often, these arguments +are lists of file names generated by `find'. `xargs' is run like this: + + xargs [OPTION...] [COMMAND [INITIAL-ARGUMENTS]] + +The following command searches the files listed in the file `file-list' +and prints all of the lines in them that contain the word `typedef'. + xargs grep typedef < file-list + + +File: find.info, Node: find Expressions, Prev: Overview, Up: Introduction + +`find' Expressions +================== + + The expression that `find' uses to select files consists of one or +more "primaries", each of which is a separate command line argument to +`find'. `find' evaluates the expression each time it processes a file. +An expression can contain any of the following types of primaries: + +"options" + affect overall operation rather than the processing of a specific + file; + +"tests" + return a true or false value, depending on the file's attributes; + +"actions" + have side effects and return a true or false value; and + +"operators" + connect the other arguments and affect when and whether they are + evaluated. + + You can omit the operator between two primaries; it defaults to +`-and'. *Note Combining Primaries With Operators::, for ways to +connect primaries into more complex expressions. If the expression +contains no actions other than `-prune', `-print' is performed on all +files for which the entire expression is true (*note Print File +Name::.). + + Options take effect immediately, rather than being evaluated for each +file when their place in the expression is reached. Therefore, for +clarity, it is best to place them at the beginning of the expression. + + Many of the primaries take arguments, which immediately follow them +in the next command line argument to `find'. Some arguments are file +names, patterns, or other strings; others are numbers. Numeric +arguments can be specified as + +`+N' + for greater than N, + +`-N' + for less than N, + +`N' + for exactly N. + + +File: find.info, Node: Finding Files, Next: Actions, Prev: Introduction, Up: Top + +Finding Files +************* + + By default, `find' prints to the standard output the names of the +files that match the given criteria. *Note Actions::, for how to get +more information about the matching files. + +* Menu: + +* Name:: +* Links:: +* Time:: +* Size:: +* Type:: +* Owner:: +* Permissions:: +* Contents:: +* Directories:: +* Filesystems:: +* Combining Primaries With Operators:: + + +File: find.info, Node: Name, Next: Links, Up: Finding Files + +Name +==== + + Here are ways to search for files whose name matches a certain +pattern. *Note Shell Pattern Matching::, for a description of the +PATTERN arguments to these tests. + + Each of these tests has a case-sensitive version and a +case-insensitive version, whose name begins with `i'. In a +case-insensitive comparison, the patterns `fo*' and `F??' match the +file names `Foo', `FOO', `foo', `fOo', etc. + +* Menu: + +* Base Name Patterns:: +* Full Name Patterns:: +* Fast Full Name Search:: +* Shell Pattern Matching:: Wildcards used by these programs. + + +File: find.info, Node: Base Name Patterns, Next: Full Name Patterns, Up: Name + +Base Name Patterns +------------------ + + - Test: -name PATTERN + - Test: -iname PATTERN + True if the base of the file name (the path with the leading + directories removed) matches shell pattern PATTERN. For `-iname', + the match is case-insensitive. To ignore a whole directory tree, + use `-prune' (*note Directories::.). As an example, to find + Texinfo source files in `/usr/local/doc': + + find /usr/local/doc -name '*.texi' + + +File: find.info, Node: Full Name Patterns, Next: Fast Full Name Search, Prev: Base Name Patterns, Up: Name + +Full Name Patterns +------------------ + + - Test: -path PATTERN + - Test: -ipath PATTERN + True if the entire file name, starting with the command line + argument under which the file was found, matches shell pattern + PATTERN. For `-ipath', the match is case-insensitive. To ignore + a whole directory tree, use `-prune' rather than checking every + file in the tree (*note Directories::.). + + - Test: -regex EXPR + - Test: -iregex EXPR + True if the entire file name matches regular expression EXPR. + This is a match on the whole path, not a search. For example, to + match a file named `./fubar3', you can use the regular expression + `.*bar.' or `.*b.*3', but not `b.*r3'. *Note Syntax of Regular + Expressions: (emacs)Regexps, for a description of the syntax of + regular expressions. For `-iregex', the match is case-insensitive. + + +File: find.info, Node: Fast Full Name Search, Next: Shell Pattern Matching, Prev: Full Name Patterns, Up: Name + +Fast Full Name Search +--------------------- + + To search for files by name without having to actually scan the +directories on the disk (which can be slow), you can use the `locate' +program. For each shell pattern you give it, `locate' searches one or +more databases of file names and displays the file names that contain +the pattern. *Note Shell Pattern Matching::, for details about shell +patterns. + + If a pattern is a plain string--it contains no +metacharacters--`locate' displays all file names in the database that +contain that string. If a pattern contains metacharacters, `locate' +only displays file names that match the pattern exactly. As a result, +patterns that contain metacharacters should usually begin with a `*', +and will most often end with one as well. The exceptions are patterns +that are intended to explicitly match the beginning or end of a file +name. + + The command + locate PATTERN + + is almost equivalent to + find DIRECTORIES -name PATTERN + + where DIRECTORIES are the directories for which the file name +databases contain information. The differences are that the `locate' +information might be out of date, and that `locate' handles wildcards +in the pattern slightly differently than `find' (*note Shell Pattern +Matching::.). + + The file name databases contain lists of files that were on the +system when the databases were last updated. The system administrator +can choose the file name of the default database, the frequency with +which the databases are updated, and the directories for which they +contain entries. + + Here is how to select which file name databases `locate' searches. +The default is system-dependent. + +`--database=PATH' +`-d PATH' + Instead of searching the default file name database, search the + file name databases in PATH, which is a colon-separated list of + database file names. You can also use the environment variable + `LOCATE_PATH' to set the list of database files to search. The + option overrides the environment variable if both are used. + + +File: find.info, Node: Shell Pattern Matching, Prev: Fast Full Name Search, Up: Name + +Shell Pattern Matching +---------------------- + + `find' and `locate' can compare file names, or parts of file names, +to shell patterns. A "shell pattern" is a string that may contain the +following special characters, which are known as "wildcards" or +"metacharacters". + + You must quote patterns that contain metacharacters to prevent the +shell from expanding them itself. Double and single quotes both work; +so does escaping with a backslash. + +`*' + Matches any zero or more characters. + +`?' + Matches any one character. + +`[STRING]' + Matches exactly one character that is a member of the string + STRING. This is called a "character class". As a shorthand, + STRING may contain ranges, which consist of two characters with a + dash between them. For example, the class `[a-z0-9_]' matches a + lowercase letter, a number, or an underscore. You can negate a + class by placing a `!' or `^' immediately after the opening + bracket. Thus, `[^A-Z@]' matches any character except an + uppercase letter or an at sign. + +`\' + Removes the special meaning of the character that follows it. This + works even in character classes. + + In the `find' tests that do shell pattern matching (`-name', +`-path', etc.), wildcards in the pattern do not match a `.' at the +beginning of a file name. This is not the case for `locate'. Thus, +`find -name '*macs'' does not match a file named `.emacs', but `locate +'*macs'' does. + + Slash characters have no special significance in the shell pattern +matching that `find' and `locate' do, unlike in the shell, in which +wildcards do not match them. Therefore, a pattern `foo*bar' can match +a file name `foo3/bar', and a pattern `./sr*sc' can match a file name +`./src/misc'. + + +File: find.info, Node: Links, Next: Time, Prev: Name, Up: Finding Files + +Links +===== + + There are two ways that files can be linked together. "Symbolic +links" are a special type of file whose contents are a portion of the +name of another file. "Hard links" are multiple directory entries for +one file; the file names all have the same index node ("inode") number +on the disk. + +* Menu: + +* Symbolic Links:: +* Hard Links:: + + +File: find.info, Node: Symbolic Links, Next: Hard Links, Up: Links + +Symbolic Links +-------------- + + - Test: -lname PATTERN + - Test: -ilname PATTERN + True if the file is a symbolic link whose contents match shell + pattern PATTERN. For `-ilname', the match is case-insensitive. + *Note Shell Pattern Matching::, for details about the PATTERN + argument. So, to list any symbolic links to `sysdep.c' in the + current directory and its subdirectories, you can do: + + find . -lname '*sysdep.c' + + - Option: -follow + Dereference symbolic links. The following differences in behavior + occur when this option is given: + + * `find' follows symbolic links to directories when searching + directory trees. + + * `-lname' and `-ilname' always return false. + + * `-type' reports the types of the files that symbolic links + point to. + + * Implies `-noleaf' (*note Directories::.). + + +File: find.info, Node: Hard Links, Prev: Symbolic Links, Up: Links + +Hard Links +---------- + + To find hard links, first get the inode number of the file whose +links you want to find. You can learn a file's inode number and the +number of links to it by running `ls -i' or `find -ls'. If the file has +more than one link, you can search for the other links by passing that +inode number to `-inum'. Add the `-xdev' option if you are starting +the search at a directory that has other filesystems mounted on it, +such as `/usr' on many systems. Doing this saves needless searching, +since hard links to a file must be on the same filesystem. *Note +Filesystems::. + + - Test: -inum N + File has inode number N. + + You can also search for files that have a certain number of links, +with `-links'. Directories normally have at least two hard links; their +`.' entry is the second one. If they have subdirectories, each of +those also has a hard link called `..' to its parent directory. + + - Test: -links N + File has N hard links. + + +File: find.info, Node: Time, Next: Size, Prev: Links, Up: Finding Files + +Time +==== + + Each file has three time stamps, which record the last time that +certain operations were performed on the file: + + 1. access (read the file's contents) + + 2. change the status (modify the file or its attributes) + + 3. modify (change the file's contents) + + You can search for files whose time stamps are within a certain age +range, or compare them to other time stamps. + +* Menu: + +* Age Ranges:: +* Comparing Timestamps:: + + +File: find.info, Node: Age Ranges, Next: Comparing Timestamps, Up: Time + +Age Ranges +---------- + + These tests are mainly useful with ranges (`+N' and `-N'). + + - Test: -atime N + - Test: -ctime N + - Test: -mtime N + True if the file was last accessed (or its status changed, or it + was modified) N*24 hours ago. + + - Test: -amin N + - Test: -cmin N + - Test: -mmin N + True if the file was last accessed (or its status changed, or it + was modified) N minutes ago. These tests provide finer + granularity of measurement than `-atime' et al. For example, to + list files in `/u/bill' that were last read from 2 to 6 hours ago: + + find /u/bill -amin +2 -amin -6 + + - Option: -daystart + Measure times from the beginning of today rather than from 24 + hours ago. So, to list the regular files in your home directory + that were modified yesterday, do + + find ~ -daystart -type f -mtime 1 + + +File: find.info, Node: Comparing Timestamps, Prev: Age Ranges, Up: Time + +Comparing Timestamps +-------------------- + + As an alternative to comparing timestamps to the current time, you +can compare them to another file's timestamp. That file's timestamp +could be updated by another program when some event occurs. Or you +could set it to a particular fixed date using the `touch' command. For +example, to list files in `/usr' modified after February 1 of the +current year: + + touch -t 02010000 /tmp/stamp$$ + find /usr -newer /tmp/stamp$$ + rm -f /tmp/stamp$$ + + - Test: -anewer FILE + - Test: -cnewer FILE + - Test: -newer FILE + True if the file was last accessed (or its status changed, or it + was modified) more recently than FILE was modified. These tests + are affected by `-follow' only if `-follow' comes before them on + the command line. *Note Symbolic Links::, for more information on + `-follow'. As an example, to list any files modified since + `/bin/sh' was last modified: + + find . -newer /bin/sh + + - Test: -used N + True if the file was last accessed N days after its status was + last changed. Useful for finding files that are not being used, + and could perhaps be archived or removed to save disk space. + + +File: find.info, Node: Size, Next: Type, Prev: Time, Up: Finding Files + +Size +==== + + - Test: -size N[BCKW] + True if the file uses N units of space, rounding up. The units + are 512-byte blocks by default, but they can be changed by adding a + one-character suffix to N: + + `b' + 512-byte blocks + + `c' + bytes + + `k' + kilobytes (1024 bytes) + + `w' + 2-byte words + + The size does not count indirect blocks, but it does count blocks + in sparse files that are not actually allocated. + + - Test: -empty + True if the file is empty and is either a regular file or a + directory. This might make it a good candidate for deletion. + This test is useful with `-depth' (*note Directories::.) and + `-exec rm -rf '{}' ';'' (*note Single File::.). + + +File: find.info, Node: Type, Next: Owner, Prev: Size, Up: Finding Files + +Type +==== + + - Test: -type C + True if the file is of type C: + + `b' + block (buffered) special + + `c' + character (unbuffered) special + + `d' + directory + + `p' + named pipe (FIFO) + + `f' + regular file + + `l' + symbolic link + + `s' + socket + + - Test: -xtype C + The same as `-type' unless the file is a symbolic link. For + symbolic links: if `-follow' has not been given, true if the file + is a link to a file of type C; if `-follow' has been given, true + if C is `l'. In other words, for symbolic links, `-xtype' checks + the type of the file that `-type' does not check. *Note Symbolic + Links::, for more information on `-follow'. + + +File: find.info, Node: Owner, Next: Permissions, Prev: Type, Up: Finding Files + +Owner +===== + + - Test: -user UNAME + - Test: -group GNAME + True if the file is owned by user UNAME (belongs to group GNAME). + A numeric ID is allowed. + + - Test: -uid N + - Test: -gid N + True if the file's numeric user ID (group ID) is N. These tests + support ranges (`+N' and `-N'), unlike `-user' and `-group'. + + - Test: -nouser + - Test: -nogroup + True if no user corresponds to the file's numeric user ID (no group + corresponds to the numeric group ID). These cases usually mean + that the files belonged to users who have since been removed from + the system. You probably should change the ownership of such + files to an existing user or group, using the `chown' or `chgrp' + program. + + +File: find.info, Node: Permissions, Next: Contents, Prev: Owner, Up: Finding Files + +Permissions +=========== + + *Note File Permissions::, for information on how file permissions are +structured and how to specify them. + + - Test: -perm MODE + True if the file's permissions are exactly MODE (which can be + numeric or symbolic). Symbolic modes use mode 0 as a point of + departure. If MODE starts with `-', true if *all* of the + permissions set in MODE are set for the file; permissions not set + in MODE are ignored. If MODE starts with `+', true if *any* of + the permissions set in MODE are set for the file; permissions not + set in MODE are ignored. + + +File: find.info, Node: Contents, Next: Directories, Prev: Permissions, Up: Finding Files + +Contents +======== + + To search for files based on their contents, you can use the `grep' +program. For example, to find out which C source files in the current +directory contain the string `thing', you can do: + + grep -l thing *.[ch] + + If you also want to search for the string in files in subdirectories, +you can combine `grep' with `find' and `xargs', like this: + + find . -name '*.[ch]' | xargs grep -l thing + + The `-l' option causes `grep' to print only the names of files that +contain the string, rather than the lines that contain it. The string +argument (`thing') is actually a regular expression, so it can contain +metacharacters. This method can be refined a little by using the `-r' +option to make `xargs' not run `grep' if `find' produces no output, and +using the `find' action `-print0' and the `xargs' option `-0' to avoid +misinterpreting files whose names contain spaces: + + find . -name '*.[ch]' -print0 | xargs -r -0 grep -l thing + + For a fuller treatment of finding files whose contents match a +pattern, see the manual page for `grep'. + + +File: find.info, Node: Directories, Next: Filesystems, Prev: Contents, Up: Finding Files + +Directories +=========== + + Here is how to control which directories `find' searches, and how it +searches them. These two options allow you to process a horizontal +slice of a directory tree. + + - Option: -maxdepth LEVELS + Descend at most LEVELS (a non-negative integer) levels of + directories below the command line arguments. `-maxdepth 0' means + only apply the tests and actions to the command line arguments. + + - Option: -mindepth LEVELS + Do not apply any tests or actions at levels less than LEVELS (a + non-negative integer). `-mindepth 1' means process all files + except the command line arguments. + + - Option: -depth + Process each directory's contents before the directory itself. + Doing this is a good idea when producing lists of files to archive + with `cpio' or `tar'. If a directory does not have write + permission for its owner, its contents can still be restored from + the archive since the directory's permissions are restored after + its contents. + + - Action: -prune + If `-depth' is not given, true; do not descend the current + directory. If `-depth' is given, false; no effect. `-prune' only + affects tests and actions that come after it in the expression, not + those that come before. + + For example, to skip the directory `src/emacs' and all files and + directories under it, and print the names of the other files found: + + find . -path './src/emacs' -prune -o -print + + - Option: -noleaf + Do not optimize by assuming that directories contain 2 fewer + subdirectories than their hard link count. This option is needed + when searching filesystems that do not follow the Unix + directory-link convention, such as CD-ROM or MS-DOS filesystems or + AFS volume mount points. Each directory on a normal Unix + filesystem has at least 2 hard links: its name and its `.' entry. + Additionally, its subdirectories (if any) each have a `..' entry + linked to that directory. When `find' is examining a directory, + after it has statted 2 fewer subdirectories than the directory's + link count, it knows that the rest of the entries in the directory + are non-directories ("leaf" files in the directory tree). If only + the files' names need to be examined, there is no need to stat + them; this gives a significant increase in search speed. + + +File: find.info, Node: Filesystems, Next: Combining Primaries With Operators, Prev: Directories, Up: Finding Files + +Filesystems +=========== + + A "filesystem" is a section of a disk, either on the local host or +mounted from a remote host over a network. Searching network +filesystems can be slow, so it is common to make `find' avoid them. + + There are two ways to avoid searching certain filesystems. One way +is to tell `find' to only search one filesystem: + + - Option: -xdev + - Option: -mount + Don't descend directories on other filesystems. These options are + synonyms. + + The other way is to check the type of filesystem each file is on, and +not descend directories that are on undesirable filesystem types: + + - Test: -fstype TYPE + True if the file is on a filesystem of type TYPE. The valid + filesystem types vary among different versions of Unix; an + incomplete list of filesystem types that are accepted on some + version of Unix or another is: + ufs 4.2 4.3 nfs tmp mfs S51K S52K + You can use `-printf' with the `%F' directive to see the types of + your filesystems. *Note Print File Information::. `-fstype' is + usually used with `-prune' to avoid searching remote filesystems + (*note Directories::.). + + +File: find.info, Node: Combining Primaries With Operators, Prev: Filesystems, Up: Finding Files + +Combining Primaries With Operators +================================== + + Operators build a complex expression from tests and actions. The +operators are, in order of decreasing precedence: + +`( EXPR )' + Force precedence. True if EXPR is true. + +`! EXPR' +`-not EXPR' + True if EXPR is false. + +`EXPR1 EXPR2' +`EXPR1 -a EXPR2' +`EXPR1 -and EXPR2' + And; EXPR2 is not evaluated if EXPR1 is false. + +`EXPR1 -o EXPR2' +`EXPR1 -or EXPR2' + Or; EXPR2 is not evaluated if EXPR1 is true. + +`EXPR1 , EXPR2' + List; both EXPR1 and EXPR2 are always evaluated. True if EXPR2 is + true. The value of EXPR1 is discarded. This operator lets you do + multiple independent operations on one traversal, without + depending on whether other operations succeeded. + + `find' searches the directory tree rooted at each file name by +evaluating the expression from left to right, according to the rules of +precedence, until the outcome is known (the left hand side is false for +`-and', true for `-or'), at which point `find' moves on to the next +file name. + + There are two other tests that can be useful in complex expressions: + + - Test: -true + Always true. + + - Test: -false + Always false. + + +File: find.info, Node: Actions, Next: Common Tasks, Prev: Finding Files, Up: Top + +Actions +******* + + There are several ways you can print information about the files that +match the criteria you gave in the `find' expression. You can print +the information either to the standard output or to a file that you +name. You can also execute commands that have the file names as +arguments. You can use those commands as further filters to select +files. + +* Menu: + +* Print File Name:: +* Print File Information:: +* Run Commands:: +* Adding Tests:: + + +File: find.info, Node: Print File Name, Next: Print File Information, Up: Actions + +Print File Name +=============== + + - Action: -print + True; print the full file name on the standard output, followed by + a newline. + + - Action: -fprint FILE + True; print the full file name into file FILE, followed by a + newline. If FILE does not exist when `find' is run, it is + created; if it does exist, it is truncated to 0 bytes. The file + names `/dev/stdout' and `/dev/stderr' are handled specially; they + refer to the standard output and standard error output, + respectively. + + +File: find.info, Node: Print File Information, Next: Run Commands, Prev: Print File Name, Up: Actions + +Print File Information +====================== + + - Action: -ls + True; list the current file in `ls -dils' format on the standard + output. The output looks like this: + + 204744 17 -rw-r--r-- 1 djm staff 17337 Nov 2 1992 ./lwall-quotes + + The fields are: + + 1. The inode number of the file. *Note Hard Links::, for how to + find files based on their inode number. + + 2. the number of blocks in the file. The block counts are of 1K + blocks, unless the environment variable `POSIXLY_CORRECT' is + set, in which case 512-byte blocks are used. *Note Size::, + for how to find files based on their size. + + 3. The file's type and permissions. The type is shown as a dash + for a regular file; for other file types, a letter like for + `-type' is used (*note Type::.). The permissions are read, + write, and execute for the file's owner, its group, and other + users, respectively; a dash means the permission is not + granted. *Note File Permissions::, for more details about + file permissions. *Note Permissions::, for how to find files + based on their permissions. + + 4. The number of hard links to the file. + + 5. The user who owns the file. + + 6. The file's group. + + 7. The file's size in bytes. + + 8. The date the file was last modified. + + 9. The file's name. `-ls' quotes non-printable characters in + the file names using C-like backslash escapes. + + - Action: -fls FILE + True; like `-ls' but write to FILE like `-fprint' (*note Print + File Name::.). + + - Action: -printf FORMAT + True; print FORMAT on the standard output, interpreting `\' + escapes and `%' directives. Field widths and precisions can be + specified as with the `printf' C function. Unlike `-print', + `-printf' does not add a newline at the end of the string. + + - Action: -fprintf FILE FORMAT + True; like `-printf' but write to FILE like `-fprint' (*note Print + File Name::.). + +* Menu: + +* Escapes:: +* Format Directives:: +* Time Formats:: + + +File: find.info, Node: Escapes, Next: Format Directives, Up: Print File Information + +Escapes +------- + + The escapes that `-printf' and `-fprintf' recognize are: + +`\a' + Alarm bell. + +`\b' + Backspace. + +`\c' + Stop printing from this format immediately and flush the output. + +`\f' + Form feed. + +`\n' + Newline. + +`\r' + Carriage return. + +`\t' + Horizontal tab. + +`\v' + Vertical tab. + +`\\' + A literal backslash (`\'). + + A `\' character followed by any other character is treated as an +ordinary character, so they both are printed, and a warning message is +printed to the standard error output (because it was probably a typo). + + +File: find.info, Node: Format Directives, Next: Time Formats, Prev: Escapes, Up: Print File Information + +Format Directives +----------------- + + `-printf' and `-fprintf' support the following format directives to +print information about the file being processed. Unlike the C +`printf' function, they do not support field width specifiers. + + `%%' is a literal percent sign. A `%' character followed by any +other character is discarded (but the other character is printed), and +a warning message is printed to the standard error output (because it +was probably a typo). + +* Menu: + +* Name Directives:: +* Ownership Directives:: +* Size Directives:: +* Location Directives:: +* Time Directives:: + + +File: find.info, Node: Name Directives, Next: Ownership Directives, Up: Format Directives + +Name Directives +............... + +`%p' + File's name. + +`%f' + File's name with any leading directories removed (only the last + element). + +`%h' + Leading directories of file's name (all but the last element and + the slash before it). + +`%P' + File's name with the name of the command line argument under which + it was found removed from the beginning. + +`%H' + Command line argument under which file was found. + + +File: find.info, Node: Ownership Directives, Next: Size Directives, Prev: Name Directives, Up: Format Directives + +Ownership Directives +.................... + +`%g' + File's group name, or numeric group ID if the group has no name. + +`%G' + File's numeric group ID. + +`%u' + File's user name, or numeric user ID if the user has no name. + +`%U' + File's numeric user ID. + +`%m' + File's permissions (in octal). + + +File: find.info, Node: Size Directives, Next: Location Directives, Prev: Ownership Directives, Up: Format Directives + +Size Directives +............... + +`%k' + File's size in 1K blocks (rounded up). + +`%b' + File's size in 512-byte blocks (rounded up). + +`%s' + File's size in bytes. + + +File: find.info, Node: Location Directives, Next: Time Directives, Prev: Size Directives, Up: Format Directives + +Location Directives +................... + +`%d' + File's depth in the directory tree; files named on the command line + have a depth of 0. + +`%F' + Type of the filesystem the file is on; this value can be used for + `-fstype' (*note Directories::.). + +`%l' + Object of symbolic link (empty string if file is not a symbolic + link). + +`%i' + File's inode number (in decimal). + +`%n' + Number of hard links to file. + + +File: find.info, Node: Time Directives, Prev: Location Directives, Up: Format Directives + +Time Directives +............... + + Some of these directives use the C `ctime' function. Its output +depends on the current locale, but it typically looks like + + Wed Nov 2 00:42:36 1994 + +`%a' + File's last access time in the format returned by the C `ctime' + function. + +`%AK' + File's last access time in the format specified by K (*note Time + Formats::.). + +`%c' + File's last status change time in the format returned by the C + `ctime' function. + +`%CK' + File's last status change time in the format specified by K (*note + Time Formats::.). + +`%t' + File's last modification time in the format returned by the C + `ctime' function. + +`%TK' + File's last modification time in the format specified by K (*note + Time Formats::.). + + +File: find.info, Node: Time Formats, Prev: Format Directives, Up: Print File Information + +Time Formats +------------ + + Below are the formats for the directives `%A', `%C', and `%T', which +print the file's timestamps. Some of these formats might not be +available on all systems, due to differences in the C `strftime' +function between systems. + +* Menu: + +* Time Components:: +* Date Components:: +* Combined Time Formats:: + + +File: find.info, Node: Time Components, Next: Date Components, Up: Time Formats + +Time Components +............... + + The following format directives print single components of the time. + +`H' + hour (00..23) + +`I' + hour (01..12) + +`k' + hour ( 0..23) + +`l' + hour ( 1..12) + +`p' + locale's AM or PM + +`Z' + time zone (e.g., EDT), or nothing if no time zone is determinable + +`M' + minute (00..59) + +`S' + second (00..61) + +`@' + seconds since Jan. 1, 1970, 00:00 GMT. + + +File: find.info, Node: Date Components, Next: Combined Time Formats, Prev: Time Components, Up: Time Formats + +Date Components +............... + + The following format directives print single components of the date. + +`a' + locale's abbreviated weekday name (Sun..Sat) + +`A' + locale's full weekday name, variable length (Sunday..Saturday) + +`b' +`h' + locale's abbreviated month name (Jan..Dec) + +`B' + locale's full month name, variable length (January..December) + +`m' + month (01..12) + +`d' + day of month (01..31) + +`w' + day of week (0..6) + +`j' + day of year (001..366) + +`U' + week number of year with Sunday as first day of week (00..53) + +`W' + week number of year with Monday as first day of week (00..53) + +`Y' + year (1970...) + +`y' + last two digits of year (00..99) + + +File: find.info, Node: Combined Time Formats, Prev: Date Components, Up: Time Formats + +Combined Time Formats +..................... + + The following format directives print combinations of time and date +components. + +`r' + time, 12-hour (hh:mm:ss [AP]M) + +`T' + time, 24-hour (hh:mm:ss) + +`X' + locale's time representation (H:M:S) + +`c' + locale's date and time (Sat Nov 04 12:02:33 EST 1989) + +`D' + date (mm/dd/yy) + +`x' + locale's date representation (mm/dd/yy) + + +File: find.info, Node: Run Commands, Next: Adding Tests, Prev: Print File Information, Up: Actions + +Run Commands +============ + + You can use the list of file names created by `find' or `locate' as +arguments to other commands. In this way you can perform arbitrary +actions on the files. + +* Menu: + +* Single File:: +* Multiple Files:: +* Querying:: + + +File: find.info, Node: Single File, Next: Multiple Files, Up: Run Commands + +Single File +----------- + + Here is how to run a command on one file at a time. + + - Action: -exec COMMAND ; + Execute COMMAND; true if 0 status is returned. `find' takes all + arguments after `-exec' to be part of the command until an + argument consisting of `;' is reached. It replaces the string + `{}' by the current file name being processed everywhere it occurs + in the command. Both of these constructions need to be escaped + (with a `\') or quoted to protect them from expansion by the shell. + The command is executed in the directory in which `find' was run. + + For example, to compare each C header file in the current + directory with the file `/tmp/master': + + find . -name '*.h' -exec diff -u '{}' /tmp/master ';' + + +File: find.info, Node: Multiple Files, Next: Querying, Prev: Single File, Up: Run Commands + +Multiple Files +-------------- + + Sometimes you need to process files alone. But when you don't, it +is faster to run a command on as many files as possible at a time, +rather than once per file. Doing this saves on the time it takes to +start up the command each time. + + To run a command on more than one file at once, use the `xargs' +command, which is invoked like this: + + xargs [OPTION...] [COMMAND [INITIAL-ARGUMENTS]] + + `xargs' reads arguments from the standard input, delimited by blanks +(which can be protected with double or single quotes or a backslash) or +newlines. It executes the COMMAND (default is `/bin/echo') one or more +times with any INITIAL-ARGUMENTS followed by arguments read from +standard input. Blank lines on the standard input are ignored. + + Instead of blank-delimited names, it is safer to use `find -print0' +or `find -fprint0' and process the output by giving the `-0' or +`--null' option to GNU `xargs', GNU `tar', GNU `cpio', or `perl'. + + You can use shell command substitution (backquotes) to process a +list of arguments, like this: + + grep -l sprintf `find $HOME -name '*.c' -print` + + However, that method produces an error if the length of the `.c' +file names exceeds the operating system's command-line length limit. +`xargs' avoids that problem by running the command as many times as +necessary without exceeding the limit: + + find $HOME -name '*.c' -print | grep -l sprintf + + However, if the command needs to have its standard input be a +terminal (`less', for example), you have to use the shell command +substitution method. + +* Menu: + +* Unsafe File Name Handling:: +* Safe File Name Handling:: +* Limiting Command Size:: +* Interspersing File Names:: + + +File: find.info, Node: Unsafe File Name Handling, Next: Safe File Name Handling, Up: Multiple Files + +Unsafe File Name Handling +......................... + + Because file names can contain quotes, backslashes, blank characters, +and even newlines, it is not safe to process them using `xargs' in its +default mode of operation. But since most files' names do not contain +blanks, this problem occurs only infrequently. If you are only +searching through files that you know have safe names, then you need not +be concerned about it. + + In many applications, if `xargs' botches processing a file because +its name contains special characters, some data might be lost. The +importance of this problem depends on the importance of the data and +whether anyone notices the loss soon enough to correct it. However, +here is an extreme example of the problems that using blank-delimited +names can cause. If the following command is run daily from `cron', +then any user can remove any file on the system: + + find / -name '#*' -atime +7 -print | xargs rm + + For example, you could do something like this: + + eg$ echo > '# + vmunix' + +and then `cron' would delete `/vmunix', if it ran `xargs' with `/' as +its current directory. + + To delete other files, for example `/u/joeuser/.plan', you could do +this: + + eg$ mkdir '# + ' + eg$ cd '# + ' + eg$ mkdir u u/joeuser u/joeuser/.plan' + ' + eg$ echo > u/joeuser/.plan' + /#foo' + eg$ cd .. + eg$ find . -name '#*' -print | xargs echo + ./# ./# /u/joeuser/.plan /#foo + + +File: find.info, Node: Safe File Name Handling, Next: Limiting Command Size, Prev: Unsafe File Name Handling, Up: Multiple Files + +Safe File Name Handling +....................... + + Here is how to make `find' output file names so that they can be +used by other programs without being mangled or misinterpreted. You can +process file names generated this way by giving the `-0' or `--null' +option to GNU `xargs', GNU `tar', GNU `cpio', or `perl'. + + - Action: -print0 + True; print the full file name on the standard output, followed by + a null character. + + - Action: -fprint0 FILE + True; like `-print0' but write to FILE like `-fprint' (*note Print + File Name::.). + + +File: find.info, Node: Limiting Command Size, Next: Interspersing File Names, Prev: Safe File Name Handling, Up: Multiple Files + +Limiting Command Size +..................... + + `xargs' gives you control over how many arguments it passes to the +command each time it executes it. By default, it uses up to `ARG_MAX' +- 2k, or 20k, whichever is smaller, characters per command. It uses as +many lines and arguments as fit within that limit. The following +options modify those values. + +`--no-run-if-empty' +`-r' + If the standard input does not contain any nonblanks, do not run + the command. By default, the command is run once even if there is + no input. + +`--max-lines[=MAX-LINES]' +`-l[MAX-LINES]' + Use at most MAX-LINES nonblank input lines per command line; + MAX-LINES defaults to 1 if omitted. Trailing blanks cause an + input line to be logically continued on the next input line, for + the purpose of counting the lines. Implies `-x'. + +`--max-args=MAX-ARGS' +`-n MAX-ARGS' + Use at most MAX-ARGS arguments per command line. Fewer than + MAX-ARGS arguments will be used if the size (see the `-s' option) + is exceeded, unless the `-x' option is given, in which case + `xargs' will exit. + +`--max-chars=MAX-CHARS' +`-s MAX-CHARS' + Use at most MAX-CHARS characters per command line, including the + command and initial arguments and the terminating nulls at the + ends of the argument strings. + +`--max-procs=MAX-PROCS' +`-P MAX-PROCS' + Run up to MAX-PROCS processes at a time; the default is 1. If + MAX-PROCS is 0, `xargs' will run as many processes as possible at + a time. Use the `-n', `-s', or `-l' option with `-P'; otherwise + chances are that the command will be run only once. + + +File: find.info, Node: Interspersing File Names, Prev: Limiting Command Size, Up: Multiple Files + +Interspersing File Names +........................ + + `xargs' can insert the name of the file it is processing between +arguments you give for the command. Unless you also give options to +limit the command size (*note Limiting Command Size::.), this mode of +operation is equivalent to `find -exec' (*note Single File::.). + +`--replace[=REPLACE-STR]' +`-i[REPLACE-STR]' + Replace occurences of REPLACE-STR in the initial arguments with + names read from standard input. Also, unquoted blanks do not + terminate arguments. If REPLACE-STR is omitted, it defaults to + `{}' (like for `find -exec'). Implies `-x' and `-l 1'. As an + example, to sort each file the `bills' directory, leaving the + output in that file name with `.sorted' appended, you could do: + + find bills -type f | xargs -iXX sort -o XX.sorted XX + + The equivalent command using `find -exec' is: + + find bills -type f -exec sort -o '{}.sorted' '{}' ';' + + +File: find.info, Node: Querying, Prev: Multiple Files, Up: Run Commands + +Querying +-------- + + To ask the user whether to execute a command on a single file, you +can use the `find' primary `-ok' instead of `-exec': + + - Action: -ok COMMAND ; + Like `-exec' (*note Single File::.), but ask the user first (on + the standard input); if the response does not start with `y' or + `Y', do not run the command, and return false. + + When processing multiple files with a single command, to query the +user you give `xargs' the following option. When using this option, you +might find it useful to control the number of files processed per +invocation of the command (*note Limiting Command Size::.). + +`--interactive' +`-p' + Prompt the user about whether to run each command line and read a + line from the terminal. Only run the command line if the response + starts with `y' or `Y'. Implies `-t'. + + +File: find.info, Node: Adding Tests, Prev: Run Commands, Up: Actions + +Adding Tests +============ + + You can test for file attributes that none of the `find' builtin +tests check. To do this, use `xargs' to run a program that filters a +list of files printed by `find'. If possible, use `find' builtin tests +to pare down the list, so the program run by `xargs' has less work to +do. The tests builtin to `find' will likely run faster than tests that +other programs perform. + + For example, here is a way to print the names of all of the +unstripped binaries in the `/usr/local' directory tree. Builtin tests +avoid running `file' on files that are not regular files or are not +executable. + + find /usr/local -type f -perm +a=x | xargs file | + grep 'not stripped' | cut -d: -f1 + +The `cut' program removes everything after the file name from the +output of `file'. + + If you want to place a special test somewhere in the middle of a +`find' expression, you can use `-exec' to run a program that performs +the test. Because `-exec' evaluates to the exit status of the executed +program, you can write a program (which can be a shell script) that +tests for a special attribute and make it exit with a true (zero) or +false (non-zero) status. It is a good idea to place such a special +test *after* the builtin tests, because it starts a new process which +could be avoided if a builtin test evaluates to false. Use this method +only when `xargs' is not flexible enough, because starting one or more +new processes to test each file is slower than using `xargs' to start +one process that tests many files. + + Here is a shell script called `unstripped' that checks whether its +argument is an unstripped binary file: + + #!/bin/sh + file $1 | grep 'not stripped' > /dev/null + + This script relies on the fact that the shell exits with the status +of the last program it executed, in this case `grep'. `grep' exits +with a true status if it found any matches, false if not. Here is an +example of using the script (assuming it is in your search path). It +lists the stripped executables in the file `sbins' and the unstripped +ones in `ubins'. + + find /usr/local -type f -perm +a=x \ + \( -exec unstripped '{}' \; -fprint ubins -o -fprint sbins \) + + +File: find.info, Node: Common Tasks, Next: Databases, Prev: Actions, Up: Top + +Common Tasks +************ + + The sections that follow contain some extended examples that both +give a good idea of the power of these programs, and show you how to +solve common real-world problems. + +* Menu: + +* Viewing And Editing:: +* Archiving:: +* Cleaning Up:: +* Strange File Names:: +* Fixing Permissions:: +* Classifying Files:: + diff --git a/doc/find.info-2 b/doc/find.info-2 new file mode 100644 index 0000000..4d5a980 --- /dev/null +++ b/doc/find.info-2 @@ -0,0 +1,1069 @@ +This is Info file find.info, produced by Makeinfo-1.55 from the input +file find.texi. + +START-INFO-DIR-ENTRY +* Finding Files: (find). Listing and operating on files + that match certain criteria. +END-INFO-DIR-ENTRY + + This file documents the GNU utilities for finding files that match +certain criteria and performing various operations on them. + + Copyright (C) 1994 Free Software Foundation, Inc. + + 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 approved by the Foundation. + + +File: find.info, Node: Viewing And Editing, Next: Archiving, Up: Common Tasks + +Viewing And Editing +=================== + + To view a list of files that meet certain criteria, simply run your +file viewing program with the file names as arguments. Shells +substitute a command enclosed in backquotes with its output, so the +whole command looks like this: + + less `find /usr/include -name '*.h' | xargs grep -l mode_t` + +You can edit those files by giving an editor name instead of a file +viewing program. + + +File: find.info, Node: Archiving, Next: Cleaning Up, Prev: Viewing And Editing, Up: Common Tasks + +Archiving +========= + + You can pass a list of files produced by `find' to a file archiving +program. GNU `tar' and `cpio' can both read lists of file names from +the standard input--either delimited by nulls (the safe way) or by +blanks (the lazy, risky default way). To use null-delimited names, +give them the `--null' option. You can store a file archive in a file, +write it on a tape, or send it over a network to extract on another +machine. + + One common use of `find' to archive files is to send a list of the +files in a directory tree to `cpio'. Use `-depth' so if a directory +does not have write permission for its owner, its contents can still be +restored from the archive since the directory's permissions are +restored after its contents. Here is an example of doing this using +`cpio'; you could use a more complex `find' expression to archive only +certain files. + + find . -depth -print0 | + cpio --create --null --format=crc --file=/dev/nrst0 + + You could restore that archive using this command: + + cpio --extract --null --make-dir --unconditional \ + --preserve --file=/dev/nrst0 + + Here are the commands to do the same things using `tar': + + find . -depth -print0 | + tar --create --null --files-from=- --file=/dev/nrst0 + + tar --extract --null --preserve-perm --same-owner \ + --file=/dev/nrst0 + + Here is an example of copying a directory from one machine to +another: + + find . -depth -print0 | cpio -0o -Hnewc | + rsh OTHER-MACHINE "cd `pwd` && cpio -i0dum" + + +File: find.info, Node: Cleaning Up, Next: Strange File Names, Prev: Archiving, Up: Common Tasks + +Cleaning Up +=========== + + This section gives examples of removing unwanted files in various +situations. Here is a command to remove the CVS backup files created +when an update requires a merge: + + find . -name '.#*' -print0 | xargs -0r rm -f + + You can run this command to clean out your clutter in `/tmp'. You +might place it in the file your shell runs when you log out +(`.bash_logout', `.logout', or `.zlogout', depending on which shell you +use). + + find /tmp -user $LOGNAME -type f -print0 | xargs -0 -r rm -f + + To remove old Emacs backup and auto-save files, you can use a command +like the following. It is especially important in this case to use +null-terminated file names because Emacs packages like the VM mailer +often create temporary file names with spaces in them, like `#reply to +David J. MacKenzie<1>#'. + + find ~ \( -name '*~' -o -name '#*#' \) -print0 | + xargs --no-run-if-empty --null rm -vf + + Removing old files from `/tmp' is commonly done from `cron': + + find /tmp /var/tmp -not -type d -mtime +3 -print0 | + xargs --null --no-run-if-empty rm -f + + find /tmp /var/tmp -depth -mindepth 1 -type d -empty -print0 | + xargs --null --no-run-if-empty rmdir + + The second `find' command above uses `-depth' so it cleans out empty +directories depth-first, hoping that the parents become empty and can +be removed too. It uses `-mindepth' to avoid removing `/tmp' itself if +it becomes totally empty. + + +File: find.info, Node: Strange File Names, Next: Fixing Permissions, Prev: Cleaning Up, Up: Common Tasks + +Strange File Names +================== + + `find' can help you remove or rename a file with strange characters +in its name. People are sometimes stymied by files whose names contain +characters such as spaces, tabs, control characters, or characters with +the high bit set. The simplest way to remove such files is: + + rm -i SOME*PATTERN*THAT*MATCHES*THE*PROBLEM*FILE + + `rm' asks you whether to remove each file matching the given +pattern. If you are using an old shell, this approach might not work if +the file name contains a character with the high bit set; the shell may +strip it off. A more reliable way is: + + find . -maxdepth 1 TESTS -ok rm '{}' \; + +where TESTS uniquely identify the file. The `-maxdepth 1' option +prevents `find' from wasting time searching for the file in any +subdirectories; if there are no subdirectories, you may omit it. A +good way to uniquely identify the problem file is to figure out its +inode number; use + + ls -i + + Suppose you have a file whose name contains control characters, and +you have found that its inode number is 12345. This command prompts +you for whether to remove it: + + find . -maxdepth 1 -inum 12345 -ok rm -f '{}' \; + + If you don't want to be asked, perhaps because the file name may +contain a strange character sequence that will mess up your screen when +printed, then use `-exec' instead of `-ok'. + + If you want to rename the file instead, you can use `mv' instead of +`rm': + + find . -maxdepth 1 -inum 12345 -ok mv '{}' NEW-FILE-NAME \; + + +File: find.info, Node: Fixing Permissions, Next: Classifying Files, Prev: Strange File Names, Up: Common Tasks + +Fixing Permissions +================== + + Suppose you want to make sure that everyone can write to the +directories in a certain directory tree. Here is a way to find +directories lacking either user or group write permission (or both), +and fix their permissions: + + find . -type d -not -perm -ug=w | xargs chmod ug+w + +You could also reverse the operations, if you want to make sure that +directories do *not* have world write permission. + + +File: find.info, Node: Classifying Files, Prev: Fixing Permissions, Up: Common Tasks + +Classifying Files +================= + + If you want to classify a set of files into several groups based on +different criteria, you can use the comma operator to perform multiple +independent tests on the files. Here is an example: + + find / -type d \( -perm -o=w -fprint allwrite , \ + -perm -o=x -fprint allexec \) + + echo "Directories that can be written to by everyone:" + cat allwrite + echo "" + echo "Directories with search permissions for everyone:" + cat allexec + + `find' has only to make one scan through the directory tree (which +is one of the most time consuming parts of its work). + + +File: find.info, Node: Databases, Next: File Permissions, Prev: Common Tasks, Up: Top + +File Name Databases +******************* + + The file name databases used by `locate' contain lists of files that +were in particular directory trees when the databases were last +updated. The file name of the default database is determined when +`locate' and `updatedb' are configured and installed. The frequency +with which the databases are updated and the directories for which they +contain entries depend on how often `updatedb' is run, and with which +arguments. + +* Menu: + +* Database Locations:: +* Database Formats:: + + +File: find.info, Node: Database Locations, Next: Database Formats, Up: Databases + +Database Locations +================== + + There can be multiple file name databases. Users can select which +databases `locate' searches using an environment variable or a command +line option. The system administrator can choose the file name of the +default database, the frequency with which the databases are updated, +and the directories for which they contain entries. File name +databases are updated by running the `updatedb' program, typically +nightly. + + In networked environments, it often makes sense to build a database +at the root of each filesystem, containing the entries for that +filesystem. `updatedb' is then run for each filesystem on the +fileserver where that filesystem is on a local disk, to prevent +thrashing the network. Here are the options to `updatedb' to select +which directories each database contains entries for: + +`--localpaths='PATH...'' + Non-network directories to put in the database. Default is `/'. + +`--netpaths='PATH...'' + Network (NFS, AFS, RFS, etc.) directories to put in the database. + Default is none. + +`--prunepaths='PATH...'' + Directories to not put in the database, which would otherwise be. + Default is `/tmp /usr/tmp /var/tmp /afs'. + +`--output=DBFILE' + The database file to build. Default is system-dependent, but + typically `/usr/local/var/locatedb'. + +`--netuser=USER' + The user to search network directories as, using `su'. Default is + `daemon'. + + +File: find.info, Node: Database Formats, Prev: Database Locations, Up: Databases + +Database Formats +================ + + The file name databases contain lists of files that were in +particular directory trees when the databases were last updated. The +file name database format changed starting with GNU `locate' version +4.0 to allow machines with diffent byte orderings to share the +databases. The new GNU `locate' can read both the old and new database +formats. However, old versions of `locate' and `find' produce incorrect +results if given a new-format database. + +* Menu: + +* New Database Format:: +* Sample Database:: +* Old Database Format:: + + +File: find.info, Node: New Database Format, Next: Sample Database, Up: Database Formats + +New Database Format +------------------- + + `updatedb' runs a program called `frcode' to "front-compress" the +list of file names, which reduces the database size by a factor of 4 to +5. Front-compression (also known as incremental encoding) works as +follows. + + The database entries are a sorted list (case-insensitively, for +users' convenience). Since the list is sorted, each entry is likely to +share a prefix (initial string) with the previous entry. Each database +entry begins with an offset-differential count byte, which is the +additional number of characters of prefix of the preceding entry to use +beyond the number that the preceding entry is using of its predecessor. +(The counts can be negative.) Following the count is a +null-terminated ASCII remainder--the part of the name that follows the +shared prefix. + + If the offset-differential count is larger than can be stored in a +byte (+/-127), the byte has the value 0x80 and the count follows in a +2-byte word, with the high byte first (network byte order). + + Every database begins with a dummy entry for a file called +`LOCATE02', which `locate' checks for to ensure that the database file +has the correct format; it ignores the entry in doing the search. + + Databases can not be concatenated together, even if the first (dummy) +entry is trimmed from all but the first database. This is because the +offset-differential count in the first entry of the second and following +databases will be wrong. + + +File: find.info, Node: Sample Database, Next: Old Database Format, Prev: New Database Format, Up: Database Formats + +Sample Database +--------------- + + Sample input to `frcode': + + /usr/src + /usr/src/cmd/aardvark.c + /usr/src/cmd/armadillo.c + /usr/tmp/zoo + + Length of the longest prefix of the preceding entry to share: + + 0 /usr/src + 8 /cmd/aardvark.c + 14 rmadillo.c + 5 tmp/zoo + + Output from `frcode', with trailing nulls changed to newlines and +count bytes made printable: + + 0 LOCATE02 + 0 /usr/src + 8 /cmd/aardvark.c + 6 rmadillo.c + -9 tmp/zoo + + (6 = 14 - 8, and -9 = 5 - 14) + + +File: find.info, Node: Old Database Format, Prev: Sample Database, Up: Database Formats + +Old Database Format +------------------- + + The old database format is used by Unix `locate' and `find' programs +and earlier releases of the GNU ones. `updatedb' produces this format +if given the `--old-format' option. + + `updatedb' runs programs called `bigram' and `code' to produce +old-format databases. The old format differs from the new one in the +following ways. Instead of each entry starting with an +offset-differential count byte and ending with a null, byte values from +0 through 28 indicate offset-differential counts from -14 through 14. +The byte value indicating that a long offset-differential count follows +is 0x1e (30), not 0x80. The long counts are stored in host byte order, +which is not necessarily network byte order, and host integer word size, +which is usually 4 bytes. They also represent a count 14 less than +their value. The database lines have no termination byte; the start of +the next line is indicated by its first byte having a value <= 30. + + In addition, instead of starting with a dummy entry, the old database +format starts with a 256 byte table containing the 128 most common +bigrams in the file list. A bigram is a pair of adjacent bytes. Bytes +in the database that have the high bit set are indexes (with the high +bit cleared) into the bigram table. The bigram and offset-differential +count coding makes these databases 20-25% smaller than the new format, +but makes them not 8-bit clean. Any byte in a file name that is in the +ranges used for the special codes is replaced in the database by a +question mark, which not coincidentally is the shell wildcard to match a +single character. + + +File: find.info, Node: File Permissions, Next: Reference, Prev: Databases, Up: Top + +File Permissions +**************** + + Each file has a set of "permissions" that control the kinds of +access that users have to that file. The permissions for a file are +also called its "access mode". They can be represented either in +symbolic form or as an octal number. + +* Menu: + +* Mode Structure:: Structure of file permissions. +* Symbolic Modes:: Mnemonic permissions representation. +* Numeric Modes:: Permissions as octal numbers. + + +File: find.info, Node: Mode Structure, Next: Symbolic Modes, Up: File Permissions + +Structure of File Permissions +============================= + + There are three kinds of permissions that a user can have for a file: + + 1. permission to read the file. For directories, this means + permission to list the contents of the directory. + + 2. permission to write to (change) the file. For directories, this + means permission to create and remove files in the directory. + + 3. permission to execute the file (run it as a program). For + directories, this means permission to access files in the + directory. + + There are three categories of users who may have different +permissions to perform any of the above operations on a file: + + 1. the file's owner; + + 2. other users who are in the file's group; + + 3. everyone else. + + Files are given an owner and group when they are created. Usually +the owner is the current user and the group is the group of the +directory the file is in, but this varies with the operating system, the +filesystem the file is created on, and the way the file is created. You +can change the owner and group of a file by using the `chown' and +`chgrp' commands. + + In addition to the three sets of three permissions listed above, a +file's permissions have three special components, which affect only +executable files (programs) and, on some systems, directories: + + 1. set the process's effective user ID to that of the file upon + execution (called the "setuid bit"). No effect on directories. + + 2. set the process's effective group ID to that of the file upon + execution (called the "setgid bit"). For directories on some + systems, put files created in the directory into the same group as + the directory, no matter what group the user who creates them is + in. + + 3. save the program's text image on the swap device so it will load + more quickly when run (called the "sticky bit"). For directories + on some systems, prevent users from removing files that they do + not own in the directory; this is called making the directory + "append-only". + + +File: find.info, Node: Symbolic Modes, Next: Numeric Modes, Prev: Mode Structure, Up: File Permissions + +Symbolic Modes +============== + + "Symbolic modes" represent changes to files' permissions as +operations on single-character symbols. They allow you to modify either +all or selected parts of files' permissions, optionally based on their +previous values, and perhaps on the current `umask' as well (*note +Umask and Protection::.). + + The format of symbolic modes is: + + [ugoa...][[+-=][rwxXstugo...]...][,...] + + The following sections describe the operators and other details of +symbolic modes. + +* Menu: + +* Setting Permissions:: Basic operations on permissions. +* Copying Permissions:: Copying existing permissions. +* Changing Special Permissions:: Special permissions. +* Conditional Executability:: Conditionally affecting executability. +* Multiple Changes:: Making multiple changes. +* Umask and Protection:: The effect of the umask. + + +File: find.info, Node: Setting Permissions, Next: Copying Permissions, Up: Symbolic Modes + +Setting Permissions +------------------- + + The basic symbolic operations on a file's permissions are adding, +removing, and setting the permission that certain users have to read, +write, and execute the file. These operations have the following +format: + + USERS OPERATION PERMISSIONS + +The spaces between the three parts above are shown for readability only; +symbolic modes can not contain spaces. + + The USERS part tells which users' access to the file is changed. It +consists of one or more of the following letters (or it can be empty; +*note Umask and Protection::., for a description of what happens then). +When more than one of these letters is given, the order that they are +in does not matter. + +`u' + the user who owns the file; + +`g' + other users who are in the file's group; + +`o' + all other users; + +`a' + all users; the same as `ugo'. + + The OPERATION part tells how to change the affected users' access to +the file, and is one of the following symbols: + +`+' + to add the PERMISSIONS to whatever permissions the USERS already + have for the file; + +`-' + to remove the PERMISSIONS from whatever permissions the USERS + already have for the file; + +`=' + to make the PERMISSIONS the only permissions that the USERS have + for the file. + + The PERMISSIONS part tells what kind of access to the file should be +changed; it is zero or more of the following letters. As with the +USERS part, the order does not matter when more than one letter is +given. Omitting the PERMISSIONS part is useful only with the `=' +operation, where it gives the specified USERS no access at all to the +file. + +`r' + the permission the USERS have to read the file; + +`w' + the permission the USERS have to write to the file; + +`x' + the permission the USERS have to execute the file. + + For example, to give everyone permission to read and write a file, +but not to execute it, use: + + a=rw + + To remove write permission for from all users other than the file's +owner, use: + + go-w + +The above command does not affect the access that the owner of the file +has to it, nor does it affect whether other users can read or execute +the file. + + To give everyone except a file's owner no permission to do anything +with that file, use the mode below. Other users could still remove the +file, if they have write permission on the directory it is in. + + go= + +Another way to specify the same thing is: + + og-rxw + + +File: find.info, Node: Copying Permissions, Next: Changing Special Permissions, Prev: Setting Permissions, Up: Symbolic Modes + +Copying Existing Permissions +---------------------------- + + You can base part of a file's permissions on part of its existing +permissions. To do this, instead of using `r', `w', or `x' after the +operator, you use the letter `u', `g', or `o'. For example, the mode + + o+g + +adds the permissions for users who are in a file's group to the +permissions that other users have for the file. Thus, if the file +started out as mode 664 (`rw-rw-r--'), the above mode would change it +to mode 666 (`rw-rw-rw-'). If the file had started out as mode 741 +(`rwxr----x'), the above mode would change it to mode 745 +(`rwxr--r-x'). The `-' and `=' operations work analogously. + + +File: find.info, Node: Changing Special Permissions, Next: Conditional Executability, Prev: Copying Permissions, Up: Symbolic Modes + +Changing Special Permissions +---------------------------- + + In addition to changing a file's read, write, and execute +permissions, you can change its special permissions. *Note Mode +Structure::, for a summary of these permissions. + + To change a file's permission to set the user ID on execution, use +`u' in the USERS part of the symbolic mode and `s' in the PERMISSIONS +part. + + To change a file's permission to set the group ID on execution, use +`g' in the USERS part of the symbolic mode and `s' in the PERMISSIONS +part. + + To change a file's permission to stay permanently on the swap device, +use `o' in the USERS part of the symbolic mode and `t' in the +PERMISSIONS part. + + For example, to add set user ID permission to a program, you can use +the mode: + + u+s + + To remove both set user ID and set group ID permission from it, you +can use the mode: + + ug-s + + To cause a program to be saved on the swap device, you can use the +mode: + + o+t + + Remember that the special permissions only affect files that are +executable, plus, on some systems, directories (on which they have +different meanings; *note Mode Structure::.). Using `a' in the USERS +part of a symbolic mode does not cause the special permissions to be +affected; thus, + + a+s + +has *no effect*. You must use `u', `g', and `o' explicitly to affect +the special permissions. Also, the combinations `u+t', `g+t', and +`o+s' have no effect. + + The `=' operator is not very useful with special permissions; for +example, the mode: + + o=t + +does cause the file to be saved on the swap device, but it also removes +all read, write, and execute permissions that users not in the file's +group might have had for it. + + +File: find.info, Node: Conditional Executability, Next: Multiple Changes, Prev: Changing Special Permissions, Up: Symbolic Modes + +Conditional Executability +------------------------- + + There is one more special type of symbolic permission: if you use +`X' instead of `x', execute permission is affected only if the file +already had execute permission or is a directory. It affects +directories' execute permission even if they did not initially have any +execute permissions set. + + For example, this mode: + + a+X + +gives all users permission to execute files (or search directories) if +anyone could before. + + +File: find.info, Node: Multiple Changes, Next: Umask and Protection, Prev: Conditional Executability, Up: Symbolic Modes + +Making Multiple Changes +----------------------- + + The format of symbolic modes is actually more complex than described +above (*note Setting Permissions::.). It provides two ways to make +multiple changes to files' permissions. + + The first way is to specify multiple OPERATION and PERMISSIONS parts +after a USERS part in the symbolic mode. + + For example, the mode: + + og+rX-w + +gives users other than the owner of the file read permission and, if it +is a directory or if someone already had execute permission to it, +gives them execute permission; and it also denies them write permission +to it file. It does not affect the permission that the owner of the +file has for it. The above mode is equivalent to the two modes: + + og+rX + og-w + + The second way to make multiple changes is to specify more than one +simple symbolic mode, separated by commas. For example, the mode: + + a+r,go-w + +gives everyone permission to read the file and removes write permission +on it for all users except its owner. Another example: + + u=rwx,g=rx,o= + +sets all of the non-special permissions for the file explicitly. (It +gives users who are not in the file's group no permission at all for +it.) + + The two methods can be combined. The mode: + + a+r,g+x-w + +gives all users permission to read the file, and gives users who are in +the file's group permission to execute it, as well, but not permission +to write to it. The above mode could be written in several different +ways; another is: + + u+r,g+rx,o+r,g-w + + +File: find.info, Node: Umask and Protection, Prev: Multiple Changes, Up: Symbolic Modes + +The Umask and Protection +------------------------ + + If the USERS part of a symbolic mode is omitted, it defaults to `a' +(affect all users), except that any permissions that are *set* in the +system variable `umask' are *not affected*. The value of `umask' can +be set using the `umask' command. Its default value varies from system +to system. + + Omitting the USERS part of a symbolic mode is generally not useful +with operations other than `+'. It is useful with `+' because it +allows you to use `umask' as an easily customizable protection against +giving away more permission to files than you intended to. + + As an example, if `umask' has the value 2, which removes write +permission for users who are not in the file's group, then the mode: + + +w + +adds permission to write to the file to its owner and to other users who +are in the file's group, but *not* to other users. In contrast, the +mode: + + a+w + +ignores `umask', and *does* give write permission for the file to all +users. + + +File: find.info, Node: Numeric Modes, Prev: Symbolic Modes, Up: File Permissions + +Numeric Modes +============= + + File permissions are stored internally as 16 bit integers. As an +alternative to giving a symbolic mode, you can give an octal (base 8) +number that corresponds to the internal representation of the new mode. +This number is always interpreted in octal; you do not have to add a +leading 0, as you do in C. Mode 0055 is the same as mode 55. + + A numeric mode is usually shorter than the corresponding symbolic +mode, but it is limited in that it can not take into account a file's +previous permissions; it can only set them absolutely. + + The permissions granted to the user, to other users in the file's +group, and to other users not in the file's group are each stored as +three bits, which are represented as one octal digit. The three special +permissions are also each stored as one bit, and they are as a group +represented as another octal digit. Here is how the bits are arranged +in the 16 bit integer, starting with the lowest valued bit: + + Value in Corresponding + Mode Permission + + Other users not in the file's group: + 1 Execute + 2 Write + 4 Read + + Other users in the file's group: + 10 Execute + 20 Write + 40 Read + + The file's owner: + 100 Execute + 200 Write + 400 Read + + Special permissions: + 1000 Save text image on swap device + 2000 Set group ID on execution + 4000 Set user ID on execution + + For example, numeric mode 4755 corresponds to symbolic mode +`u=rwxs,go=rx', and numeric mode 664 corresponds to symbolic mode +`ug=rw,o=r'. Numeric mode 0 corresponds to symbolic mode `ugo='. + + +File: find.info, Node: Reference, Next: Primary Index, Prev: File Permissions, Up: Top + +Reference +********* + + Below are summaries of the command line syntax for the programs +discussed in this manual. + +* Menu: + +* Invoking find:: +* Invoking locate:: +* Invoking updatedb:: +* Invoking xargs:: + + +File: find.info, Node: Invoking find, Next: Invoking locate, Up: Reference + +Invoking `find' +=============== + + find [FILE...] [EXPRESSION] + + `find' searches the directory tree rooted at each file name FILE by +evaluating the EXPRESSION on each file it finds in the tree. + + `find' considers the first argument that begins with `-', `(', `)', +`,', or `!' to be the beginning of the expression; any arguments before +it are paths to search, and any arguments after it are the rest of the +expression. If no paths are given, the current directory is used. If +no expression is given, the expression `-print' is used. + + `find' exits with status 0 if all files are processed successfully, +greater than 0 if errors occur. + + *Note Primary Index::, for a summary of all of the tests, actions, +and options that the expression can contain. + + `find' also recognizes two options for administrative use: + +`--help' + Print a summary of the command-line argument format and exit. + +`--version' + Print the version number of `find' and exit. + + +File: find.info, Node: Invoking locate, Next: Invoking updatedb, Prev: Invoking find, Up: Reference + +Invoking `locate' +================= + + locate [OPTION...] PATTERN... + +`--database=PATH' +`-d PATH' + Instead of searching the default file name database, search the + file name databases in PATH, which is a colon-separated list of + database file names. You can also use the environment variable + `LOCATE_PATH' to set the list of database files to search. The + option overrides the environment variable if both are used. + +`--help' + Print a summary of the options to `locate' and exit. + +`--version' + Print the version number of `locate' and exit. + + +File: find.info, Node: Invoking updatedb, Next: Invoking xargs, Prev: Invoking locate, Up: Reference + +Invoking `updatedb' +=================== + + updatedb [OPTION...] + +`--localpaths='PATH...'' + Non-network directories to put in the database. Default is `/'. + +`--netpaths='PATH...'' + Network (NFS, AFS, RFS, etc.) directories to put in the database. + Default is none. + +`--prunepaths='PATH...'' + Directories to not put in the database, which would otherwise be. + Default is `/tmp /usr/tmp /var/tmp /afs'. + +`--output=DBFILE' + The database file to build. Default is system-dependent, but + typically `/usr/local/var/locatedb'. + +`--netuser=USER' + The user to search network directories as, using `su'(1). Default + is `daemon'. + + +File: find.info, Node: Invoking xargs, Prev: Invoking updatedb, Up: Reference + +Invoking `xargs' +================ + + xargs [OPTION...] [COMMAND [INITIAL-ARGUMENTS]] + + `xargs' exits with the following status: + +0 + if it succeeds + +123 + if any invocation of the command exited with status 1-125 + +124 + if the command exited with status 255 + +125 + if the command is killed by a signal + +126 + if the command cannot be run + +127 + if the command is not found + +1 + if some other error occurred. + +`--null' +`-0' + Input filenames are terminated by a null character instead of by + whitespace, and the quotes and backslash are not special (every + character is taken literally). Disables the end of file string, + which is treated like any other argument. + +`--eof[=EOF-STR]' +`-e[EOF-STR]' + Set the end of file string to EOF-STR. If the end of file string + occurs as a line of input, the rest of the input is ignored. If + EOF-STR is omitted, there is no end of file string. If this + option is not given, the end of file string defaults to `_'. + +`--help' + Print a summary of the options to `xargs' and exit. + +`--replace[=REPLACE-STR]' +`-i[REPLACE-STR]' + Replace occurences of REPLACE-STR in the initial arguments with + names read from standard input. Also, unquoted blanks do not + terminate arguments. If REPLACE-STR is omitted, it defaults to + `{}' (like for `find -exec'). Implies `-x' and `-l 1'. + +`--max-lines[=MAX-LINES]' +`-l[MAX-LINES]' + Use at most MAX-LINES nonblank input lines per command line; + MAX-LINES defaults to 1 if omitted. Trailing blanks cause an + input line to be logically continued on the next input line, for + the purpose of counting the lines. Implies `-x'. + +`--max-args=MAX-ARGS' +`-n MAX-ARGS' + Use at most MAX-ARGS arguments per command line. Fewer than + MAX-ARGS arguments will be used if the size (see the `-s' option) + is exceeded, unless the `-x' option is given, in which case + `xargs' will exit. + +`--interactive' +`-p' + Prompt the user about whether to run each command line and read a + line from the terminal. Only run the command line if the response + starts with `y' or `Y'. Implies `-t'. + +`--no-run-if-empty' +`-r' + If the standard input does not contain any nonblanks, do not run + the command. By default, the command is run once even if there is + no input. + +`--max-chars=MAX-CHARS' +`-s MAX-CHARS' + Use at most MAX-CHARS characters per command line, including the + command and initial arguments and the terminating nulls at the + ends of the argument strings. + +`--verbose' +`-t' + Print the command line on the standard error output before + executing it. + +`--version' + Print the version number of `xargs' and exit. + +`--exit' +`-x' + Exit if the size (see the -S option) is exceeded. + +`--max-procs=MAX-PROCS' +`-P MAX-PROCS' + Run up to MAX-PROCS processes at a time; the default is 1. If + MAX-PROCS is 0, `xargs' will run as many processes as possible at + a time. + + +File: find.info, Node: Primary Index, Prev: Reference, Up: Top + +`find' Primary Index +******************** + + This is a list of all of the primaries (tests, actions, and options) +that make up `find' expressions for selecting files. *Note find +Expressions::, for more information on expressions. + +* Menu: + +* -amin: Age Ranges. +* -anewer: Comparing Timestamps. +* -atime: Age Ranges. +* -cmin: Age Ranges. +* -cnewer: Comparing Timestamps. +* -ctime: Age Ranges. +* -daystart: Age Ranges. +* -depth: Directories. +* -empty: Size. +* -exec: Single File. +* -false: Combining Primaries With Operators. +* -fls: Print File Information. +* -follow: Symbolic Links. +* -fprint: Print File Name. +* -fprint0: Safe File Name Handling. +* -fprintf: Print File Information. +* -fstype: Filesystems. +* -gid: Owner. +* -group: Owner. +* -ilname: Symbolic Links. +* -iname: Base Name Patterns. +* -inum: Hard Links. +* -ipath: Full Name Patterns. +* -iregex: Full Name Patterns. +* -links: Hard Links. +* -lname: Symbolic Links. +* -ls: Print File Information. +* -maxdepth: Directories. +* -mindepth: Directories. +* -mmin: Age Ranges. +* -mount: Filesystems. +* -mtime: Age Ranges. +* -name: Base Name Patterns. +* -newer: Comparing Timestamps. +* -nogroup: Owner. +* -noleaf: Directories. +* -nouser: Owner. +* -ok: Querying. +* -path: Full Name Patterns. +* -perm: Permissions. +* -print: Print File Name. +* -print0: Safe File Name Handling. +* -printf: Print File Information. +* -prune: Directories. +* -regex: Full Name Patterns. +* -size: Size. +* -true: Combining Primaries With Operators. +* -type: Type. +* -uid: Owner. +* -used: Comparing Timestamps. +* -user: Owner. +* -xdev: Filesystems. +* -xtype: Type. + + diff --git a/doc/find.texi b/doc/find.texi new file mode 100644 index 0000000..0b8d6f4 --- /dev/null +++ b/doc/find.texi @@ -0,0 +1,2206 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename find.info +@settitle Finding Files +@c For double-sided printing, uncomment: +@c @setchapternewpage odd +@c %**end of header + +@set EDITION 1.1 +@set VERSION 4.1 +@set UPDATED November 1994 + +@iftex +@finalout +@end iftex + +@ifinfo +@format +START-INFO-DIR-ENTRY +* Finding Files: (find). Listing and operating on files + that match certain criteria. +END-INFO-DIR-ENTRY +@end format + +This file documents the GNU utilities for finding files that match +certain criteria and performing various operations on them. + +Copyright (C) 1994 Free Software Foundation, Inc. + +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 approved +by the Foundation. +@end ifinfo + +@titlepage +@title Finding Files +@subtitle Edition @value{EDITION}, for GNU @code{find} version @value{VERSION} +@subtitle @value{UPDATED} +@author by David MacKenzie + +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1994 Free Software Foundation, Inc. + +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 approved +by the Foundation. +@end titlepage + +@node Top, Introduction, , (dir) +@comment node-name, next, previous, up + +@ifinfo +This file documents the GNU utilities for finding files that match +certain criteria and performing various actions on them. +This is edition @value{EDITION}, for @code{find} version @value{VERSION}. +@end ifinfo + +@c The master menu, created with texinfo-master-menu, goes here. + +@menu +* Introduction:: Summary of the tasks this manual describes. +* Finding Files:: Finding files that match certain criteria. +* Actions:: Doing things to files you have found. +* Common Tasks:: Solutions to common real-world problems. +* Databases:: Maintaining file name databases. +* File Permissions:: How to control access to files. +* Reference:: Summary of how to invoke the programs. +* Primary Index:: The components of @code{find} expressions. +@end menu + +@node Introduction, Finding Files, Top, Top +@chapter Introduction + +This manual shows how to find files that meet criteria you specify, and +how to perform various actions on the files that you find. The +principal programs that you use to perform these tasks are @code{find}, +@code{locate}, and @code{xargs}. Some of the examples in this manual +use capabilities specific to the GNU versions of those programs. + +GNU @code{find} was originally written by Eric Decker, with enhancements +by David MacKenzie, Jay Plett, and Tim Wood. GNU @code{xargs} was +originally written by Mike Rendell, with enhancements by David +MacKenzie. GNU @code{locate} and its associated utilities were +originally written by James Woods, with enhancements by David MacKenzie. +The idea for @samp{find -print0} and @samp{xargs -0} came from Dan +Bernstein. Many other people have contributed bug fixes, small +improvements, and helpful suggestions. Thanks! + +Mail suggestions and bug reports for these programs to +@code{bug-gnu-utils@@prep.ai.mit.edu}. Please include the version +number, which you can get by running @samp{find --version}. + +@menu +* Scope:: +* Overview:: +* find Expressions:: +@end menu + +@node Scope +@section Scope + +For brevity, the word @dfn{file} in this manual means a regular file, a +directory, a symbolic link, or any other kind of node that has a +directory entry. A directory entry is also called a @dfn{file name}. A +file name may contain some, all, or none of the directories in a path +that leads to the file. These are all examples of what this manual +calls ``file names'': + +@example +parser.c +README +./budget/may-94.sc +fred/.cshrc +/usr/local/include/termcap.h +@end example + +A @dfn{directory tree} is a directory and the files it contains, all of +its subdirectories and the files they contain, etc. It can also be a +single non-directory file. + +These programs enable you to find the files in one or more directory +trees that: + +@itemize @bullet +@item +have names that contain certain text or match a certain pattern; +@item +are links to certain files; +@item +were last used during a certain period of time; +@item +are within a certain size range; +@item +are of a certain type (regular file, directory, symbolic link, etc.); +@item +are owned by a certain user or group; +@item +have certain access permissions; +@item +contain text that matches a certain pattern; +@item +are within a certain depth in the directory tree; +@item +or some combination of the above. +@end itemize + +Once you have found the files you're looking for (or files that are +potentially the ones you're looking for), you can do more to them than +simply list their names. You can get any combination of the files' +attributes, or process the files in many ways, either individually or in +groups of various sizes. Actions that you might want to perform on the +files you have found include, but are not limited to: + +@itemize @bullet +@item +view or edit +@item +store in an archive +@item +remove or rename +@item +change access permissions +@item +classify into groups +@end itemize + +This manual describes how to perform each of those tasks, and more. + +@node Overview +@section Overview + +The principal programs used for making lists of files that match given +criteria and running commands on them are @code{find}, @code{locate}, +and @code{xargs}. An additional command, @code{updatedb}, is used by +system administrators to create databases for @code{locate} to use. + +@code{find} searches for files in a directory hierarchy and prints +information about the files it found. It is run like this: + +@example +find @r{[}@var{file}@dots{}@r{]} @r{[}@var{expression}@r{]} +@end example + +@noindent +Here is a typical use of @code{find}. This example prints the names of +all files in the directory tree rooted in @file{/usr/src} whose name +ends with @samp{.c} and that are larger than 100 Kilobytes. +@example +find /usr/src -name '*.c' -size +100k -print +@end example + +@code{locate} searches special file name databases for file names that +match patterns. The system administrator runs the @code{updatedb} +program to create the databases. @code{locate} is run like this: + +@example +locate @r{[}@var{option}@dots{}@r{]} @var{pattern}@dots{} +@end example + +@noindent +This example prints the names of all files in the default file name +database whose name ends with @samp{Makefile} or @samp{makefile}. Which +file names are stored in the database depends on how the system +administrator ran @code{updatedb}. +@example +locate '*[Mm]akefile' +@end example + +The name @code{xargs}, pronounced EX-args, means ``combine arguments.'' +@code{xargs} builds and executes command lines by gathering together +arguments it reads on the standard input. Most often, these arguments +are lists of file names generated by @code{find}. @code{xargs} is run +like this: + +@example +xargs @r{[}@var{option}@dots{}@r{]} @r{[}@var{command} @r{[}@var{initial-arguments}@r{]}@r{]} +@end example + +@noindent +The following command searches the files listed in the file +@file{file-list} and prints all of the lines in them that contain the +word @samp{typedef}. +@example +xargs grep typedef < file-list +@end example + +@node find Expressions +@section @code{find} Expressions + +The expression that @code{find} uses to select files consists of one or +more @dfn{primaries}, each of which is a separate command line argument +to @code{find}. @code{find} evaluates the expression each time it +processes a file. An expression can contain any of the following types +of primaries: + +@table @dfn +@item options +affect overall operation rather than the processing of a specific file; +@item tests +return a true or false value, depending on the file's attributes; +@item actions +have side effects and return a true or false value; and +@item operators +connect the other arguments and affect when and whether they are +evaluated. +@end table + +You can omit the operator between two primaries; it defaults to +@samp{-and}. @xref{Combining Primaries With Operators}, for ways to +connect primaries into more complex expressions. If the expression +contains no actions other than @samp{-prune}, @samp{-print} is performed +on all files for which the entire expression is true (@pxref{Print File +Name}). + +Options take effect immediately, rather than being evaluated for each +file when their place in the expression is reached. Therefore, for +clarity, it is best to place them at the beginning of the expression. + +Many of the primaries take arguments, which immediately follow them in +the next command line argument to @code{find}. Some arguments are file +names, patterns, or other strings; others are numbers. Numeric +arguments can be specified as + +@table @code +@item +@var{n} +for greater than @var{n}, +@item -@var{n} +for less than @var{n}, +@item @var{n} +for exactly @var{n}. +@end table + +@node Finding Files, Actions, Introduction, Top +@chapter Finding Files + +By default, @code{find} prints to the standard output the names of the +files that match the given criteria. @xref{Actions}, for how to get more +information about the matching files. + +@menu +* Name:: +* Links:: +* Time:: +* Size:: +* Type:: +* Owner:: +* Permissions:: +* Contents:: +* Directories:: +* Filesystems:: +* Combining Primaries With Operators:: +@end menu + +@node Name +@section Name + +Here are ways to search for files whose name matches a certain pattern. +@xref{Shell Pattern Matching}, for a description of the @var{pattern} +arguments to these tests. + +Each of these tests has a case-sensitive version and a case-insensitive +version, whose name begins with @samp{i}. In a case-insensitive +comparison, the patterns @samp{fo*} and @samp{F??} match the file names +@file{Foo}, @samp{FOO}, @samp{foo}, @samp{fOo}, etc. + +@menu +* Base Name Patterns:: +* Full Name Patterns:: +* Fast Full Name Search:: +* Shell Pattern Matching:: Wildcards used by these programs. +@end menu + +@node Base Name Patterns +@subsection Base Name Patterns + +@deffn Test -name pattern +@deffnx Test -iname pattern +True if the base of the file name (the path with the leading directories +removed) matches shell pattern @var{pattern}. For @samp{-iname}, the +match is case-insensitive. To ignore a whole directory tree, use +@samp{-prune} (@pxref{Directories}). As an example, to find Texinfo +source files in @file{/usr/local/doc}: + +@example +find /usr/local/doc -name '*.texi' +@end example +@end deffn + +@node Full Name Patterns +@subsection Full Name Patterns + +@deffn Test -path pattern +@deffnx Test -ipath pattern +True if the entire file name, starting with the command line argument +under which the file was found, matches shell pattern @var{pattern}. +For @samp{-ipath}, the match is case-insensitive. To ignore a whole +directory tree, use @samp{-prune} rather than checking every file in the +tree (@pxref{Directories}). +@end deffn + +@deffn Test -regex expr +@deffnx Test -iregex expr +True if the entire file name matches regular expression @var{expr}. +This is a match on the whole path, not a search. For example, to match +a file named @file{./fubar3}, you can use the regular expression +@samp{.*bar.} or @samp{.*b.*3}, but not @samp{b.*r3}. @xref{Regexps, , +Syntax of Regular Expressions, emacs, The GNU Emacs Manual}, for a +description of the syntax of regular expressions. For @samp{-iregex}, +the match is case-insensitive. +@end deffn + +@node Fast Full Name Search +@subsection Fast Full Name Search + +To search for files by name without having to actually scan the +directories on the disk (which can be slow), you can use the +@code{locate} program. For each shell pattern you give it, +@code{locate} searches one or more databases of file names and displays +the file names that contain the pattern. @xref{Shell Pattern Matching}, +for details about shell patterns. + +If a pattern is a plain string---it contains no +metacharacters---@code{locate} displays all file names in the database +that contain that string. If a pattern contains +metacharacters, @code{locate} only displays file names that match the +pattern exactly. As a result, patterns that contain metacharacters +should usually begin with a @samp{*}, and will most often end with one +as well. The exceptions are patterns that are intended to explicitly +match the beginning or end of a file name. + +The command +@example +locate @var{pattern} +@end example + +is almost equivalent to +@example +find @var{directories} -name @var{pattern} +@end example + +where @var{directories} are the directories for which the file name +databases contain information. The differences are that the +@code{locate} information might be out of date, and that @code{locate} +handles wildcards in the pattern slightly differently than @code{find} +(@pxref{Shell Pattern Matching}). + +The file name databases contain lists of files that were on the system +when the databases were last updated. The system administrator can +choose the file name of the default database, the frequency with which +the databases are updated, and the directories for which they contain +entries. + +Here is how to select which file name databases @code{locate} searches. +The default is system-dependent. + +@table @code +@item --database=@var{path} +@itemx -d @var{path} +Instead of searching the default file name database, search the file +name databases in @var{path}, which is a colon-separated list of +database file names. You can also use the environment variable +@code{LOCATE_PATH} to set the list of database files to search. The +option overrides the environment variable if both are used. +@end table + +@node Shell Pattern Matching +@subsection Shell Pattern Matching + +@code{find} and @code{locate} can compare file names, or parts of file +names, to shell patterns. A @dfn{shell pattern} is a string that may +contain the following special characters, which are known as +@dfn{wildcards} or @dfn{metacharacters}. + +You must quote patterns that contain metacharacters to prevent the shell +from expanding them itself. Double and single quotes both work; so does +escaping with a backslash. + +@table @code +@item * +Matches any zero or more characters. + +@item ? +Matches any one character. + +@item [@var{string}] +Matches exactly one character that is a member of the string +@var{string}. This is called a @dfn{character class}. As a shorthand, +@var{string} may contain ranges, which consist of two characters with a +dash between them. For example, the class @samp{[a-z0-9_]} matches a +lowercase letter, a number, or an underscore. You can negate a class by +placing a @samp{!} or @samp{^} immediately after the opening bracket. +Thus, @samp{[^A-Z@@]} matches any character except an uppercase letter +or an at sign. + +@item \ +Removes the special meaning of the character that follows it. This +works even in character classes. +@end table + +In the @code{find} tests that do shell pattern matching (@samp{-name}, +@samp{-path}, etc.), wildcards in the pattern do not match a @samp{.} +at the beginning of a file name. This is not the case for +@code{locate}. Thus, @samp{find -name '*macs'} does not match a file +named @file{.emacs}, but @samp{locate '*macs'} does. + +Slash characters have no special significance in the shell pattern +matching that @code{find} and @code{locate} do, unlike in the shell, in +which wildcards do not match them. Therefore, a pattern @samp{foo*bar} +can match a file name @samp{foo3/bar}, and a pattern @samp{./sr*sc} can +match a file name @samp{./src/misc}. + +@node Links +@section Links + +There are two ways that files can be linked together. @dfn{Symbolic +links} are a special type of file whose contents are a portion of the +name of another file. @dfn{Hard links} are multiple directory entries +for one file; the file names all have the same index node (@dfn{inode}) +number on the disk. + +@menu +* Symbolic Links:: +* Hard Links:: +@end menu + +@node Symbolic Links +@subsection Symbolic Links + +@deffn Test -lname pattern +@deffnx Test -ilname pattern +True if the file is a symbolic link whose contents match shell pattern +@var{pattern}. For @samp{-ilname}, the match is case-insensitive. +@xref{Shell Pattern Matching}, for details about the @var{pattern} +argument. So, to list any symbolic links to @file{sysdep.c} in the +current directory and its subdirectories, you can do: + +@example +find . -lname '*sysdep.c' +@end example +@end deffn + +@deffn Option -follow +Dereference symbolic links. The following differences in behavior occur +when this option is given: + +@itemize @bullet +@item +@code{find} follows symbolic links to directories when searching +directory trees. +@item +@samp{-lname} and @samp{-ilname} always return false. +@item +@samp{-type} reports the types of the files that symbolic links point +to. +@item +Implies @samp{-noleaf} (@pxref{Directories}). +@end itemize +@end deffn + +@node Hard Links +@subsection Hard Links + +To find hard links, first get the inode number of the file whose links +you want to find. You can learn a file's inode number and the number of +links to it by running @samp{ls -i} or @samp{find -ls}. If the file has +more than one link, you can search for the other links by passing that +inode number to @samp{-inum}. Add the @samp{-xdev} option if you are +starting the search at a directory that has other filesystems mounted on +it, such as @file{/usr} on many systems. Doing this saves needless +searching, since hard links to a file must be on the same filesystem. +@xref{Filesystems}. + +@deffn Test -inum n +File has inode number @var{n}. +@end deffn + +You can also search for files that have a certain number of links, with +@samp{-links}. Directories normally have at least two hard links; their +@file{.} entry is the second one. If they have subdirectories, each of +those also has a hard link called @file{..} to its parent directory. + +@deffn Test -links n +File has @var{n} hard links. +@end deffn + +@node Time +@section Time + +Each file has three time stamps, which record the last time that certain +operations were performed on the file: + +@enumerate +@item +access (read the file's contents) +@item +change the status (modify the file or its attributes) +@item +modify (change the file's contents) +@end enumerate + +You can search for files whose time stamps are within a certain age +range, or compare them to other time stamps. + +@menu +* Age Ranges:: +* Comparing Timestamps:: +@end menu + +@node Age Ranges +@subsection Age Ranges + +These tests are mainly useful with ranges (@samp{+@var{n}} and +@samp{-@var{n}}). + +@deffn Test -atime n +@deffnx Test -ctime n +@deffnx Test -mtime n +True if the file was last accessed (or its status changed, or it was +modified) @var{n}*24 hours ago. +@end deffn + +@deffn Test -amin n +@deffnx Test -cmin n +@deffnx Test -mmin n +True if the file was last accessed (or its status changed, or it was +modified) @var{n} minutes ago. These tests provide finer granularity of +measurement than @samp{-atime} et al. For example, to list files in +@file{/u/bill} that were last read from 2 to 6 hours ago: + +@example +find /u/bill -amin +2 -amin -6 +@end example +@end deffn + +@deffn Option -daystart +Measure times from the beginning of today rather than from 24 hours ago. +So, to list the regular files in your home directory that were modified +yesterday, do + +@example +find ~ -daystart -type f -mtime 1 +@end example +@end deffn + +@node Comparing Timestamps +@subsection Comparing Timestamps + +As an alternative to comparing timestamps to the current time, you can +compare them to another file's timestamp. That file's timestamp could +be updated by another program when some event occurs. Or you could set +it to a particular fixed date using the @code{touch} command. For +example, to list files in @file{/usr} modified after February 1 of the +current year: + +@c Idea from Rick Sladkey. +@example +touch -t 02010000 /tmp/stamp$$ +find /usr -newer /tmp/stamp$$ +rm -f /tmp/stamp$$ +@end example + +@deffn Test -anewer file +@deffnx Test -cnewer file +@deffnx Test -newer file +True if the file was last accessed (or its status changed, or it was +modified) more recently than @var{file} was modified. These tests are +affected by @samp{-follow} only if @samp{-follow} comes before them on +the command line. @xref{Symbolic Links}, for more information on +@samp{-follow}. As an example, to list any files modified since +@file{/bin/sh} was last modified: + +@example +find . -newer /bin/sh +@end example +@end deffn + +@deffn Test -used n +True if the file was last accessed @var{n} days after its status was +last changed. Useful for finding files that are not being used, and +could perhaps be archived or removed to save disk space. +@end deffn + +@node Size +@section Size + +@deffn Test -size n@r{[}bckw@r{]} +True if the file uses @var{n} units of space, rounding up. The units +are 512-byte blocks by default, but they can be changed by adding a +one-character suffix to @var{n}: + +@table @code +@item b +512-byte blocks +@item c +bytes +@item k +kilobytes (1024 bytes) +@item w +2-byte words +@end table + +The size does not count indirect blocks, but it does count blocks in +sparse files that are not actually allocated. +@end deffn + +@deffn Test -empty +True if the file is empty and is either a regular file or a directory. +This might make it a good candidate for deletion. This test is useful +with @samp{-depth} (@pxref{Directories}) and @samp{-exec rm -rf '@{@}' ';'} +(@pxref{Single File}). +@end deffn + +@node Type +@section Type + +@deffn Test -type c +True if the file is of type @var{c}: + +@table @code +@item b +block (buffered) special +@item c +character (unbuffered) special +@item d +directory +@item p +named pipe (FIFO) +@item f +regular file +@item l +symbolic link +@item s +socket +@end table +@end deffn + +@deffn Test -xtype c +The same as @samp{-type} unless the file is a symbolic link. For +symbolic links: if @samp{-follow} has not been given, true if the file +is a link to a file of type @var{c}; if @samp{-follow} has been given, +true if @var{c} is @samp{l}. In other words, for symbolic links, +@samp{-xtype} checks the type of the file that @samp{-type} does not +check. @xref{Symbolic Links}, for more information on @samp{-follow}. +@end deffn + +@node Owner +@section Owner + +@deffn Test -user uname +@deffnx Test -group gname +True if the file is owned by user @var{uname} (belongs to group @var{gname}). +A numeric ID is allowed. +@end deffn + +@deffn Test -uid n +@deffnx Test -gid n +True if the file's numeric user ID (group ID) is @var{n}. These tests +support ranges (@samp{+@var{n}} and @samp{-@var{n}}), unlike +@samp{-user} and @samp{-group}. +@end deffn + +@deffn Test -nouser +@deffnx Test -nogroup +True if no user corresponds to the file's numeric user ID (no group +corresponds to the numeric group ID). These cases usually mean that the +files belonged to users who have since been removed from the system. +You probably should change the ownership of such files to an existing +user or group, using the @code{chown} or @code{chgrp} program. +@end deffn + +@node Permissions +@section Permissions + +@xref{File Permissions}, for information on how file permissions are +structured and how to specify them. + +@deffn Test -perm mode +True if the +file's permissions are exactly @var{mode} (which can be numeric or symbolic). +Symbolic modes use mode 0 as a point of departure. +If @var{mode} starts with @samp{-}, true if +@emph{all} of the permissions set in @var{mode} are set for the file; +permissions not set in @var{mode} are ignored. +If @var{mode} starts with @samp{+}, true if +@emph{any} of the permissions set in @var{mode} are set for the file; +permissions not set in @var{mode} are ignored. +@end deffn + +@node Contents +@section Contents + +To search for files based on their contents, you can use the @code{grep} +program. For example, to find out which C source files in the current +directory contain the string @samp{thing}, you can do: + +@example +grep -l thing *.[ch] +@end example + +If you also want to search for the string in files in subdirectories, +you can combine @code{grep} with @code{find} and @code{xargs}, like +this: + +@example +find . -name '*.[ch]' | xargs grep -l thing +@end example + +The @samp{-l} option causes @code{grep} to print only the names of files +that contain the string, rather than the lines that contain it. The +string argument (@samp{thing}) is actually a regular expression, so it +can contain metacharacters. This method can be refined a little by +using the @samp{-r} option to make @code{xargs} not run @code{grep} if +@code{find} produces no output, and using the @code{find} action +@samp{-print0} and the @code{xargs} option @samp{-0} to avoid +misinterpreting files whose names contain spaces: + +@example +find . -name '*.[ch]' -print0 | xargs -r -0 grep -l thing +@end example + +For a fuller treatment of finding files whose contents match a pattern, +see the manual page for @code{grep}. + +@node Directories +@section Directories + +Here is how to control which directories @code{find} searches, and how +it searches them. These two options allow you to process a horizontal +slice of a directory tree. + +@deffn Option -maxdepth levels +Descend at most @var{levels} (a non-negative integer) levels of +directories below the command line arguments. @samp{-maxdepth 0} means +only apply the tests and actions to the command line arguments. +@end deffn + +@deffn Option -mindepth levels +Do not apply any tests or actions at levels less than @var{levels} (a +non-negative integer). @samp{-mindepth 1} means process all files +except the command line arguments. +@end deffn + +@deffn Option -depth +Process each directory's contents before the directory itself. Doing +this is a good idea when producing lists of files to archive with +@code{cpio} or @code{tar}. If a directory does not have write +permission for its owner, its contents can still be restored from the +archive since the directory's permissions are restored after its contents. +@end deffn + +@deffn Action -prune +If @samp{-depth} is not given, true; do not descend the current +directory. If @samp{-depth} is given, false; no effect. @samp{-prune} +only affects tests and actions that come after it in the expression, not +those that come before. + +For example, to skip the directory @file{src/emacs} and all files and +directories under it, and print the names of the other files found: + +@example +find . -path './src/emacs' -prune -o -print +@end example +@end deffn + +@deffn Option -noleaf +Do not optimize by assuming that directories contain 2 fewer +subdirectories than their hard link count. This option is needed when +searching filesystems that do not follow the Unix directory-link +convention, such as CD-ROM or MS-DOS filesystems or AFS volume mount +points. Each directory on a normal Unix filesystem has at least 2 hard +links: its name and its @file{.} entry. Additionally, its +subdirectories (if any) each have a @file{..} entry linked to that +directory. When @code{find} is examining a directory, after it has +statted 2 fewer subdirectories than the directory's link count, it knows +that the rest of the entries in the directory are non-directories +(@dfn{leaf} files in the directory tree). If only the files' names need +to be examined, there is no need to stat them; this gives a significant +increase in search speed. +@end deffn + +@node Filesystems +@section Filesystems + +A @dfn{filesystem} is a section of a disk, either on the local host or +mounted from a remote host over a network. Searching network +filesystems can be slow, so it is common to make @code{find} avoid them. + +There are two ways to avoid searching certain filesystems. One way is +to tell @code{find} to only search one filesystem: + +@deffn Option -xdev +@deffnx Option -mount +Don't descend directories on other filesystems. These options are synonyms. +@end deffn + +The other way is to check the type of filesystem each file is on, and +not descend directories that are on undesirable filesystem types: + +@deffn Test -fstype type +True if the file is on a filesystem of type @var{type}. The valid +filesystem types vary among different versions of Unix; an incomplete +list of filesystem types that are accepted on some version of Unix or +another is: +@example +ufs 4.2 4.3 nfs tmp mfs S51K S52K +@end example +You can use @samp{-printf} with the @samp{%F} directive to see the types +of your filesystems. @xref{Print File Information}. @samp{-fstype} is +usually used with @samp{-prune} to avoid searching remote filesystems +(@pxref{Directories}). +@end deffn + +@node Combining Primaries With Operators +@section Combining Primaries With Operators + +Operators build a complex expression from tests and actions. +The operators are, in order of decreasing precedence: + +@table @code +@item @asis{( @var{expr} )} +Force precedence. True if @var{expr} is true. + +@item @asis{! @var{expr}} +@itemx @asis{-not @var{expr}} +True if @var{expr} is false. + +@item @asis{@var{expr1 expr2}} +@itemx @asis{@var{expr1} -a @var{expr2}} +@itemx @asis{@var{expr1} -and @var{expr2}} +And; @var{expr2} is not evaluated if @var{expr1} is false. + +@item @asis{@var{expr1} -o @var{expr2}} +@itemx @asis{@var{expr1} -or @var{expr2}} +Or; @var{expr2} is not evaluated if @var{expr1} is true. + +@item @asis{@var{expr1} , @var{expr2}} +List; both @var{expr1} and @var{expr2} are always evaluated. True if +@var{expr2} is true. The value of @var{expr1} is discarded. This +operator lets you do multiple independent operations on one traversal, +without depending on whether other operations succeeded. +@end table + +@code{find} searches the directory tree rooted at each file name by +evaluating the expression from left to right, according to the rules of +precedence, until the outcome is known (the left hand side is false for +@samp{-and}, true for @samp{-or}), at which point @code{find} moves on +to the next file name. + +There are two other tests that can be useful in complex expressions: + +@deffn Test -true +Always true. +@end deffn + +@deffn Test -false +Always false. +@end deffn + +@node Actions, Common Tasks, Finding Files, Top +@chapter Actions + +There are several ways you can print information about the files that +match the criteria you gave in the @code{find} expression. You can +print the information either to the standard output or to a file that +you name. You can also execute commands that have the file names as +arguments. You can use those commands as further filters to select files. + +@menu +* Print File Name:: +* Print File Information:: +* Run Commands:: +* Adding Tests:: +@end menu + +@node Print File Name +@section Print File Name + +@deffn Action -print +True; print the full file name on the standard output, followed by a +newline. +@end deffn + +@deffn Action -fprint file +True; print the full file name into file @var{file}, followed by a +newline. If @var{file} does not exist when @code{find} is run, it is +created; if it does exist, it is truncated to 0 bytes. The file names +@file{/dev/stdout} and @file{/dev/stderr} are handled specially; they +refer to the standard output and standard error output, respectively. +@end deffn + +@node Print File Information +@section Print File Information + +@deffn Action -ls +True; list the current file in @samp{ls -dils} format on the standard +output. The output looks like this: + +@smallexample +204744 17 -rw-r--r-- 1 djm staff 17337 Nov 2 1992 ./lwall-quotes +@end smallexample + +The fields are: + +@enumerate +@item +The inode number of the file. @xref{Hard Links}, for how to find files +based on their inode number. + +@item +the number of blocks in the file. The block counts are of 1K blocks, +unless the environment variable @code{POSIXLY_CORRECT} is set, in which +case 512-byte blocks are used. @xref{Size}, for how to find files based +on their size. + +@item +The file's type and permissions. The type is shown as a dash for a +regular file; for other file types, a letter like for @samp{-type} is +used (@pxref{Type}). The permissions are read, write, and execute for +the file's owner, its group, and other users, respectively; a dash means +the permission is not granted. @xref{File Permissions}, for more details +about file permissions. @xref{Permissions}, for how to find files based +on their permissions. + +@item +The number of hard links to the file. + +@item +The user who owns the file. + +@item +The file's group. + +@item +The file's size in bytes. + +@item +The date the file was last modified. + +@item +The file's name. @samp{-ls} quotes non-printable characters in the file +names using C-like backslash escapes. +@end enumerate +@end deffn + +@deffn Action -fls file +True; like @samp{-ls} but write to @var{file} like @samp{-fprint} +(@pxref{Print File Name}). +@end deffn + +@deffn Action -printf format +True; print @var{format} on the standard output, interpreting @samp{\} +escapes and @samp{%} directives. Field widths and precisions can be +specified as with the @code{printf} C function. Unlike @samp{-print}, +@samp{-printf} does not add a newline at the end of the string. +@end deffn + +@deffn Action -fprintf file format +True; like @samp{-printf} but write to @var{file} like @samp{-fprint} +(@pxref{Print File Name}). +@end deffn + +@menu +* Escapes:: +* Format Directives:: +* Time Formats:: +@end menu + +@node Escapes +@subsection Escapes + +The escapes that @samp{-printf} and @samp{-fprintf} recognize are: + +@table @code +@item \a +Alarm bell. +@item \b +Backspace. +@item \c +Stop printing from this format immediately and flush the output. +@item \f +Form feed. +@item \n +Newline. +@item \r +Carriage return. +@item \t +Horizontal tab. +@item \v +Vertical tab. +@item \\ +A literal backslash (@samp{\}). +@end table + +A @samp{\} character followed by any other character is treated as an +ordinary character, so they both are printed, and a warning message is +printed to the standard error output (because it was probably a typo). + +@node Format Directives +@subsection Format Directives + +@samp{-printf} and @samp{-fprintf} support the following format +directives to print information about the file being processed. Unlike +the C @code{printf} function, they do not support field width specifiers. + +@samp{%%} is a literal percent sign. A @samp{%} character followed by +any other character is discarded (but the other character is printed), +and a warning message is printed to the standard error output (because +it was probably a typo). + +@menu +* Name Directives:: +* Ownership Directives:: +* Size Directives:: +* Location Directives:: +* Time Directives:: +@end menu + +@node Name Directives +@subsubsection Name Directives + +@table @code +@item %p +File's name. +@item %f +File's name with any leading directories removed (only the last element). +@item %h +Leading directories of file's name (all but the last element and the +slash before it). +@item %P +File's name with the name of the command line argument under which +it was found removed from the beginning. +@item %H +Command line argument under which file was found. +@end table + +@node Ownership Directives +@subsubsection Ownership Directives + +@table @code +@item %g +File's group name, or numeric group ID if the group has no name. +@item %G +File's numeric group ID. +@item %u +File's user name, or numeric user ID if the user has no name. +@item %U +File's numeric user ID. +@item %m +File's permissions (in octal). +@end table + +@node Size Directives +@subsubsection Size Directives + +@table @code +@item %k +File's size in 1K blocks (rounded up). +@item %b +File's size in 512-byte blocks (rounded up). +@item %s +File's size in bytes. +@end table + +@node Location Directives +@subsubsection Location Directives + +@table @code +@item %d +File's depth in the directory tree; files named on the command line +have a depth of 0. +@item %F +Type of the filesystem the file is on; this value can be used for +@samp{-fstype} (@pxref{Directories}). +@item %l +Object of symbolic link (empty string if file is not a symbolic link). +@item %i +File's inode number (in decimal). +@item %n +Number of hard links to file. +@end table + +@node Time Directives +@subsubsection Time Directives + +Some of these directives use the C @code{ctime} function. Its output +depends on the current locale, but it typically looks like + +@example +Wed Nov 2 00:42:36 1994 +@end example + +@table @code +@item %a +File's last access time in the format returned by the C @code{ctime} function. +@item %A@var{k} +File's last access time in the format specified by @var{k} +(@pxref{Time Formats}). +@item %c +File's last status change time in the format returned by the C @code{ctime} +function. +@item %C@var{k} +File's last status change time in the format specified by @var{k} +(@pxref{Time Formats}). +@item %t +File's last modification time in the format returned by the C @code{ctime} +function. +@item %T@var{k} +File's last modification time in the format specified by @var{k} +(@pxref{Time Formats}). +@end table + +@node Time Formats +@subsection Time Formats + +Below are the formats for the directives @samp{%A}, @samp{%C}, and +@samp{%T}, which print the file's timestamps. Some of these formats +might not be available on all systems, due to differences in the C +@code{strftime} function between systems. + +@menu +* Time Components:: +* Date Components:: +* Combined Time Formats:: +@end menu + +@node Time Components +@subsubsection Time Components + +The following format directives print single components of the time. + +@table @code +@item H +hour (00..23) +@item I +hour (01..12) +@item k +hour ( 0..23) +@item l +hour ( 1..12) +@item p +locale's AM or PM +@item Z +time zone (e.g., EDT), or nothing if no time zone is determinable +@item M +minute (00..59) +@item S +second (00..61) +@item @@ +seconds since Jan. 1, 1970, 00:00 GMT. +@end table + +@node Date Components +@subsubsection Date Components + +The following format directives print single components of the date. + +@table @code +@item a +locale's abbreviated weekday name (Sun..Sat) +@item A +locale's full weekday name, variable length (Sunday..Saturday) +@item b +@itemx h +locale's abbreviated month name (Jan..Dec) +@item B +locale's full month name, variable length (January..December) +@item m +month (01..12) +@item d +day of month (01..31) +@item w +day of week (0..6) +@item j +day of year (001..366) +@item U +week number of year with Sunday as first day of week (00..53) +@item W +week number of year with Monday as first day of week (00..53) +@item Y +year (1970@dots{}) +@item y +last two digits of year (00..99) +@end table + +@node Combined Time Formats +@subsubsection Combined Time Formats + +The following format directives print combinations of time and date +components. + +@table @code +@item r +time, 12-hour (hh:mm:ss [AP]M) +@item T +time, 24-hour (hh:mm:ss) +@item X +locale's time representation (H:M:S) +@item c +locale's date and time (Sat Nov 04 12:02:33 EST 1989) +@item D +date (mm/dd/yy) +@item x +locale's date representation (mm/dd/yy) +@end table + +@node Run Commands +@section Run Commands + +You can use the list of file names created by @code{find} or +@code{locate} as arguments to other commands. In this way you can +perform arbitrary actions on the files. + +@menu +* Single File:: +* Multiple Files:: +* Querying:: +@end menu + +@node Single File +@subsection Single File + +Here is how to run a command on one file at a time. + +@deffn Action -exec command ; +Execute @var{command}; true if 0 status is returned. @code{find} takes +all arguments after @samp{-exec} to be part of the command until an +argument consisting of @samp{;} is reached. It replaces the string +@samp{@{@}} by the current file name being processed everywhere it +occurs in the command. Both of these constructions need to be escaped +(with a @samp{\}) or quoted to protect them from expansion by the shell. +The command is executed in the directory in which @code{find} was run. + +For example, to compare each C header file in the current directory with +the file @file{/tmp/master}: + +@example +find . -name '*.h' -exec diff -u '@{@}' /tmp/master ';' +@end example +@end deffn + +@node Multiple Files +@subsection Multiple Files + +Sometimes you need to process files alone. But when you +don't, it is faster to run a command on as many files as possible at a +time, rather than once per file. Doing this saves on the time it takes +to start up the command each time. + +To run a command on more than one file at once, use the @code{xargs} +command, which is invoked like this: + +@example +xargs @r{[}@var{option}@dots{}@r{]} @r{[}@var{command} @r{[}@var{initial-arguments}@r{]}@r{]} +@end example + +@code{xargs} reads arguments from the standard input, delimited by +blanks (which can be protected with double or single quotes or a +backslash) or newlines. It executes the @var{command} (default is +@file{/bin/echo}) one or more times with any @var{initial-arguments} +followed by arguments read from standard input. Blank lines on the +standard input are ignored. + +Instead of blank-delimited names, it is safer to use @samp{find -print0} +or @samp{find -fprint0} and process the output by giving the @samp{-0} +or @samp{--null} option to GNU @code{xargs}, GNU @code{tar}, GNU +@code{cpio}, or @code{perl}. + +You can use shell command substitution (backquotes) to process a list of +arguments, like this: + +@example +grep -l sprintf `find $HOME -name '*.c' -print` +@end example + +However, that method produces an error if the length of the @samp{.c} +file names exceeds the operating system's command-line length limit. +@code{xargs} avoids that problem by running the command as many times as +necessary without exceeding the limit: + +@example +find $HOME -name '*.c' -print | grep -l sprintf +@end example + +However, if the command needs to have its standard input be a terminal +(@code{less}, for example), you have to use the shell command +substitution method. + +@menu +* Unsafe File Name Handling:: +* Safe File Name Handling:: +* Limiting Command Size:: +* Interspersing File Names:: +@end menu + +@node Unsafe File Name Handling +@subsubsection Unsafe File Name Handling + +Because file names can contain quotes, backslashes, blank characters, +and even newlines, it is not safe to process them using @code{xargs} in its +default mode of operation. But since most files' names do not contain +blanks, this problem occurs only infrequently. If you are only +searching through files that you know have safe names, then you need not +be concerned about it. + +@c This example is adapted from: +@c From: pfalstad@stone.Princeton.EDU (Paul John Falstad) +@c Newsgroups: comp.unix.shell +@c Subject: Re: Beware xargs security holes +@c Date: 16 Oct 90 19:12:06 GMT +@c +In many applications, if @code{xargs} botches processing a file because +its name contains special characters, some data might be lost. The +importance of this problem depends on the importance of the data and +whether anyone notices the loss soon enough to correct it. However, +here is an extreme example of the problems that using blank-delimited +names can cause. If the following command is run daily from +@code{cron}, then any user can remove any file on the system: + +@example +find / -name '#*' -atime +7 -print | xargs rm +@end example + +For example, you could do something like this: + +@example +eg$ echo > '# +vmunix' +@end example + +@noindent +and then @code{cron} would delete @file{/vmunix}, if it ran +@code{xargs} with @file{/} as its current directory. + +To delete other files, for example @file{/u/joeuser/.plan}, you could do +this: + +@example +eg$ mkdir '# +' +eg$ cd '# +' +eg$ mkdir u u/joeuser u/joeuser/.plan' +' +eg$ echo > u/joeuser/.plan' +/#foo' +eg$ cd .. +eg$ find . -name '#*' -print | xargs echo +./# ./# /u/joeuser/.plan /#foo +@end example + +@node Safe File Name Handling +@subsubsection Safe File Name Handling + +Here is how to make @code{find} output file names so that they can be +used by other programs without being mangled or misinterpreted. You can +process file names generated this way by giving the @samp{-0} or +@samp{--null} option to GNU @code{xargs}, GNU @code{tar}, GNU +@code{cpio}, or @code{perl}. + +@deffn Action -print0 +True; print the full file name on the standard output, followed by a +null character. +@end deffn + +@deffn Action -fprint0 file +True; like @samp{-print0} but write to @var{file} like @samp{-fprint} +(@pxref{Print File Name}). +@end deffn + +@node Limiting Command Size +@subsubsection Limiting Command Size + +@code{xargs} gives you control over how many arguments it passes to the +command each time it executes it. By default, it uses up to +@code{ARG_MAX} - 2k, or 20k, whichever is smaller, characters per +command. It uses as many lines and arguments as fit within that limit. +The following options modify those values. + +@table @code +@item --no-run-if-empty +@itemx -r +If the standard input does not contain any nonblanks, do not run the +command. By default, the command is run once even if there is no input. + +@item --max-lines@r{[}=@var{max-lines}@r{]} +@itemx -l@r{[}@var{max-lines}@r{]} +Use at most @var{max-lines} nonblank input lines per command line; +@var{max-lines} defaults to 1 if omitted. Trailing blanks cause an +input line to be logically continued on the next input line, for the +purpose of counting the lines. Implies @samp{-x}. + +@item --max-args=@var{max-args} +@itemx -n @var{max-args} +Use at most @var{max-args} arguments per command line. Fewer than +@var{max-args} arguments will be used if the size (see the @samp{-s} +option) is exceeded, unless the @samp{-x} option is given, in which case +@code{xargs} will exit. + +@item --max-chars=@var{max-chars} +@itemx -s @var{max-chars} +Use at most @var{max-chars} characters per command line, including the +command and initial arguments and the terminating nulls at the ends of +the argument strings. + +@item --max-procs=@var{max-procs} +@itemx -P @var{max-procs} +Run up to @var{max-procs} processes at a time; the default is 1. If +@var{max-procs} is 0, @code{xargs} will run as many processes as +possible at a time. Use the @samp{-n}, @samp{-s}, or @samp{-l} option +with @samp{-P}; otherwise chances are that the command will be run only +once. +@end table + +@node Interspersing File Names +@subsubsection Interspersing File Names + +@code{xargs} can insert the name of the file it is processing between +arguments you give for the command. Unless you also give options to +limit the command size (@pxref{Limiting Command Size}), this mode of +operation is equivalent to @samp{find -exec} (@pxref{Single File}). + +@table @code +@item --replace@r{[}=@var{replace-str}@r{]} +@itemx -i@r{[}@var{replace-str}@r{]} +Replace occurences of @var{replace-str} in the initial arguments with +names read from standard input. Also, unquoted blanks do not terminate +arguments. If @var{replace-str} is omitted, it defaults to @samp{@{@}} +(like for @samp{find -exec}). Implies @samp{-x} and @samp{-l 1}. As an +example, to sort each file the @file{bills} directory, leaving the +output in that file name with @file{.sorted} appended, you could do: + +@example +find bills -type f | xargs -iXX sort -o XX.sorted XX +@end example + +@noindent +The equivalent command using @samp{find -exec} is: + +@example +find bills -type f -exec sort -o '@{@}.sorted' '@{@}' ';' +@end example +@end table + +@node Querying +@subsection Querying + +To ask the user whether to execute a command on a single file, you can +use the @code{find} primary @samp{-ok} instead of @samp{-exec}: + +@deffn Action -ok command ; +Like @samp{-exec} (@pxref{Single File}), but ask the user first (on +the standard input); if the response does not start with @samp{y} or +@samp{Y}, do not run the command, and return false. +@end deffn + +When processing multiple files with a single command, to query the user +you give @code{xargs} the following option. When using this option, you +might find it useful to control the number of files processed per +invocation of the command (@pxref{Limiting Command Size}). + +@table @code +@item --interactive +@itemx -p +Prompt the user about whether to run each command line and read a line +from the terminal. Only run the command line if the response starts +with @samp{y} or @samp{Y}. Implies @samp{-t}. +@end table + +@node Adding Tests +@section Adding Tests + +You can test for file attributes that none of the @code{find} builtin +tests check. To do this, use @code{xargs} to run a program that filters +a list of files printed by @code{find}. If possible, use @code{find} +builtin tests to pare down the list, so the program run by @code{xargs} +has less work to do. The tests builtin to @code{find} will likely run +faster than tests that other programs perform. + +For example, here is a way to print the names of all of the unstripped +binaries in the @file{/usr/local} directory tree. Builtin tests avoid +running @code{file} on files that are not regular files or are not +executable. + +@example +find /usr/local -type f -perm +a=x | xargs file | + grep 'not stripped' | cut -d: -f1 +@end example + +@noindent +The @code{cut} program removes everything after the file name from the +output of @code{file}. + +@c Idea from Martin Weitzel. +If you want to place a special test somewhere in the middle of a +@code{find} expression, you can use @samp{-exec} to run a program that +performs the test. Because @samp{-exec} evaluates to the exit status of +the executed program, you can write a program (which can be a shell +script) that tests for a special attribute and make it exit with a true +(zero) or false (non-zero) status. It is a good idea to place such a +special test @emph{after} the builtin tests, because it starts a new +process which could be avoided if a builtin test evaluates to false. +Use this method only when @code{xargs} is not flexible enough, because +starting one or more new processes to test each file is slower than +using @code{xargs} to start one process that tests many files. + +Here is a shell script called @code{unstripped} that checks whether its +argument is an unstripped binary file: + +@example +#!/bin/sh +file $1 | grep 'not stripped' > /dev/null +@end example + +This script relies on the fact that the shell exits with the status of +the last program it executed, in this case @code{grep}. @code{grep} +exits with a true status if it found any matches, false if not. Here is +an example of using the script (assuming it is in your search path). It +lists the stripped executables in the file @file{sbins} and the +unstripped ones in @file{ubins}. + +@example +find /usr/local -type f -perm +a=x \ + \( -exec unstripped '@{@}' \; -fprint ubins -o -fprint sbins \) +@end example + +@node Common Tasks, Databases, Actions, Top +@chapter Common Tasks + +The sections that follow contain some extended examples that both give a +good idea of the power of these programs, and show you how to solve +common real-world problems. + +@menu +* Viewing And Editing:: +* Archiving:: +* Cleaning Up:: +* Strange File Names:: +* Fixing Permissions:: +* Classifying Files:: +@end menu + +@node Viewing And Editing +@section Viewing And Editing + +To view a list of files that meet certain criteria, simply run your file +viewing program with the file names as arguments. Shells substitute a +command enclosed in backquotes with its output, so the whole command +looks like this: + +@example +less `find /usr/include -name '*.h' | xargs grep -l mode_t` +@end example + +@noindent +You can edit those files by giving an editor name instead of a file +viewing program. + +@node Archiving +@section Archiving + +You can pass a list of files produced by @code{find} to a file archiving +program. GNU @code{tar} and @code{cpio} can both read lists of file +names from the standard input---either delimited by nulls (the safe way) +or by blanks (the lazy, risky default way). To use null-delimited +names, give them the @samp{--null} option. You can store a file archive +in a file, write it on a tape, or send it over a network to extract on +another machine. + +One common use of @code{find} to archive files is to send a list of the +files in a directory tree to @code{cpio}. Use @samp{-depth} so if a +directory does not have write permission for its owner, its contents can +still be restored from the archive since the directory's permissions are +restored after its contents. Here is an example of doing this using +@code{cpio}; you could use a more complex @code{find} expression to +archive only certain files. + +@example +find . -depth -print0 | + cpio --create --null --format=crc --file=/dev/nrst0 +@end example + +You could restore that archive using this command: + +@example +cpio --extract --null --make-dir --unconditional \ + --preserve --file=/dev/nrst0 +@end example + +Here are the commands to do the same things using @code{tar}: + +@example +find . -depth -print0 | + tar --create --null --files-from=- --file=/dev/nrst0 + +tar --extract --null --preserve-perm --same-owner \ + --file=/dev/nrst0 +@end example + +@c Idea from Rick Sladkey. +Here is an example of copying a directory from one machine to another: + +@example +find . -depth -print0 | cpio -0o -Hnewc | + rsh @var{other-machine} "cd `pwd` && cpio -i0dum" +@end example + +@node Cleaning Up +@section Cleaning Up + +@c Idea from Jim Meyering. +This section gives examples of removing unwanted files in various situations. +Here is a command to remove the CVS backup files created when an update +requires a merge: + +@example +find . -name '.#*' -print0 | xargs -0r rm -f +@end example + +@c Idea from Franc,ois Pinard. +You can run this command to clean out your clutter in @file{/tmp}. You +might place it in the file your shell runs when you log out +(@file{.bash_logout}, @file{.logout}, or @file{.zlogout}, depending on +which shell you use). + +@example +find /tmp -user $LOGNAME -type f -print0 | xargs -0 -r rm -f +@end example + +@c Idea from Noah Friedman. +To remove old Emacs backup and auto-save files, you can use a command +like the following. It is especially important in this case to use +null-terminated file names because Emacs packages like the VM mailer +often create temporary file names with spaces in them, like @file{#reply +to David J. MacKenzie<1>#}. + +@example +find ~ \( -name '*~' -o -name '#*#' \) -print0 | + xargs --no-run-if-empty --null rm -vf +@end example + +Removing old files from @file{/tmp} is commonly done from @code{cron}: + +@c Idea from Kaveh Ghazi. +@example +find /tmp /var/tmp -not -type d -mtime +3 -print0 | + xargs --null --no-run-if-empty rm -f + +find /tmp /var/tmp -depth -mindepth 1 -type d -empty -print0 | + xargs --null --no-run-if-empty rmdir +@end example + +The second @code{find} command above uses @samp{-depth} so it cleans out +empty directories depth-first, hoping that the parents become empty and +can be removed too. It uses @samp{-mindepth} to avoid removing +@file{/tmp} itself if it becomes totally empty. + +@node Strange File Names +@section Strange File Names + +@c Idea from: +@c From: tmatimar@isgtec.com (Ted Timar) +@c Newsgroups: comp.unix.questions,comp.unix.shell,comp.answers,news.answers +@c Subject: Unix - Frequently Asked Questions (2/7) [Frequent posting] +@c Subject: How do I remove a file with funny characters in the filename ? +@c Date: Thu Mar 18 17:16:55 EST 1993 +@code{find} can help you remove or rename a file with strange characters +in its name. People are sometimes stymied by files whose names contain +characters such as spaces, tabs, control characters, or characters with +the high bit set. The simplest way to remove such files is: + +@example +rm -i @var{some*pattern*that*matches*the*problem*file} +@end example + +@code{rm} asks you whether to remove each file matching the given +pattern. If you are using an old shell, this approach might not work if +the file name contains a character with the high bit set; the shell may +strip it off. A more reliable way is: + +@example +find . -maxdepth 1 @var{tests} -ok rm '@{@}' \; +@end example + +@noindent +where @var{tests} uniquely identify the file. The @samp{-maxdepth 1} +option prevents @code{find} from wasting time searching for the file in +any subdirectories; if there are no subdirectories, you may omit it. A +good way to uniquely identify the problem file is to figure out its +inode number; use + +@example +ls -i +@end example + +Suppose you have a file whose name contains control characters, and you +have found that its inode number is 12345. This command prompts you for +whether to remove it: + +@example +find . -maxdepth 1 -inum 12345 -ok rm -f '@{@}' \; +@end example + +If you don't want to be asked, perhaps because the file name may contain +a strange character sequence that will mess up your screen when printed, +then use @samp{-exec} instead of @samp{-ok}. + +If you want to rename the file instead, you can use @code{mv} instead of +@code{rm}: + +@example +find . -maxdepth 1 -inum 12345 -ok mv '@{@}' @var{new-file-name} \; +@end example + +@node Fixing Permissions +@section Fixing Permissions + +Suppose you want to make sure that everyone can write to the directories in a +certain directory tree. Here is a way to find directories lacking either +user or group write permission (or both), and fix their permissions: + +@example +find . -type d -not -perm -ug=w | xargs chmod ug+w +@end example + +@noindent +You could also reverse the operations, if you want to make sure that +directories do @emph{not} have world write permission. + +@node Classifying Files +@section Classifying Files + +@c Idea from: +@c From: martin@mwtech.UUCP (Martin Weitzel) +@c Newsgroups: comp.unix.wizards,comp.unix.questions +@c Subject: Advanced usage of 'find' (Re: Unix security automating script) +@c Date: 22 Mar 90 15:05:19 GMT +If you want to classify a set of files into several groups based on +different criteria, you can use the comma operator to perform multiple +independent tests on the files. Here is an example: + +@example +find / -type d \( -perm -o=w -fprint allwrite , \ + -perm -o=x -fprint allexec \) + +echo "Directories that can be written to by everyone:" +cat allwrite +echo "" +echo "Directories with search permissions for everyone:" +cat allexec +@end example + +@code{find} has only to make one scan through the directory tree (which +is one of the most time consuming parts of its work). + +@node Databases, File Permissions, Common Tasks, Top +@chapter File Name Databases + +The file name databases used by @code{locate} contain lists of files +that were in particular directory trees when the databases were last +updated. The file name of the default database is determined when +@code{locate} and @code{updatedb} are configured and installed. The +frequency with which the databases are updated and the directories for +which they contain entries depend on how often @code{updatedb} is run, +and with which arguments. + +@menu +* Database Locations:: +* Database Formats:: +@end menu + +@node Database Locations +@section Database Locations + +There can be multiple file name databases. Users can select which +databases @code{locate} searches using an environment variable or a +command line option. The system administrator can choose the file name +of the default database, the frequency with which the databases are +updated, and the directories for which they contain entries. File name +databases are updated by running the @code{updatedb} program, typically +nightly. + +In networked environments, it often makes sense to build a database at +the root of each filesystem, containing the entries for that filesystem. +@code{updatedb} is then run for each filesystem on the fileserver where +that filesystem is on a local disk, to prevent thrashing the network. +Here are the options to @code{updatedb} to select which directories each +database contains entries for: + +@table @code +@item --localpaths='@var{path}@dots{}' +Non-network directories to put in the database. +Default is @file{/}. + +@item --netpaths='@var{path}@dots{}' +Network (NFS, AFS, RFS, etc.) directories to put in the database. +Default is none. + +@item --prunepaths='@var{path}@dots{}' +Directories to not put in the database, which would otherwise be. +Default is @file{/tmp /usr/tmp /var/tmp /afs}. + +@item --output=@var{dbfile} +The database file to build. +Default is system-dependent, but typically @file{/usr/local/var/locatedb}. + +@item --netuser=@var{user} +The user to search network directories as, using @code{su}. +Default is @code{daemon}. +@end table + +@node Database Formats +@section Database Formats + +The file name databases contain lists of files that were in particular +directory trees when the databases were last updated. The file name +database format changed starting with GNU @code{locate} version 4.0 to +allow machines with diffent byte orderings to share the databases. The +new GNU @code{locate} can read both the old and new database formats. +However, old versions of @code{locate} and @code{find} produce incorrect +results if given a new-format database. + +@menu +* New Database Format:: +* Sample Database:: +* Old Database Format:: +@end menu + +@node New Database Format +@subsection New Database Format + +@code{updatedb} runs a program called @code{frcode} to +@dfn{front-compress} the list of file names, which reduces the database +size by a factor of 4 to 5. Front-compression (also known as +incremental encoding) works as follows. + +The database entries are a sorted list (case-insensitively, for users' +convenience). Since the list is sorted, each entry is likely to share a +prefix (initial string) with the previous entry. Each database entry +begins with an offset-differential count byte, which is the additional +number of characters of prefix of the preceding entry to use beyond the +number that the preceding entry is using of its predecessor. (The +counts can be negative.) Following the count is a null-terminated ASCII +remainder---the part of the name that follows the shared prefix. + +If the offset-differential count is larger than can be stored in a byte +(+/-127), the byte has the value 0x80 and the count follows in a 2-byte +word, with the high byte first (network byte order). + +Every database begins with a dummy entry for a file called +@file{LOCATE02}, which @code{locate} checks for to ensure that the +database file has the correct format; it ignores the entry in doing the +search. + +Databases can not be concatenated together, even if the first (dummy) +entry is trimmed from all but the first database. This is because the +offset-differential count in the first entry of the second and following +databases will be wrong. + +@node Sample Database +@subsection Sample Database + +Sample input to @code{frcode}: +@c with nulls changed to newlines: + +@example +/usr/src +/usr/src/cmd/aardvark.c +/usr/src/cmd/armadillo.c +/usr/tmp/zoo +@end example + +Length of the longest prefix of the preceding entry to share: + +@example +0 /usr/src +8 /cmd/aardvark.c +14 rmadillo.c +5 tmp/zoo +@end example + +Output from @code{frcode}, with trailing nulls changed to newlines +and count bytes made printable: + +@example +0 LOCATE02 +0 /usr/src +8 /cmd/aardvark.c +6 rmadillo.c +-9 tmp/zoo +@end example + +(6 = 14 - 8, and -9 = 5 - 14) + +@node Old Database Format +@subsection Old Database Format + +The old database format is used by Unix @code{locate} and @code{find} +programs and earlier releases of the GNU ones. @code{updatedb} produces +this format if given the @samp{--old-format} option. + +@code{updatedb} runs programs called @code{bigram} and @code{code} to +produce old-format databases. The old format differs from the new one +in the following ways. Instead of each entry starting with an +offset-differential count byte and ending with a null, byte values from +0 through 28 indicate offset-differential counts from -14 through 14. +The byte value indicating that a long offset-differential count follows +is 0x1e (30), not 0x80. The long counts are stored in host byte order, +which is not necessarily network byte order, and host integer word size, +which is usually 4 bytes. They also represent a count 14 less than +their value. The database lines have no termination byte; the start of +the next line is indicated by its first byte having a value <= 30. + +In addition, instead of starting with a dummy entry, the old database +format starts with a 256 byte table containing the 128 most common +bigrams in the file list. A bigram is a pair of adjacent bytes. Bytes +in the database that have the high bit set are indexes (with the high +bit cleared) into the bigram table. The bigram and offset-differential +count coding makes these databases 20-25% smaller than the new format, +but makes them not 8-bit clean. Any byte in a file name that is in the +ranges used for the special codes is replaced in the database by a +question mark, which not coincidentally is the shell wildcard to match a +single character. + +@node File Permissions, Reference, Databases, Top +@chapter File Permissions + +@include perm.texi + +@node Reference, Primary Index, File Permissions, Top +@chapter Reference + +Below are summaries of the command line syntax for the programs +discussed in this manual. + +@menu +* Invoking find:: +* Invoking locate:: +* Invoking updatedb:: +* Invoking xargs:: +@end menu + +@node Invoking find, Invoking locate, , Reference +@section Invoking @code{find} + +@example +find @r{[}@var{file}@dots{}@r{]} @r{[}@var{expression}@r{]} +@end example + +@code{find} searches the directory tree rooted at each file name +@var{file} by evaluating the @var{expression} on each file it finds in +the tree. + +@code{find} considers the first argument that begins with @samp{-}, +@samp{(}, @samp{)}, @samp{,}, or @samp{!} to be the beginning of the +expression; any arguments before it are paths to search, and any +arguments after it are the rest of the expression. If no paths are +given, the current directory is used. If no expression is given, the +expression @samp{-print} is used. + +@code{find} exits with status 0 if all files are processed successfully, +greater than 0 if errors occur. + +@xref{Primary Index}, for a summary of all of the tests, actions, and +options that the expression can contain. + +@code{find} also recognizes two options for administrative use: + +@table @code +@item --help +Print a summary of the command-line argument format and exit. +@item --version +Print the version number of @code{find} and exit. +@end table + +@node Invoking locate, Invoking updatedb, Invoking find, Reference +@section Invoking @code{locate} + +@example +locate @r{[}@var{option}@dots{}@r{]} @var{pattern}@dots{} +@end example + +@table @code +@item --database=@var{path} +@itemx -d @var{path} +Instead of searching the default file name database, search the file +name databases in @var{path}, which is a colon-separated list of +database file names. You can also use the environment variable +@code{LOCATE_PATH} to set the list of database files to search. The +option overrides the environment variable if both are used. + +@item --help +Print a summary of the options to @code{locate} and exit. + +@item --version +Print the version number of @code{locate} and exit. +@end table + +@node Invoking updatedb, Invoking xargs, Invoking locate, Reference +@section Invoking @code{updatedb} + +@example +updatedb @r{[}@var{option}@dots{}@r{]} +@end example + +@table @code +@item --localpaths='@var{path}@dots{}' +Non-network directories to put in the database. +Default is @file{/}. + +@item --netpaths='@var{path}@dots{}' +Network (NFS, AFS, RFS, etc.) directories to put in the database. +Default is none. + +@item --prunepaths='@var{path}@dots{}' +Directories to not put in the database, which would otherwise be. +Default is @file{/tmp /usr/tmp /var/tmp /afs}. + +@item --output=@var{dbfile} +The database file to build. +Default is system-dependent, but typically @file{/usr/local/var/locatedb}. + +@item --netuser=@var{user} +The user to search network directories as, using @code{su}(1). +Default is @code{daemon}. +@end table + +@node Invoking xargs, , Invoking updatedb, Reference +@section Invoking @code{xargs} + +@example +xargs @r{[}@var{option}@dots{}@r{]} @r{[}@var{command} @r{[}@var{initial-arguments}@r{]}@r{]} +@end example + +@code{xargs} exits with the following status: + +@table @asis +@item 0 +if it succeeds +@item 123 +if any invocation of the command exited with status 1-125 +@item 124 +if the command exited with status 255 +@item 125 +if the command is killed by a signal +@item 126 +if the command cannot be run +@item 127 +if the command is not found +@item 1 +if some other error occurred. +@end table + +@table @code +@item --null +@itemx -0 +Input filenames are terminated by a null character instead of by +whitespace, and the quotes and backslash are not special (every +character is taken literally). Disables the end of file string, which +is treated like any other argument. + +@item --eof@r{[}=@var{eof-str}@r{]} +@itemx -e@r{[}@var{eof-str}@r{]} +Set the end of file string to @var{eof-str}. If the end of file string +occurs as a line of input, the rest of the input is ignored. If +@var{eof-str} is omitted, there is no end of file string. If this +option is not given, the end of file string defaults to @samp{_}. + +@item --help +Print a summary of the options to @code{xargs} and exit. + +@item --replace@r{[}=@var{replace-str}@r{]} +@itemx -i@r{[}@var{replace-str}@r{]} +Replace occurences of @var{replace-str} in the initial arguments with +names read from standard input. Also, unquoted blanks do not terminate +arguments. If @var{replace-str} is omitted, it defaults to @samp{@{@}} +(like for @samp{find -exec}). Implies @samp{-x} and @samp{-l 1}. + +@item --max-lines@r{[}=@var{max-lines}@r{]} +@itemx -l@r{[}@var{max-lines}@r{]} +Use at most @var{max-lines} nonblank input lines per command line; +@var{max-lines} defaults to 1 if omitted. Trailing blanks cause an +input line to be logically continued on the next input line, for the +purpose of counting the lines. Implies @samp{-x}. + +@item --max-args=@var{max-args} +@itemx -n @var{max-args} +Use at most @var{max-args} arguments per command line. Fewer than +@var{max-args} arguments will be used if the size (see the @samp{-s} +option) is exceeded, unless the @samp{-x} option is given, in which case +@code{xargs} will exit. + +@item --interactive +@itemx -p +Prompt the user about whether to run each command line and read a line +from the terminal. Only run the command line if the response starts +with @samp{y} or @samp{Y}. Implies @samp{-t}. + +@item --no-run-if-empty +@itemx -r +If the standard input does not contain any nonblanks, do not run the +command. By default, the command is run once even if there is no input. + +@item --max-chars=@var{max-chars} +@itemx -s @var{max-chars} +Use at most @var{max-chars} characters per command line, including the +command and initial arguments and the terminating nulls at the ends of +the argument strings. + +@item --verbose +@itemx -t +Print the command line on the standard error output before executing +it. + +@item --version +Print the version number of @code{xargs} and exit. + +@item --exit +@itemx -x +Exit if the size (see the @var{-s} option) is exceeded. + +@item --max-procs=@var{max-procs} +@itemx -P @var{max-procs} +Run up to @var{max-procs} processes at a time; the default is 1. If +@var{max-procs} is 0, @code{xargs} will run as many processes as +possible at a time. +@end table + +@node Primary Index, , Reference, Top +@unnumbered @code{find} Primary Index + +This is a list of all of the primaries (tests, actions, and options) +that make up @code{find} expressions for selecting files. @xref{find +Expressions}, for more information on expressions. + +@printindex fn + +@contents +@bye diff --git a/doc/perm.texi b/doc/perm.texi new file mode 100644 index 0000000..ee43938 --- /dev/null +++ b/doc/perm.texi @@ -0,0 +1,481 @@ +Each file has a set of @dfn{permissions} that control the kinds of +access that users have to that file. The permissions for a file are +also called its @dfn{access mode}. They can be represented either in +symbolic form or as an octal number. + +@menu +* Mode Structure:: Structure of file permissions. +* Symbolic Modes:: Mnemonic permissions representation. +* Numeric Modes:: Permissions as octal numbers. +@end menu + +@node Mode Structure +@section Structure of File Permissions + +There are three kinds of permissions that a user can have for a file: + +@enumerate +@item +@cindex read permission +permission to read the file. For directories, this means permission to +list the contents of the directory. +@item +@cindex write permission +permission to write to (change) the file. For directories, this means +permission to create and remove files in the directory. +@item +@cindex execute permission +permission to execute the file (run it as a program). For directories, +this means permission to access files in the directory. +@end enumerate + +There are three categories of users who may have different permissions +to perform any of the above operations on a file: + +@enumerate +@item +the file's owner; +@item +other users who are in the file's group; +@item +everyone else. +@end enumerate + +@cindex owner, default +@cindex group owner, default +Files are given an owner and group when they are created. Usually the +owner is the current user and the group is the group of the directory +the file is in, but this varies with the operating system, the +filesystem the file is created on, and the way the file is created. You +can change the owner and group of a file by using the @code{chown} and +@code{chgrp} commands. + +In addition to the three sets of three permissions listed above, a +file's permissions have three special components, which affect only +executable files (programs) and, on some systems, directories: + +@enumerate +@item +@cindex setuid +set the process's effective user ID to that of the file upon execution +(called the @dfn{setuid bit}). No effect on directories. +@item +@cindex setgid +set the process's effective group ID to that of the file upon execution +(called the @dfn{setgid bit}). For directories on some systems, put +files created in the directory into the same group as the directory, no +matter what group the user who creates them is in. +@item +@cindex sticky +@cindex swap space, saving text image in +@cindex text image, saving in swap space +@cindex append-only directories +save the program's text image on the swap device so it will load more +quickly when run (called the @dfn{sticky bit}). For directories on some +systems, prevent users from removing files that they do not own in the +directory; this is called making the directory @dfn{append-only}. +@end enumerate + +@node Symbolic Modes +@section Symbolic Modes + +@cindex symbolic modes +@dfn{Symbolic modes} represent changes to files' permissions as +operations on single-character symbols. They allow you to modify either +all or selected parts of files' permissions, optionally based on +their previous values, and perhaps on the current @code{umask} as well +(@pxref{Umask and Protection}). + +The format of symbolic modes is: + +@example +@r{[}ugoa@dots{}@r{][[}+-=@r{][}rwxXstugo@dots{}@r{]}@dots{}@r{][},@dots{}@r{]} +@end example + +The following sections describe the operators and other details of +symbolic modes. + +@menu +* Setting Permissions:: Basic operations on permissions. +* Copying Permissions:: Copying existing permissions. +* Changing Special Permissions:: Special permissions. +* Conditional Executability:: Conditionally affecting executability. +* Multiple Changes:: Making multiple changes. +* Umask and Protection:: The effect of the umask. +@end menu + +@node Setting Permissions +@subsection Setting Permissions + +The basic symbolic operations on a file's permissions are adding, +removing, and setting the permission that certain users have to read, +write, and execute the file. These operations have the following +format: + +@example +@var{users} @var{operation} @var{permissions} +@end example + +@noindent +The spaces between the three parts above are shown for readability only; +symbolic modes can not contain spaces. + +The @var{users} part tells which users' access to the file is changed. +It consists of one or more of the following letters (or it can be empty; +@pxref{Umask and Protection}, for a description of what happens then). When +more than one of these letters is given, the order that they are in does +not matter. + +@table @code +@item u +@cindex owner of file, permissions for +the user who owns the file; +@item g +@cindex group, permissions for +other users who are in the file's group; +@item o +@cindex other permissions +all other users; +@item a +all users; the same as @samp{ugo}. +@end table + +The @var{operation} part tells how to change the affected users' access +to the file, and is one of the following symbols: + +@table @code +@item + +@cindex adding permissions +to add the @var{permissions} to whatever permissions the @var{users} +already have for the file; +@item - +@cindex removing permissions +@cindex subtracting permissions +to remove the @var{permissions} from whatever permissions the +@var{users} already have for the file; +@item = +@cindex setting permissions +to make the @var{permissions} the only permissions that the @var{users} +have for the file. +@end table + +The @var{permissions} part tells what kind of access to the file should +be changed; it is zero or more of the following letters. As with the +@var{users} part, the order does not matter when more than one letter is +given. Omitting the @var{permissions} part is useful only with the +@samp{=} operation, where it gives the specified @var{users} no access +at all to the file. + +@table @code +@item r +@cindex read permission, symbolic +the permission the @var{users} have to read the file; +@item w +@cindex write permission, symbolic +the permission the @var{users} have to write to the file; +@item x +@cindex execute permission, symbolic +the permission the @var{users} have to execute the file. +@end table + +For example, to give everyone permission to read and write a file, +but not to execute it, use: + +@example +a=rw +@end example + +To remove write permission for from all users other than the file's +owner, use: + +@example +go-w +@end example + +@noindent +The above command does not affect the access that the owner of +the file has to it, nor does it affect whether other users can +read or execute the file. + +To give everyone except a file's owner no permission to do anything with +that file, use the mode below. Other users could still remove the file, +if they have write permission on the directory it is in. + +@example +go= +@end example + +@noindent +Another way to specify the same thing is: + +@example +og-rxw +@end example + +@node Copying Permissions +@subsection Copying Existing Permissions + +@cindex copying existing permissions +@cindex permissions, copying existing +You can base part of a file's permissions on part of its existing +permissions. To do this, instead of using @samp{r}, @samp{w}, or +@samp{x} after the operator, you use the letter @samp{u}, @samp{g}, or +@samp{o}. For example, the mode + +@example +o+g +@end example + +@noindent +@c FIXME describe the ls -l notation for showing permissions. +adds the permissions for users who are in a file's group to the +permissions that other users have for the file. Thus, if the file +started out as mode 664 (@samp{rw-rw-r--}), the above mode would change +it to mode 666 (@samp{rw-rw-rw-}). If the file had started out as mode +741 (@samp{rwxr----x}), the above mode would change it to mode 745 +(@samp{rwxr--r-x}). The @samp{-} and @samp{=} operations work +analogously. + +@node Changing Special Permissions +@subsection Changing Special Permissions + +@cindex changing special permissions +In addition to changing a file's read, write, and execute permissions, +you can change its special permissions. @xref{Mode Structure}, for a +summary of these permissions. + +To change a file's permission to set the user ID on execution, use +@samp{u} in the @var{users} part of the symbolic mode and +@samp{s} in the @var{permissions} part. + +To change a file's permission to set the group ID on execution, use +@samp{g} in the @var{users} part of the symbolic mode and +@samp{s} in the @var{permissions} part. + +To change a file's permission to stay permanently on the swap device, +use @samp{o} in the @var{users} part of the symbolic mode and +@samp{t} in the @var{permissions} part. + +For example, to add set user ID permission to a program, +you can use the mode: + +@example +u+s +@end example + +To remove both set user ID and set group ID permission from +it, you can use the mode: + +@example +ug-s +@end example + +To cause a program to be saved on the swap device, you can use +the mode: + +@example +o+t +@end example + +Remember that the special permissions only affect files that are +executable, plus, on some systems, directories (on which they have +different meanings; @pxref{Mode Structure}). Using @samp{a} +in the @var{users} part of a symbolic mode does not cause the special +permissions to be affected; thus, + +@example +a+s +@end example + +@noindent +has @emph{no effect}. You must use @samp{u}, @samp{g}, and @samp{o} +explicitly to affect the special permissions. Also, the +combinations @samp{u+t}, @samp{g+t}, and @samp{o+s} have no effect. + +The @samp{=} operator is not very useful with special permissions; for +example, the mode: + +@example +o=t +@end example + +@noindent +does cause the file to be saved on the swap device, but it also +removes all read, write, and execute permissions that users not in the +file's group might have had for it. + +@node Conditional Executability +@subsection Conditional Executability + +@cindex conditional executability +There is one more special type of symbolic permission: if you use +@samp{X} instead of @samp{x}, execute permission is affected only if the +file already had execute permission or is a directory. It affects +directories' execute permission even if they did not initially have any +execute permissions set. + +For example, this mode: + +@example +a+X +@end example + +@noindent +gives all users permission to execute files (or search directories) if +anyone could before. + +@node Multiple Changes +@subsection Making Multiple Changes + +@cindex multiple changes to permissions +The format of symbolic modes is actually more complex than described +above (@pxref{Setting Permissions}). It provides two ways to make +multiple changes to files' permissions. + +The first way is to specify multiple @var{operation} and +@var{permissions} parts after a @var{users} part in the symbolic mode. + +For example, the mode: + +@example +og+rX-w +@end example + +@noindent +gives users other than the owner of the file read permission and, if +it is a directory or if someone already had execute permission +to it, gives them execute permission; and it also denies them write +permission to it file. It does not affect the permission that the +owner of the file has for it. The above mode is equivalent to +the two modes: + +@example +og+rX +og-w +@end example + +The second way to make multiple changes is to specify more than one +simple symbolic mode, separated by commas. For example, the mode: + +@example +a+r,go-w +@end example + +@noindent +gives everyone permission to read the file and removes write +permission on it for all users except its owner. Another example: + +@example +u=rwx,g=rx,o= +@end example + +@noindent +sets all of the non-special permissions for the file explicitly. (It +gives users who are not in the file's group no permission at all for +it.) + +The two methods can be combined. The mode: + +@example +a+r,g+x-w +@end example + +@noindent +gives all users permission to read the file, and gives users who are in +the file's group permission to execute it, as well, but not permission +to write to it. The above mode could be written in several different +ways; another is: + +@example +u+r,g+rx,o+r,g-w +@end example + +@node Umask and Protection +@subsection The Umask and Protection + +@cindex umask and modes +@cindex modes and umask +If the @var{users} part of a symbolic mode is omitted, it defaults to +@samp{a} (affect all users), except that any permissions that are +@emph{set} in the system variable @code{umask} are @emph{not affected}. +The value of @code{umask} can be set using the +@code{umask} command. Its default value varies from system to system. + +@cindex giving away permissions +Omitting the @var{users} part of a symbolic mode is generally not useful +with operations other than @samp{+}. It is useful with @samp{+} because +it allows you to use @code{umask} as an easily customizable protection +against giving away more permission to files than you intended to. + +As an example, if @code{umask} has the value 2, which removes write +permission for users who are not in the file's group, then the mode: + +@example ++w +@end example + +@noindent +adds permission to write to the file to its owner and to other users who +are in the file's group, but @emph{not} to other users. In contrast, +the mode: + +@example +a+w +@end example + +@noindent +ignores @code{umask}, and @emph{does} give write permission for +the file to all users. + +@node Numeric Modes +@section Numeric Modes + +@cindex numeric modes +@cindex file permissions, numeric +@cindex octal numbers for file modes +File permissions are stored internally as 16 bit integers. As an +alternative to giving a symbolic mode, you can give an octal (base 8) +number that corresponds to the internal representation of the new mode. +This number is always interpreted in octal; you do not have to add a +leading 0, as you do in C. Mode 0055 is the same as mode 55. + +A numeric mode is usually shorter than the corresponding symbolic +mode, but it is limited in that it can not take into account a file's +previous permissions; it can only set them absolutely. + +The permissions granted to the user, to other users in the file's group, +and to other users not in the file's group are each stored as three +bits, which are represented as one octal digit. The three special +permissions are also each stored as one bit, and they are as a group +represented as another octal digit. Here is how the bits are arranged +in the 16 bit integer, starting with the lowest valued bit: + +@example +Value in Corresponding +Mode Permission + + Other users not in the file's group: + 1 Execute + 2 Write + 4 Read + + Other users in the file's group: + 10 Execute + 20 Write + 40 Read + + The file's owner: + 100 Execute + 200 Write + 400 Read + + Special permissions: +1000 Save text image on swap device +2000 Set group ID on execution +4000 Set user ID on execution +@end example + +For example, numeric mode 4755 corresponds to symbolic mode +@samp{u=rwxs,go=rx}, and numeric mode 664 corresponds to symbolic mode +@samp{ug=rw,o=r}. Numeric mode 0 corresponds to symbolic mode +@samp{ugo=}. diff --git a/doc/texinfo.tex b/doc/texinfo.tex new file mode 100644 index 0000000..731cc1f --- /dev/null +++ b/doc/texinfo.tex @@ -0,0 +1,4365 @@ +%% TeX macros to handle texinfo files + +% Copyright (C) 1985, 86, 88, 90, 91, 92, 93, 1994 Free Software Foundation, Inc. + +%This texinfo.tex file 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 texinfo.tex file 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 texinfo.tex file; see the file COPYING. If not, write +%to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, +%USA. + + +%In other words, you are welcome to use, share and improve this program. +%You are forbidden to forbid anyone else to use, share and improve +%what you give them. Help stamp out software-hoarding! + +% This automatically updates the version number based on RCS. +\def\deftexinfoversion$#1: #2 ${\def\texinfoversion{#2}} +\deftexinfoversion$Revision: 1.1 $ +\message{Loading texinfo package [Version \texinfoversion]:} + +% Print the version number if in a .fmt file. +\everyjob{\message{[Texinfo version \texinfoversion]}\message{}} + +% Save some parts of plain tex whose names we will redefine. + +\let\ptextilde=\~ +\let\ptexlbrace=\{ +\let\ptexrbrace=\} +\let\ptexdots=\dots +\let\ptexdot=\. +\let\ptexstar=\* +\let\ptexend=\end +\let\ptexbullet=\bullet +\let\ptexb=\b +\let\ptexc=\c +\let\ptexi=\i +\let\ptext=\t +\let\ptexl=\l +\let\ptexL=\L + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + \gdef\tie{\leavevmode\penalty\@M\ } +} +\let\~ = \tie % And make it available as @~. + +\message{Basics,} +\chardef\other=12 + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Set up fixed words for English. +\ifx\putwordChapter\undefined{\gdef\putwordChapter{Chapter}}\fi% +\def\putwordInfo{Info}% +\ifx\putwordSee\undefined{\gdef\putwordSee{See}}\fi% +\ifx\putwordsee\undefined{\gdef\putwordsee{see}}\fi% +\ifx\putwordfile\undefined{\gdef\putwordfile{file}}\fi% +\ifx\putwordpage\undefined{\gdef\putwordpage{page}}\fi% +\ifx\putwordsection\undefined{\gdef\putwordsection{section}}\fi% +\ifx\putwordSection\undefined{\gdef\putwordSection{Section}}\fi% +\ifx\putwordTableofContents\undefined{\gdef\putwordTableofContents{Table of Contents}}\fi% +\ifx\putwordShortContents\undefined{\gdef\putwordShortContents{Short Contents}}\fi% +\ifx\putwordAppendix\undefined{\gdef\putwordAppendix{Appendix}}\fi% + +% Ignore a token. +% +\def\gobble#1{} + +\hyphenation{ap-pen-dix} +\hyphenation{mini-buf-fer mini-buf-fers} +\hyphenation{eshell} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen \bindingoffset \bindingoffset=0pt +\newdimen \normaloffset \normaloffset=\hoffset +\newdimen\pagewidth \newdimen\pageheight +\pagewidth=\hsize \pageheight=\vsize + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{\tracingcommands2 \tracingstats2 + \tracingpages1 \tracingoutput1 \tracinglostchars1 + \tracingmacros2 \tracingparagraphs1 \tracingrestores1 + \showboxbreadth\maxdimen\showboxdepth\maxdimen +}% + +%---------------------Begin change----------------------- +% +%%%% For @cropmarks command. +% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\cornerlong \newdimen\cornerthick +\newdimen \topandbottommargin +\newdimen \outerhsize \newdimen \outervsize +\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks +\outerhsize=7in +%\outervsize=9.5in +% Alternative @smallbook page size is 9.25in +\outervsize=9.25in +\topandbottommargin=.75in +% +%---------------------End change----------------------- + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions itself, but you have to call it yourself. +\chardef\PAGE=255 \output={\onepageout{\pagecontents\PAGE}} +\def\onepageout#1{\hoffset=\normaloffset +\ifodd\pageno \advance\hoffset by \bindingoffset +\else \advance\hoffset by -\bindingoffset\fi +{\escapechar=`\\\relax % makes sure backslash is used in output files. +\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}% +{\let\hsize=\pagewidth \makefootline}}}% +\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi} + +%%%% For @cropmarks command %%%% + +% Here is a modification of the main output routine for Near East Publications +% This provides right-angle cropmarks at all four corners. +% The contents of the page are centerlined into the cropmarks, +% and any desired binding offset is added as an \hskip on either +% site of the centerlined box. (P. A. MacKay, 12 November, 1986) +% +\def\croppageout#1{\hoffset=0pt % make sure this doesn't mess things up +{\escapechar=`\\\relax % makes sure backslash is used in output files. + \shipout + \vbox to \outervsize{\hsize=\outerhsize + \vbox{\line{\ewtop\hfill\ewtop}} + \nointerlineskip + \line{\vbox{\moveleft\cornerthick\nstop} + \hfill + \vbox{\moveright\cornerthick\nstop}} + \vskip \topandbottommargin + \centerline{\ifodd\pageno\hskip\bindingoffset\fi + \vbox{ + {\let\hsize=\pagewidth \makeheadline} + \pagebody{#1} + {\let\hsize=\pagewidth \makefootline}} + \ifodd\pageno\else\hskip\bindingoffset\fi} + \vskip \topandbottommargin plus1fill minus1fill + \boxmaxdepth\cornerthick + \line{\vbox{\moveleft\cornerthick\nsbot} + \hfill + \vbox{\moveright\cornerthick\nsbot}} + \nointerlineskip + \vbox{\line{\ewbot\hfill\ewbot}} + }} + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi} +% +% Do @cropmarks to get crop marks +\def\cropmarks{\let\onepageout=\croppageout } + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +\dimen@=\dp#1 \unvbox#1 +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg#1{% + \let\next = #1% + \begingroup + \obeylines + \futurelet\temp\parseargx +} + +% If the next token is an obeyed space (from an @example environment or +% the like), remove it and recurse. Otherwise, we're done. +\def\parseargx{% + % \obeyedspace is defined far below, after the definition of \sepspaces. + \ifx\obeyedspace\temp + \expandafter\parseargdiscardspace + \else + \expandafter\parseargline + \fi +} + +% Remove a single space (as the delimiter token to the macro call). +{\obeyspaces % + \gdef\parseargdiscardspace {\futurelet\temp\parseargx}} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + % + % First remove any @c comment, then any @comment. + % Result of each macro is put in \toks0. + \argremovec #1\c\relax % + \expandafter\argremovecomment \the\toks0 \comment\relax % + % + % Call the caller's macro, saved as \next in \parsearg. + \expandafter\next\expandafter{\the\toks0}% + }% +} + +% Since all \c{,omment} does is throw away the argument, we can let TeX +% do that for us. The \relax here is matched by the \relax in the call +% in \parseargline; it could be more or less anything, its purpose is +% just to delimit the argument to the \c. +\def\argremovec#1\c#2\relax{\toks0 = {#1}} +\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}} + +% \argremovec{,omment} might leave us with trailing spaces, though; e.g., +% @end itemize @c foo +% will have two active spaces as part of the argument with the +% `itemize'. Here we remove all active spaces from #1, and assign the +% result to \toks0. +% +% This loses if there are any *other* active characters besides spaces +% in the argument -- _ ^ +, for example -- since they get expanded. +% Fortunately, Texinfo does not define any such commands. (If it ever +% does, the catcode of the characters in questionwill have to be changed +% here.) But this means we cannot call \removeactivespaces as part of +% \argremovec{,omment}, since @c uses \parsearg, and thus the argument +% that \parsearg gets might well have any character at all in it. +% +\def\removeactivespaces#1{% + \begingroup + \ignoreactivespaces + \edef\temp{#1}% + \global\toks0 = \expandafter{\temp}% + \endgroup +} + +% Change the active space to expand to nothing. +% +\begingroup + \obeyspaces + \gdef\ignoreactivespaces{\obeyspaces\let =\empty} +\endgroup + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +%% These are used to keep @begin/@end levels from running away +%% Call \inENV within environments (after a \begingroup) +\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi} +\def\ENVcheck{% +\ifENV\errmessage{Still within an environment. Type Return to continue.} +\endgroup\fi} % This is not perfect, but it should reduce lossage + +% @begin foo is the same as @foo, for now. +\newhelp\EMsimple{Type to continue.} + +\outer\def\begin{\parsearg\beginxxx} + +\def\beginxxx #1{% +\expandafter\ifx\csname #1\endcsname\relax +{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else +\csname #1\endcsname\fi} + +% @end foo executes the definition of \Efoo. +% +\def\end{\parsearg\endxxx} +\def\endxxx #1{% + \removeactivespaces{#1}% + \edef\endthing{\the\toks0}% + % + \expandafter\ifx\csname E\endthing\endcsname\relax + \expandafter\ifx\csname \endthing\endcsname\relax + % There's no \foo, i.e., no ``environment'' foo. + \errhelp = \EMsimple + \errmessage{Undefined command `@end \endthing'}% + \else + \unmatchedenderror\endthing + \fi + \else + % Everything's ok; the right environment has been started. + \csname E\endthing\endcsname + \fi +} + +% There is an environment #1, but it hasn't been started. Give an error. +% +\def\unmatchedenderror#1{% + \errhelp = \EMsimple + \errmessage{This `@end #1' doesn't have a matching `@#1'}% +} + +% Define the control sequence \E#1 to give an unmatched @end error. +% +\def\defineunmatchedend#1{% + \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}% +} + + +% Single-spacing is done by various environments (specifically, in +% \nonfillstart and \quotations). +\newskip\singlespaceskip \singlespaceskip = 12.5pt +\def\singlespace{% + % Why was this kern here? It messes up equalizing space above and below + % environments. --karl, 6may93 + %{\advance \baselineskip by -\singlespaceskip + %\kern \baselineskip}% + \setleading \singlespaceskip +} + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt \char '100}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. + +\def\mylbrace {{\tt \char '173}} +\def\myrbrace {{\tt \char '175}} +\let\{=\mylbrace +\let\}=\myrbrace + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=3000 } + +% @enddots{} is an end-of-sentence ellipsis. +\gdef\enddots{$\mathinner{\ldotp\ldotp\ldotp\ldotp}$\spacefactor=3000} + +% @! is an end-of-sentence bang. +\gdef\!{!\spacefactor=3000 } + +% @? is an end-of-sentence query. +\gdef\?{?\spacefactor=3000 } + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +\def\group{\begingroup + \ifnum\catcode13=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + % + % The \vtop we start below produces a box with normal height and large + % depth; thus, TeX puts \baselineskip glue before it, and (when the + % next line of text is done) \lineskip glue after it. (See p.82 of + % the TeXbook.) Thus, space below is not quite equal to space + % above. But it's pretty close. + \def\Egroup{% + \egroup % End the \vtop. + \endgroup % End the \group. + }% + % + \vtop\bgroup + % We have to put a strut on the last line in case the @group is in + % the midst of an example, rather than completely enclosing it. + % Otherwise, the interline space between the last line of the group + % and the first line afterwards is too small. But we can't put the + % strut in \Egroup, since there it would be on a line by itself. + % Hence this just inserts a strut at the beginning of each line. + \everypar = {\strut}% + % + % Since we have a strut on every line, we don't need any of TeX's + % normal interline spacing. + \offinterlineskip + % + % OK, but now we have to do something about blank + % lines in the input in @example-like environments, which normally + % just turn into \lisppar, which will insert no space now that we've + % turned off the interline space. Simplest is to make them be an + % empty paragraph. + \ifx\par\lisppar + \edef\par{\leavevmode \par}% + % + % Reset ^^M's definition to new definition of \par. + \obeylines + \fi + % + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\def\need{\parsearg\needx} + +% Old definition--didn't work. +%\def\needx #1{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\penalty 10000 +%\prevdepth=-1000pt +%}} + +\def\needx#1{% + % Go into vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % Don't add any leading before our big empty box, but allow a page + % break, since the best break might be right here. + \allowbreak + \nointerlineskip + \vtop to #1\mil{\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak +} + +% @br forces paragraph break + +\let\br = \par + +% @dots{} output some dots + +\def\dots{$\ldots$} + +% @page forces the start of a new page + +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\def\exdent{\parsearg\exdentyyy} +\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}} + +% This defn is used inside nofill environments such as @example. +\def\nofillexdent{\parsearg\nofillexdentyyy} +\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount +\leftline{\hskip\leftskip{\rm#1}}}} + +%\hbox{{\rm#1}}\hfil\break}} + +% @include file insert text of that file as input. + +\def\include{\parsearg\includezzz} +%Use \input\thisfile to avoid blank after \input, which may be an active +%char (in which case the blank would become the \input argument). +%The grouping keeps the value of \thisfile correct even when @include +%is nested. +\def\includezzz #1{\begingroup +\def\thisfile{#1}\input\thisfile +\endgroup} + +\def\thisfile{} + +% @center line outputs that line, centered + +\def\center{\parsearg\centerzzz} +\def\centerzzz #1{{\advance\hsize by -\leftskip +\advance\hsize by -\rightskip +\centerline{#1}}} + +% @sp n outputs n lines of vertical space + +\def\sp{\parsearg\spxxx} +\def\spxxx #1{\par \vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\catcode 64=\other \catcode 123=\other \catcode 125=\other% +\parsearg \commentxxx} + +\def\commentxxx #1{\catcode 64=0 \catcode 123=1 \catcode 125=2 } + +\let\c=\comment + +% Prevent errors for section commands. +% Used in @ignore and in failing conditionals. +\def\ignoresections{% +\let\chapter=\relax +\let\unnumbered=\relax +\let\top=\relax +\let\unnumberedsec=\relax +\let\unnumberedsection=\relax +\let\unnumberedsubsec=\relax +\let\unnumberedsubsection=\relax +\let\unnumberedsubsubsec=\relax +\let\unnumberedsubsubsection=\relax +\let\section=\relax +\let\subsec=\relax +\let\subsubsec=\relax +\let\subsection=\relax +\let\subsubsection=\relax +\let\appendix=\relax +\let\appendixsec=\relax +\let\appendixsection=\relax +\let\appendixsubsec=\relax +\let\appendixsubsection=\relax +\let\appendixsubsubsec=\relax +\let\appendixsubsubsection=\relax +\let\contents=\relax +\let\smallbook=\relax +\let\titlepage=\relax +} + +% Used in nested conditionals, where we have to parse the Texinfo source +% and so want to turn off most commands, in case they are used +% incorrectly. +% +\def\ignoremorecommands{% + \let\defcv = \relax + \let\deffn = \relax + \let\deffnx = \relax + \let\defindex = \relax + \let\defivar = \relax + \let\defmac = \relax + \let\defmethod = \relax + \let\defop = \relax + \let\defopt = \relax + \let\defspec = \relax + \let\deftp = \relax + \let\deftypefn = \relax + \let\deftypefun = \relax + \let\deftypevar = \relax + \let\deftypevr = \relax + \let\defun = \relax + \let\defvar = \relax + \let\defvr = \relax + \let\ref = \relax + \let\xref = \relax + \let\printindex = \relax + \let\pxref = \relax + \let\settitle = \relax + \let\include = \relax + \let\lowersections = \relax + \let\down = \relax + \let\raisesections = \relax + \let\up = \relax + \let\set = \relax + \let\clear = \relax + \let\item = \relax + \let\message = \relax +} + +% Ignore @ignore ... @end ignore. +% +\def\ignore{\doignore{ignore}} + +% Also ignore @ifinfo, @ifhtml, @html, @menu, and @direntry text. +% +\def\ifinfo{\doignore{ifinfo}} +\def\ifhtml{\doignore{ifhtml}} +\def\html{\doignore{html}} +\def\menu{\doignore{menu}} +\def\direntry{\doignore{direntry}} + +% Ignore text until a line `@end #1'. +% +\def\doignore#1{\begingroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define a command to swallow text until we reach `@end #1'. + \long\def\doignoretext##1\end #1{\enddoignore}% + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \catcode32 = 10 + % + % And now expand that command. + \doignoretext +} + +% What we do to finish off ignored text. +% +\def\enddoignore{\endgroup\ignorespaces}% + +\newif\ifwarnedobs\warnedobsfalse +\def\obstexwarn{% + \ifwarnedobs\relax\else + % We need to warn folks that they may have trouble with TeX 3.0. + % This uses \immediate\write16 rather than \message to get newlines. + \immediate\write16{} + \immediate\write16{***WARNING*** for users of Unix TeX 3.0!} + \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).} + \immediate\write16{If you are running another version of TeX, relax.} + \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.} + \immediate\write16{ Then upgrade your TeX installation if you can.} + \immediate\write16{If you are stuck with version 3.0, run the} + \immediate\write16{ script ``tex3patch'' from the Texinfo distribution} + \immediate\write16{ to use a workaround.} + \immediate\write16{} + \warnedobstrue + \fi +} + +% **In TeX 3.0, setting text in \nullfont hangs tex. For a +% workaround (which requires the file ``dummy.tfm'' to be installed), +% uncomment the following line: +%%%%%\font\nullfont=dummy\let\obstexwarn=\relax + +% Ignore text, except that we keep track of conditional commands for +% purposes of nesting, up to an `@end #1' command. +% +\def\nestedignore#1{% + \obstexwarn + % We must actually expand the ignored text to look for the @end + % command, so that nested ignore constructs work. Thus, we put the + % text into a \vbox and then do nothing with the result. To minimize + % the change of memory overflow, we follow the approach outlined on + % page 401 of the TeXbook: make the current font be a dummy font. + % + \setbox0 = \vbox\bgroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define `@end #1' to end the box, which will in turn undefine the + % @end command again. + \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}% + % + % We are going to be parsing Texinfo commands. Most cause no + % trouble when they are used incorrectly, but some commands do + % complicated argument parsing or otherwise get confused, so we + % undefine them. + % + % We can't do anything about stray @-signs, unfortunately; + % they'll produce `undefined control sequence' errors. + \ignoremorecommands + % + % Set the current font to be \nullfont, a TeX primitive, and define + % all the font commands to also use \nullfont. We don't use + % dummy.tfm, as suggested in the TeXbook, because not all sites + % might have that installed. Therefore, math mode will still + % produce output, but that should be an extremely small amount of + % stuff compared to the main input. + % + \nullfont + \let\tenrm = \nullfont \let\tenit = \nullfont \let\tensl = \nullfont + \let\tenbf = \nullfont \let\tentt = \nullfont \let\smallcaps = \nullfont + \let\tensf = \nullfont + % Similarly for index fonts (mostly for their use in + % smallexample) + \let\indrm = \nullfont \let\indit = \nullfont \let\indsl = \nullfont + \let\indbf = \nullfont \let\indtt = \nullfont \let\indsc = \nullfont + \let\indsf = \nullfont + % + % Don't complain when characters are missing from the fonts. + \tracinglostchars = 0 + % + % Don't bother to do space factor calculations. + \frenchspacing + % + % Don't report underfull hboxes. + \hbadness = 10000 + % + % Do minimal line-breaking. + \pretolerance = 10000 + % + % Do not execute instructions in @tex + \def\tex{\doignore{tex}} +} + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% +\def\set{\parsearg\setxxx} +\def\setxxx#1{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + \def\temp{#2}% + \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty + \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted. + \fi +} +\def\setzzz#1#2 \endsetzzz{\expandafter\xdef\csname SET#1\endcsname{#2}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\def\clear{\parsearg\clearxxx} +\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax} + +% @value{foo} gets the text saved in variable foo. +% +\def\value#1{\expandafter + \ifx\csname SET#1\endcsname\relax + {\{No value for ``#1''\}} + \else \csname SET#1\endcsname \fi} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +\def\ifset{\parsearg\ifsetxxx} +\def\ifsetxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifsetfail + \else + \expandafter\ifsetsucceed + \fi +} +\def\ifsetsucceed{\conditionalsucceed{ifset}} +\def\ifsetfail{\nestedignore{ifset}} +\defineunmatchedend{ifset} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +\def\ifclear{\parsearg\ifclearxxx} +\def\ifclearxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifclearsucceed + \else + \expandafter\ifclearfail + \fi +} +\def\ifclearsucceed{\conditionalsucceed{ifclear}} +\def\ifclearfail{\nestedignore{ifclear}} +\defineunmatchedend{ifclear} + +% @iftex always succeeds; we read the text following, through @end +% iftex). But `@end iftex' should be valid only after an @iftex. +% +\def\iftex{\conditionalsucceed{iftex}} +\defineunmatchedend{iftex} + +% We can't just want to start a group at @iftex (for example) and end it +% at @end iftex, since then @set commands inside the conditional have no +% effect (they'd get reverted at the end of the group). So we must +% define \Eiftex to redefine itself to be its previous value. (We can't +% just define it to fail again with an ``unmatched end'' error, since +% the @ifset might be nested.) +% +\def\conditionalsucceed#1{% + \edef\temp{% + % Remember the current value of \E#1. + \let\nece{prevE#1} = \nece{E#1}% + % + % At the `@end #1', redefine \E#1 to be its previous value. + \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}% + }% + \temp +} + +% We need to expand lots of \csname's, but we don't want to expand the +% control sequences after we've constructed them. +% +\def\nece#1{\expandafter\noexpand\csname#1\endcsname} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math means output in math mode. +% We don't use $'s directly in the definition of \math because control +% sequences like \math are expanded when the toc file is written. Then, +% we read the toc file back, the $'s will be normal characters (as they +% should be, according to the definition of Texinfo). So we must use a +% control sequence to switch into and out of math mode. +% +% This isn't quite enough for @math to work properly in indices, but it +% seems unlikely it will ever be needed there. +% +\let\implicitmath = $ +\def\math#1{\implicitmath #1\implicitmath} + +% @bullet and @minus need the same treatment as @math, just above. +\def\bullet{\implicitmath\ptexbullet\implicitmath} +\def\minus{\implicitmath-\implicitmath} + +\def\node{\ENVcheck\parsearg\nodezzz} +\def\nodezzz#1{\nodexxx [#1,]} +\def\nodexxx[#1,#2]{\gdef\lastnode{#1}} +\let\nwnode=\node +\let\lastnode=\relax + +\def\donoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\setref{\lastnode}\fi +\global\let\lastnode=\relax} + +\def\unnumbnoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi +\global\let\lastnode=\relax} + +\def\appendixnoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\appendixsetref{\lastnode}\fi +\global\let\lastnode=\relax} + +\let\refill=\relax + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \readauxfile + \opencontents + \openindices + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \global\let\setfilename=\comment % Ignore extra @setfilename cmds. + \comment % Ignore the actual filename. +} + +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +\message{fonts,} + +% Font-change commands. + +% Texinfo supports the sans serif font style, which plain TeX does not. +% So we set up a \sf analogous to plain's \rm, etc. +\newfam\sffam +\def\sf{\fam=\sffam \tensf} +\let\li = \sf % Sometimes we call it \li, not \sf. + +%% Try out Computer Modern fonts at \magstephalf +\let\mainmagstep=\magstephalf + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +\def\fontprefix{cm} +\def\setfont#1#2{\font#1=\fontprefix#2} + +% Enter `@setfontprefix dc' to use the dc fonts instead of the cm fonts. +\def\setfontprefix{\parsearg\\setfontprefixzzz} +\def\setfontprefixzzz#1{\gdef\fontprefix{#1}} + +\ifx\bigger\relax +\let\mainmagstep=\magstep1 +\setfont\textrm{r12} +\setfont\texttt{tt12} +\else +\setfont\textrm{r10 scaled \mainmagstep} +\setfont\texttt{tt10 scaled \mainmagstep} +\fi +% Instead of cmb10, you many want to use cmbx10. +% cmbx10 is a prettier font on its own, but cmb10 +% looks better when embedded in a line with cmr10. +\setfont\textbf{b10 scaled \mainmagstep} +\setfont\textit{ti10 scaled \mainmagstep} +\setfont\textsl{sl10 scaled \mainmagstep} +\setfont\textsf{ss10 scaled \mainmagstep} +\setfont\textsc{csc10 scaled \mainmagstep} +\setfont\texti{mi10 scaled \mainmagstep} +\setfont\textsy{sy10 scaled \mainmagstep} + +% A few fonts for @defun, etc. +\setfont\defbf{bx10 scaled \magstep1} %was 1314 +\setfont\deftt{tt10 scaled \magstep1} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf} + +% Fonts for indices and small examples. +% We actually use the slanted font rather than the italic, +% because texinfo normally uses the slanted fonts for that. +% Do not make many font distinctions in general in the index, since they +% aren't very useful. +\setfont\ninett{tt9} +\setfont\indrm{r9} +\setfont\indit{sl9} +\let\indsl=\indit +\let\indtt=\ninett +\let\indsf=\indrm +\let\indbf=\indrm +\let\indsc=\indrm +\setfont\indi{mi9} +\setfont\indsy{sy9} + +% Fonts for headings +\setfont\chaprm{bx12 scaled \magstep2} +\setfont\chapit{ti12 scaled \magstep2} +\setfont\chapsl{sl12 scaled \magstep2} +\setfont\chaptt{tt12 scaled \magstep2} +\setfont\chapsf{ss12 scaled \magstep2} +\let\chapbf=\chaprm +\setfont\chapsc{csc10 scaled\magstep3} +\setfont\chapi{mi12 scaled \magstep2} +\setfont\chapsy{sy10 scaled \magstep3} + +\setfont\secrm{bx12 scaled \magstep1} +\setfont\secit{ti12 scaled \magstep1} +\setfont\secsl{sl12 scaled \magstep1} +\setfont\sectt{tt12 scaled \magstep1} +\setfont\secsf{ss12 scaled \magstep1} +\setfont\secbf{bx12 scaled \magstep1} +\setfont\secsc{csc10 scaled\magstep2} +\setfont\seci{mi12 scaled \magstep1} +\setfont\secsy{sy10 scaled \magstep2} + +% \setfont\ssecrm{bx10 scaled \magstep1} % This size an font looked bad. +% \setfont\ssecit{cmti10 scaled \magstep1} % The letters were too crowded. +% \setfont\ssecsl{sl10 scaled \magstep1} +% \setfont\ssectt{tt10 scaled \magstep1} +% \setfont\ssecsf{ss10 scaled \magstep1} + +%\setfont\ssecrm{b10 scaled 1315} % Note the use of cmb rather than cmbx. +%\setfont\ssecit{ti10 scaled 1315} % Also, the size is a little larger than +%\setfont\ssecsl{sl10 scaled 1315} % being scaled magstep1. +%\setfont\ssectt{tt10 scaled 1315} +%\setfont\ssecsf{ss10 scaled 1315} + +%\let\ssecbf=\ssecrm + +\setfont\ssecrm{bx12 scaled \magstephalf} +\setfont\ssecit{ti12 scaled \magstephalf} +\setfont\ssecsl{sl12 scaled \magstephalf} +\setfont\ssectt{tt12 scaled \magstephalf} +\setfont\ssecsf{ss12 scaled \magstephalf} +\setfont\ssecbf{bx12 scaled \magstephalf} +\setfont\ssecsc{csc10 scaled \magstep1} +\setfont\sseci{mi12 scaled \magstephalf} +\setfont\ssecsy{sy10 scaled \magstep1} +% The smallcaps and symbol fonts should actually be scaled \magstep1.5, +% but that is not a standard magnification. + +% Fonts for title page: +\setfont\titlerm{bx12 scaled \magstep3} +\let\authorrm = \secrm + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts, we +% don't bother to reset \scriptfont and \scriptscriptfont (which would +% also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy + \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf + \textfont\ttfam = \tentt \textfont\sffam = \tensf +} + + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this so that font changes will continue to work +% in math mode, where it is the current \fam that is relevant in most +% cases, not the current. Plain TeX does, for example, +% \def\bf{\fam=\bffam \tenbf} By redefining \tenbf, we obviate the need +% to redefine \bf itself. +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \resetmathfonts} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \resetmathfonts} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \resetmathfonts} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \resetmathfonts} +\def\indexfonts{% + \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl + \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc + \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy + \resetmathfonts} + +% Set up the default fonts, so we can use them for creating boxes. +% +\textfonts + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Fonts for short table of contents. +\setfont\shortcontrm{r12} +\setfont\shortcontbf{bx12} +\setfont\shortcontsl{sl12} + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi} +\def\smartitalic#1{{\sl #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\var=\smartitalic +\let\dfn=\smartitalic +\let\emph=\smartitalic +\let\cite=\smartitalic + +\def\b#1{{\bf #1}} +\let\strong=\b + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +\def\t#1{% + {\tt \nohyphenation \rawbackslash \frenchspacing #1}% + \null +} +\let\ttfont = \t +%\def\samp #1{`{\tt \rawbackslash \frenchspacing #1}'\null} +\def\samp #1{`\tclose{#1}'\null} +\def\key #1{{\tt \nohyphenation \uppercase{#1}}\null} +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +\let\file=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \frenchspacing + #1% + }% + \null +} + +% We *must* turn on hyphenation at `-' and `_' in \code. +% Otherwise, it is too hard to avoid overful hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate an a dash. +% -- rms. +{ +\catcode`\-=\active +\catcode`\_=\active +\global\def\code{\begingroup \catcode`\-=\active \let-\codedash \catcode`\_=\active \let_\codeunder \codex} +% The following is used by \doprintindex to insure that long function names +% wrap around. It is necessary for - and _ to be active before the index is +% read from the file, as \entry parses the arguments long before \code is +% ever called. -- mycroft +\global\def\indexbreaks{\catcode`\-=\active \let-\realdash \catcode`\_=\active \let_\realunder} +} +\def\realdash{-} +\def\realunder{_} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{\normalunderscore\discretionary{}{}{}} +\def\codex #1{\tclose{#1}\endgroup} + +%\let\exp=\tclose %Was temporary + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else\tclose{\look}\fi +\else\tclose{\look}\fi} + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of +% @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par} + +\def\l#1{{\li #1}\null} % + +\def\r#1{{\rm #1}} % roman font +% Use of \lowercase was suggested. +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\def\titlefont#1{{\titlerm #1}} + +\newif\ifseenauthor +\newif\iffinishedtitlepage + +\def\shorttitlepage{\parsearg\shorttitlepagezzz} +\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\def\titlepage{\begingroup \parindent=0pt \textfonts + \let\subtitlerm=\tenrm +% I deinstalled the following change because \cmr12 is undefined. +% This change was not in the ChangeLog anyway. --rms. +% \let\subtitlerm=\cmr12 + \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}% + % + \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}% + % + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % + % Now you can print the title using @title. + \def\title{\parsearg\titlezzz}% + \def\titlezzz##1{\leftline{\titlefont{##1}} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt}% + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Now you can put text using @subtitle. + \def\subtitle{\parsearg\subtitlezzz}% + \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}% + % + % @author should come last, but may come many times. + \def\author{\parsearg\authorzzz}% + \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi + {\authorfont \leftline{##1}}}% + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \oldpage + \let\page = \oldpage + \hbox{}}% +% \def\page{\oldpage \hbox{}} +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + \HEADINGSon +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks \evenheadline % Token sequence for heading line of even pages +\newtoks \oddheadline % Token sequence for heading line of odd pages +\newtoks \evenfootline % Token sequence for footing line of even pages +\newtoks \oddfootline % Token sequence for footing line of odd pages + +% Now make Tex use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + +\def\evenheading{\parsearg\evenheadingxxx} +\def\oddheading{\parsearg\oddheadingxxx} +\def\everyheading{\parsearg\everyheadingxxx} + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\oddfooting{\parsearg\oddfootingxxx} +\def\everyfooting{\parsearg\everyfootingxxx} + +{\catcode`\@=0 % + +\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish} +\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish} +\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyheadingxxx #1{\everyheadingyyy #1@|@|@|@|\finish} +\gdef\everyheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}} +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish} +\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish} +\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{% +\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyfootingxxx #1{\everyfootingyyy #1@|@|@|@|\finish} +\gdef\everyfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}} +\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} +% +}% unbind the catcode of @. + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{ +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{ +%\pagealignmacro +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{ +%\pagealignmacro +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +} + +% Subroutines used in generating headings +% Produces Day Month Year style of output. +\def\today{\number\day\space +\ifcase\month\or +January\or February\or March\or April\or May\or June\or +July\or August\or September\or October\or November\or December\fi +\space\number\year} + +% Use this if you want the Month Day, Year style of output. +%\def\today{\ifcase\month\or +%January\or February\or March\or April\or May\or June\or +%July\or August\or September\or October\or November\or December\fi +%\space\number\day, \number\year} + +% @settitle line... specifies the title of the document, for headings +% It generates no output of its own + +\def\thistitle{No Title} +\def\settitle{\parsearg\settitlezzz} +\def\settitlezzz #1{\gdef\thistitle{#1}} + +\message{tables,} + +% @tabs -- simple alignment + +% These don't work. For one thing, \+ is defined as outer. +% So these macros cannot even be defined. + +%\def\tabs{\parsearg\tabszzz} +%\def\tabszzz #1{\settabs\+#1\cr} +%\def\tabline{\parsearg\tablinezzz} +%\def\tablinezzz #1{\+#1\cr} +%\def\&{&} + +% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @vtable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz} +\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz} + +\def\internalBkitem{\smallbreak \parsearg\kitemzzz} +\def\internalBkitemx{\itemxpar \parsearg\kitemzzz} + +\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}% + \itemzzz {#1}} + +\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}% + \itemzzz {#1}} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemfont{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % Be sure we are not still in the middle of a paragraph. + %{\parskip = 0in + %\par + %}% + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. Unfortunately + % we can't prevent a possible page break at the following + % \baselineskip glue. + \nobreak + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. Since that + % text will be indented by \tableindent, we make the item text be in + % a zero-width box. + \noindent + \rlap{\hskip -\tableindent\box0}\ignorespaces% + \endgroup% + \itemxneedsnegativevskiptrue% + \fi +} + +\def\item{\errmessage{@item while not in a table}} +\def\itemx{\errmessage{@itemx while not in a table}} +\def\kitem{\errmessage{@kitem while not in a table}} +\def\kitemx{\errmessage{@kitemx while not in a table}} +\def\xitem{\errmessage{@xitem while not in a table}} +\def\xitemx{\errmessage{@xitemx while not in a table}} + +%% Contains a kludge to get @end[description] to work +\def\description{\tablez{\dontindex}{1}{}{}{}{}} + +\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex} +{\obeylines\obeyspaces% +\gdef\tablex #1^^M{% +\tabley\dontindex#1 \endtabley}} + +\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex} +{\obeylines\obeyspaces% +\gdef\ftablex #1^^M{% +\tabley\fnitemindex#1 \endtabley +\def\Eftable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex} +{\obeylines\obeyspaces% +\gdef\vtablex #1^^M{% +\tabley\vritemindex#1 \endtabley +\def\Evtable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\dontindex #1{} +\def\fnitemindex #1{\doind {fn}{\code{#1}}}% +\def\vritemindex #1{\doind {vr}{\code{#1}}}% + +{\obeyspaces % +\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup% +\tablez{#1}{#2}{#3}{#4}{#5}{#6}}} + +\def\tablez #1#2#3#4#5#6{% +\aboveenvbreak % +\begingroup % +\def\Edescription{\Etable}% Neccessary kludge. +\let\itemindex=#1% +\ifnum 0#3>0 \advance \leftskip by #3\mil \fi % +\ifnum 0#4>0 \tableindent=#4\mil \fi % +\ifnum 0#5>0 \advance \rightskip by #5\mil \fi % +\def\itemfont{#2}% +\itemmax=\tableindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \tableindent % +\exdentamount=\tableindent +\parindent = 0pt +\parskip = \smallskipamount +\ifdim \parskip=0pt \parskip=2pt \fi% +\def\Etable{\endgraf\afterenvbreak\endgroup}% +\let\item = \internalBitem % +\let\itemx = \internalBitemx % +\let\kitem = \internalBkitem % +\let\kitemx = \internalBkitemx % +\let\xitem = \internalBxitem % +\let\xitemx = \internalBxitemx % +} + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\def\itemize{\parsearg\itemizezzz} + +\def\itemizezzz #1{% + \begingroup % ended by the @end itemsize + \itemizey {#1}{\Eitemize} +} + +\def\itemizey #1#2{% +\aboveenvbreak % +\itemmax=\itemindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \itemindent % +\exdentamount=\itemindent +\parindent = 0pt % +\parskip = \smallskipamount % +\ifdim \parskip=0pt \parskip=2pt \fi% +\def#2{\endgraf\afterenvbreak\endgroup}% +\def\itemcontents{#1}% +\let\item=\itemizeitem} + +% Set sfcode to normal for the chars that usually have another value. +% These are `.?!:;,' +\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000 + \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 } + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\def\enumerate{\parsearg\enumeratezzz} +\def\enumeratezzz #1{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + \begingroup % ended by the @end enumerate + % + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call itemizey, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \itemizey{#1.}\Eenumerate\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + +% Definition of @item while inside @itemize. + +\def\itemizeitem{% +\advance\itemno by 1 +{\let\par=\endgraf \smallbreak}% +\ifhmode \errmessage{\in hmode at itemizeitem}\fi +{\parskip=0in \hskip 0pt +\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}% +\vadjust{\penalty 1200}}% +\flushcr} + +% @multitable macros +% Amy Hendrickson, 8/18/94 +% +% @multitable ... @endmultitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @percentofhsize .2 .3 .5 +% @item ... +% +% Numbers following @percentofhsize are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab, @multicolumn or @endmulticolumn do not need to be on their +% own lines, but it will not hurt if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @endmultitable + +% Default dimensions may be reset by user. +% @intableparskip will set vertical space between paragraphs in table. +% @intableparindent will set paragraph indent in table. +% @spacebetweencols will set horizontal space to be left between columns. +% @spacebetweenlines will set vertical space to be left between lines. + +%%%% +% Dimensions + +\newdimen\intableparskip +\newdimen\intableparindent +\newdimen\spacebetweencols +\newdimen\spacebetweenlines +\intableparskip=0pt +\intableparindent=6pt +\spacebetweencols=12pt +\spacebetweenlines=12pt + +%%%% +% Macros used to set up halign preamble: +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\percentofhsize\relax +\def\xpercentofhsize{\percentofhsize} +\newif\ifsetpercent + +\newcount\colcount +\def\setuptable#1{\def\firstarg{#1}% +\ifx\firstarg\xendsetuptable\let\go\relax% +\else + \ifx\firstarg\xpercentofhsize\global\setpercenttrue% + \else + \ifsetpercent + \if#1.\else% + \global\advance\colcount by1 % + \expandafter\xdef\csname col\the\colcount\endcsname{.#1\hsize}% + \fi + \else + \global\advance\colcount by1 + \setbox0=\hbox{#1}% + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi% + \fi% + \let\go\setuptable% +\fi\go} +%%%% +% multitable syntax +\def\tab{&} + +%%%% +% @multitable ... @endmultitable definitions: + +\def\multitable#1\item{\bgroup +\let\item\cr +\tolerance=9500 +\hbadness=9500 +\parskip=\intableparskip +\parindent=\intableparindent +\overfullrule=0pt +\global\colcount=0\relax% +\def\Emultitable{\global\setpercentfalse\global\everycr{}\cr\egroup\egroup}% + % To parse everything between @multitable and @item : +\def\one{#1}\expandafter\setuptable\one\endsetuptable + % Need to reset this to 0 after \setuptable. +\global\colcount=0\relax% + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. +\halign\bgroup&\global\advance\colcount by 1\relax% +\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname + % In order to keep entries from bumping into each other + % we will add a \leftskip of \spacebetweencols to all columns after + % the first one. + % If a template has been used, we will add \spacebetweencols + % to the width of each template entry. + % If user has set preamble in terms of percent of \hsize + % we will use that dimension as the width of the column, and + % the \leftskip will keep entries from bumping into each other. + % Table will start at left margin and final column will justify at + % right margin. +\ifnum\colcount=1 +\else + \ifsetpercent + \else + % If user has set preamble in terms of percent of \hsize + % we will advance \hsize by \spacebetweencols + \advance\hsize by \spacebetweencols + \fi + % In either case we will make \leftskip=\spacebetweencols: +\leftskip=\spacebetweencols +\fi +\noindent##}\cr% + % \everycr will reset column counter, \colcount, at the end of + % each line. Every column entry will cause \colcount to advance by one. + % The table preamble + % looks at the current \colcount to find the correct column width. +\global\everycr{\noalign{\nointerlineskip\vskip\spacebetweenlines +\filbreak%% keeps underfull box messages off when table breaks over pages. +\global\colcount=0\relax}}} + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within \newindex. +{\catcode`\@=11 +\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. + +\def\newindex #1{ +\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file +\openout \csname#1indfile\endcsname \jobname.#1 % Open the file +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\doindex {#1}} +} + +% @defindex foo == \newindex{foo} + +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. + +\def\newcodeindex #1{ +\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file +\openout \csname#1indfile\endcsname \jobname.#1 % Open the file +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\docodeindex {#1}} +} + +\def\defcodeindex{\parsearg\newcodeindex} + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +\def\synindex #1 #2 {% +\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname +\expandafter\let\csname#1indfile\endcsname=\synindexfoo +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\doindex {#2}}% +} + +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +\def\syncodeindex #1 #2 {% +\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname +\expandafter\let\csname#1indfile\endcsname=\synindexfoo +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\docodeindex {#2}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +\def\indexdummies{% +% Take care of the plain tex accent commands. +\def\"{\realbackslash "}% +\def\`{\realbackslash `}% +\def\'{\realbackslash '}% +\def\^{\realbackslash ^}% +\def\~{\realbackslash ~}% +\def\={\realbackslash =}% +\def\b{\realbackslash b}% +\def\c{\realbackslash c}% +\def\d{\realbackslash d}% +\def\u{\realbackslash u}% +\def\v{\realbackslash v}% +\def\H{\realbackslash H}% +% Take care of the plain tex special European modified letters. +\def\oe{\realbackslash oe}% +\def\ae{\realbackslash ae}% +\def\aa{\realbackslash aa}% +\def\OE{\realbackslash OE}% +\def\AE{\realbackslash AE}% +\def\AA{\realbackslash AA}% +\def\o{\realbackslash o}% +\def\O{\realbackslash O}% +\def\l{\realbackslash l}% +\def\L{\realbackslash L}% +\def\ss{\realbackslash ss}% +% Take care of texinfo commands likely to appear in an index entry. +\def\_{{\realbackslash _}}% +\def\w{\realbackslash w }% +\def\bf{\realbackslash bf }% +\def\rm{\realbackslash rm }% +\def\sl{\realbackslash sl }% +\def\sf{\realbackslash sf}% +\def\tt{\realbackslash tt}% +\def\gtr{\realbackslash gtr}% +\def\less{\realbackslash less}% +\def\hat{\realbackslash hat}% +\def\char{\realbackslash char}% +\def\TeX{\realbackslash TeX}% +\def\dots{\realbackslash dots }% +\def\copyright{\realbackslash copyright }% +\def\tclose##1{\realbackslash tclose {##1}}% +\def\code##1{\realbackslash code {##1}}% +\def\samp##1{\realbackslash samp {##1}}% +\def\t##1{\realbackslash r {##1}}% +\def\r##1{\realbackslash r {##1}}% +\def\i##1{\realbackslash i {##1}}% +\def\b##1{\realbackslash b {##1}}% +\def\cite##1{\realbackslash cite {##1}}% +\def\key##1{\realbackslash key {##1}}% +\def\file##1{\realbackslash file {##1}}% +\def\var##1{\realbackslash var {##1}}% +\def\kbd##1{\realbackslash kbd {##1}}% +\def\dfn##1{\realbackslash dfn {##1}}% +\def\emph##1{\realbackslash emph {##1}}% +} + +% \indexnofonts no-ops all font-change commands. +% This is used when outputting the strings to sort the index by. +\def\indexdummyfont#1{#1} +\def\indexdummytex{TeX} +\def\indexdummydots{...} + +\def\indexnofonts{% +% Just ignore accents. +\let\"=\indexdummyfont +\let\`=\indexdummyfont +\let\'=\indexdummyfont +\let\^=\indexdummyfont +\let\~=\indexdummyfont +\let\==\indexdummyfont +\let\b=\indexdummyfont +\let\c=\indexdummyfont +\let\d=\indexdummyfont +\let\u=\indexdummyfont +\let\v=\indexdummyfont +\let\H=\indexdummyfont +% Take care of the plain tex special European modified letters. +\def\oe{oe}% +\def\ae{ae}% +\def\aa{aa}% +\def\OE{OE}% +\def\AE{AE}% +\def\AA{AA}% +\def\o{o}% +\def\O{O}% +\def\l{l}% +\def\L{L}% +\def\ss{ss}% +\let\w=\indexdummyfont +\let\t=\indexdummyfont +\let\r=\indexdummyfont +\let\i=\indexdummyfont +\let\b=\indexdummyfont +\let\emph=\indexdummyfont +\let\strong=\indexdummyfont +\let\cite=\indexdummyfont +\let\sc=\indexdummyfont +%Don't no-op \tt, since it isn't a user-level command +% and is used in the definitions of the active chars like <, >, |... +%\let\tt=\indexdummyfont +\let\tclose=\indexdummyfont +\let\code=\indexdummyfont +\let\file=\indexdummyfont +\let\samp=\indexdummyfont +\let\kbd=\indexdummyfont +\let\key=\indexdummyfont +\let\var=\indexdummyfont +\let\TeX=\indexdummytex +\let\dots=\indexdummydots +} + +% To define \realbackslash, we must make \ not be an escape. +% We must first make another character (@) an escape +% so we do not become unable to do a definition. + +{\catcode`\@=0 \catcode`\\=\other +@gdef@realbackslash{\}} + +\let\indexbackslash=0 %overridden during \printindex. + +\def\doind #1#2{% +{\count10=\lastpenalty % +{\indexdummies % Must do this here, since \bf, etc expand at this stage +\escapechar=`\\% +{\let\folio=0% Expand all macros now EXCEPT \folio +\def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now +% so it will be output as is; and it will print as backslash in the indx. +% +% Now process the index-string once, with all font commands turned off, +% to get the string to sort the index by. +{\indexnofonts +\xdef\temp1{#2}% +}% +% Now produce the complete index entry. We process the index-string again, +% this time with font commands expanded, to get what to print in the index. +\edef\temp{% +\write \csname#1indfile\endcsname{% +\realbackslash entry {\temp1}{\folio}{#2}}}% +\temp }% +}\penalty\count10}} + +\def\dosubind #1#2#3{% +{\count10=\lastpenalty % +{\indexdummies % Must do this here, since \bf, etc expand at this stage +\escapechar=`\\% +{\let\folio=0% +\def\rawbackslashxx{\indexbackslash}% +% +% Now process the index-string once, with all font commands turned off, +% to get the string to sort the index by. +{\indexnofonts +\xdef\temp1{#2 #3}% +}% +% Now produce the complete index entry. We process the index-string again, +% this time with font commands expanded, to get what to print in the index. +\edef\temp{% +\write \csname#1indfile\endcsname{% +\realbackslash entry {\temp1}{\folio}{#2}{#3}}}% +\temp }% +}\penalty\count10}} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% This is what you call to cause a particular index to get printed. +% Write +% @unnumbered Function Index +% @printindex fn + +\def\printindex{\parsearg\doprintindex} + +\def\doprintindex#1{% + \tex + \dobreak \chapheadingskip {10000} + \catcode`\%=\other\catcode`\&=\other\catcode`\#=\other + \catcode`\$=\other + \catcode`\~=\other + \indexbreaks + % + % The following don't help, since the chars were translated + % when the raw index was written, and their fonts were discarded + % due to \indexnofonts. + %\catcode`\"=\active + %\catcode`\^=\active + %\catcode`\_=\active + %\catcode`\|=\active + %\catcode`\<=\active + %\catcode`\>=\active + % % + \def\indexbackslash{\rawbackslashxx} + \indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt + \begindoublecolumns + % + % See if the index file exists and is nonempty. + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + (Index is nonexistent) + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + (Index is empty) + \else + \input \jobname.#1s + \fi + \fi + \closein 1 + \enddoublecolumns + \Etex +} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +% Same as \bigskipamount except no shrink. +% \balancecolumns gets confused if there is any shrink. +\newskip\initialskipamount \initialskipamount 12pt plus4pt + +\def\initial #1{% +{\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt +\ifdim\lastskip<\initialskipamount +\removelastskip \penalty-200 \vskip \initialskipamount\fi +\line{\secbf#1\hfill}\kern 2pt\penalty10000}} + +% This typesets a paragraph consisting of #1, dot leaders, and then #2 +% flush to the right margin. It is used for index and table of contents +% entries. The paragraph is indented by \leftskip. +% +\def\entry #1#2{\begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent=2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % Start a ``paragraph'' for the index entry so the line breaking + % parameters we've set above will have an effect. + \noindent + % + % Insert the text of the index entry. TeX will do line-breaking on it. + #1% + % The following is kluged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \def\tempa{{\rm }}% + \def\tempb{#2}% + \edef\tempc{\tempa}% + \edef\tempd{\tempb}% + \ifx\tempc\tempd\ \else% + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ #2% The page number ends the paragraph. + \fi% + \par +\endgroup} + +% Like \dotfill except takes at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm + +\def\secondary #1#2{ +{\parfillskip=0in \parskip=0in +\hangindent =1in \hangafter=1 +\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par +}} + +%% Define two-column mode, which is used in indexes. +%% Adapted from the TeXbook, page 416. +\catcode `\@=11 + +\newbox\partialpage + +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup + % Grab any single-column material above us. + \output = {\global\setbox\partialpage + =\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}% + \eject + % + % Now switch to the double-column output routine. + \output={\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it once. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +- < + % 1pt) as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize + \doublecolumnpagegoal +} + +\def\enddoublecolumns{\eject \endgroup \pagegoal=\vsize \unvbox\partialpage} + +\def\doublecolumnsplit{\splittopskip=\topskip \splitmaxdepth=\maxdepth + \global\dimen@=\pageheight \global\advance\dimen@ by-\ht\partialpage + \global\setbox1=\vsplit255 to\dimen@ \global\setbox0=\vbox{\unvbox1} + \global\setbox3=\vsplit255 to\dimen@ \global\setbox2=\vbox{\unvbox3} + \ifdim\ht0>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi + \ifdim\ht2>\dimen@ \setbox255=\vbox{\unvbox0\unvbox2} \global\setbox255=\copy5 \fi +} +\def\doublecolumnpagegoal{% + \dimen@=\vsize \advance\dimen@ by-2\ht\partialpage \global\pagegoal=\dimen@ +} +\def\pagesofar{\unvbox\partialpage % + \hsize=\doublecolumnhsize % have to restore this since output routine + \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}} +\def\doublecolumnout{% + \setbox5=\copy255 + {\vbadness=10000 \doublecolumnsplit} + \ifvbox255 + \setbox0=\vtop to\dimen@{\unvbox0} + \setbox2=\vtop to\dimen@{\unvbox2} + \onepageout\pagesofar \unvbox255 \penalty\outputpenalty + \else + \setbox0=\vbox{\unvbox5} + \ifvbox0 + \dimen@=\ht0 \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip + \divide\dimen@ by2 \splittopskip=\topskip \splitmaxdepth=\maxdepth + {\vbadness=10000 + \loop \global\setbox5=\copy0 + \setbox1=\vsplit5 to\dimen@ + \setbox3=\vsplit5 to\dimen@ + \ifvbox5 \global\advance\dimen@ by1pt \repeat + \setbox0=\vbox to\dimen@{\unvbox1} + \setbox2=\vbox to\dimen@{\unvbox3} + \global\setbox\partialpage=\vbox{\pagesofar} + \doublecolumnpagegoal + } + \fi + \fi +} + +\catcode `\@=\other +\message{sectioning,} +% Define chapters, sections, etc. + +\newcount \chapno +\newcount \secno \secno=0 +\newcount \subsecno \subsecno=0 +\newcount \subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount \appendixno \appendixno = `\@ +\def\appendixletter{\char\the\appendixno} + +\newwrite \contentsfile +% This is called from \setfilename. +\def\opencontents{\openout \contentsfile = \jobname.toc} + +% Each @chapter defines this as the name of the chapter. +% page headings and footings can use it. @section does likewise + +\def\thischapter{} \def\thissection{} +\def\seccheck#1{\if \pageno<0 % +\errmessage{@#1 not allowed after generating table of contents}\fi +% +} + +\def\chapternofonts{% +\let\rawbackslash=\relax% +\let\frenchspacing=\relax% +\def\result{\realbackslash result} +\def\equiv{\realbackslash equiv} +\def\expansion{\realbackslash expansion} +\def\print{\realbackslash print} +\def\TeX{\realbackslash TeX} +\def\dots{\realbackslash dots} +\def\copyright{\realbackslash copyright} +\def\tt{\realbackslash tt} +\def\bf{\realbackslash bf } +\def\w{\realbackslash w} +\def\less{\realbackslash less} +\def\gtr{\realbackslash gtr} +\def\hat{\realbackslash hat} +\def\char{\realbackslash char} +\def\tclose##1{\realbackslash tclose {##1}} +\def\code##1{\realbackslash code {##1}} +\def\samp##1{\realbackslash samp {##1}} +\def\r##1{\realbackslash r {##1}} +\def\b##1{\realbackslash b {##1}} +\def\key##1{\realbackslash key {##1}} +\def\file##1{\realbackslash file {##1}} +\def\kbd##1{\realbackslash kbd {##1}} +% These are redefined because @smartitalic wouldn't work inside xdef. +\def\i##1{\realbackslash i {##1}} +\def\cite##1{\realbackslash cite {##1}} +\def\var##1{\realbackslash var {##1}} +\def\emph##1{\realbackslash emph {##1}} +\def\dfn##1{\realbackslash dfn {##1}} +} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raise/lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% Choose a numbered-heading macro +% #1 is heading level if unmodified by @raisesections or @lowersections +% #2 is text for heading +\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \chapterzzz{#2} +\or + \seczzz{#2} +\or + \numberedsubseczzz{#2} +\or + \numberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \chapterzzz{#2} + \else + \numberedsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses appendix heading levels +\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \appendixzzz{#2} +\or + \appendixsectionzzz{#2} +\or + \appendixsubseczzz{#2} +\or + \appendixsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \appendixzzz{#2} + \else + \appendixsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses numberless heading levels +\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \unnumberedzzz{#2} +\or + \unnumberedseczzz{#2} +\or + \unnumberedsubseczzz{#2} +\or + \unnumberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \unnumberedzzz{#2} + \else + \unnumberedsubsubseczzz{#2} + \fi +\fi +} + + +\def\thischaptername{No Chapter Title} +\outer\def\chapter{\parsearg\chapteryyy} +\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz #1{\seccheck{chapter}% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \chapno by 1 \message{Chapter \the\chapno}% +\chapmacro {#1}{\the\chapno}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +% We don't substitute the actual chapter name into \thischapter +% because we don't want its macros evaluated now. +\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}% +{\chapternofonts% +\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec +}} + +\outer\def\appendix{\parsearg\appendixyyy} +\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz #1{\seccheck{appendix}% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \appendixno by 1 \message{Appendix \appendixletter}% +\chapmacro {#1}{\putwordAppendix{} \appendixletter}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}% +{\chapternofonts% +\edef\temp{{\realbackslash chapentry + {#1}{\putwordAppendix{} \appendixletter}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\global\let\section = \appendixsec +\global\let\subsection = \appendixsubsec +\global\let\subsubsection = \appendixsubsubsec +}} + +\outer\def\top{\parsearg\unnumberedyyy} +\outer\def\unnumbered{\parsearg\unnumberedyyy} +\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz #1{\seccheck{unnumbered}% +\secno=0 \subsecno=0 \subsubsecno=0 +% +% This used to be simply \message{#1}, but TeX fully expands the +% argument to \message. Therefore, if #1 contained @-commands, TeX +% expanded them. For example, in `@unnumbered The @cite{Book}', TeX +% expanded @cite (which turns out to cause errors because \cite is meant +% to be executed, not expanded). +% +% Anyway, we don't want the fully-expanded definition of @cite to appear +% as a result of the \message, we just want `@cite' itself. We use +% \the to achieve this: TeX expands \the only once, +% simply yielding the contents of the . +\toks0 = {#1}\message{(\the\toks0)}% +% +\unnumbchapmacro {#1}% +\gdef\thischapter{#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\global\let\section = \unnumberedsec +\global\let\subsection = \unnumberedsubsec +\global\let\subsubsection = \unnumberedsubsubsec +}} + +\outer\def\numberedsec{\parsearg\secyyy} +\def\secyyy #1{\numhead1{#1}} % normally calls seczzz +\def\seczzz #1{\seccheck{section}% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}% +{\chapternofonts% +\edef\temp{{\realbackslash secentry % +{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appenixsection{\parsearg\appendixsecyyy} +\outer\def\appendixsec{\parsearg\appendixsecyyy} +\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz #1{\seccheck{appendixsection}% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}% +{\chapternofonts% +\edef\temp{{\realbackslash secentry % +{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy} +\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz #1{\seccheck{unnumberedsec}% +\plainsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy} +\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz #1{\seccheck{subsection}% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsecentry % +{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy} +\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz #1{\seccheck{appendixsubsec}% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsecentry % +{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy} +\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}% +\plainsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy} +\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz #1{\seccheck{subsubsection}% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsubsecentry % + {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno} + {\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy} +\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsubsecentry{#1}% + {\appendixletter} + {\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy} +\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}% +\plainsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +% These are variants which are not "outer", so they can appear in @ifinfo. +% Actually, they should now be obsolete; ordinary section commands should work. +\def\infotop{\parsearg\unnumberedzzz} +\def\infounnumbered{\parsearg\unnumberedzzz} +\def\infounnumberedsec{\parsearg\unnumberedseczzz} +\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz} +\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz} + +\def\infoappendix{\parsearg\appendixzzz} +\def\infoappendixsec{\parsearg\appendixseczzz} +\def\infoappendixsubsec{\parsearg\appendixsubseczzz} +\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz} + +\def\infochapter{\parsearg\chapterzzz} +\def\infosection{\parsearg\sectionzzz} +\def\infosubsection{\parsearg\subsectionzzz} +\def\infosubsubsection{\parsearg\subsubsectionzzz} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and +% such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + + +\def\majorheading{\parsearg\majorheadingzzz} +\def\majorheadingzzz #1{% +{\advance\chapheadingskip by 10pt \chapbreak }% +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +\def\chapheading{\parsearg\chapheadingzzz} +\def\chapheadingzzz #1{\chapbreak % +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +\def\heading{\parsearg\secheadingi} + +\def\subheading{\parsearg\subsecheadingi} + +\def\subsubheading{\parsearg\subsubsecheadingi} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip \chapheadingskip \chapheadingskip = 30pt plus 8pt minus 4pt + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{ +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{ +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{ +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +\def\CHAPFplain{ +\global\let\chapmacro=\chfplain +\global\let\unnumbchapmacro=\unnchfplain} + +\def\chfplain #1#2{% + \pchapsepmacro + {% + \chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #2\enspace #1}% + }% + \bigskip + \penalty5000 +} + +\def\unnchfplain #1{% +\pchapsepmacro % +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 10000 % +} +\CHAPFplain % The default + +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 10000 % +} + +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} + +\def\CHAPFopen{ +\global\let\chapmacro=\chfopen +\global\let\unnumbchapmacro=\unnchfopen} + +% Parameter controlling skip before section headings. + +\newskip \subsecheadingskip \subsecheadingskip = 17pt plus 8pt minus 4pt +\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}} + +\newskip \secheadingskip \secheadingskip = 21pt plus 8pt minus 4pt +\def\secheadingbreak{\dobreak \secheadingskip {-1000}} + +% @paragraphindent is defined for the Info formatting commands only. +\let\paragraphindent=\comment + +% Section fonts are the base font at magstep2, which produces +% a size a bit more than 14 points in the default situation. + +\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}} +\def\plainsecheading #1{\secheadingi {#1}} +\def\secheadingi #1{{\advance \secheadingskip by \parskip % +\secheadingbreak}% +{\secfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% +\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 } + + +% Subsection fonts are the base font at magstep1, +% which produces a size of 12 points. + +\def\subsecheading #1#2#3#4{\subsecheadingi {#2.#3.#4\enspace #1}} +\def\subsecheadingi #1{{\advance \subsecheadingskip by \parskip % +\subsecheadingbreak}% +{\subsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% +\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 } + +\def\subsubsecfonts{\subsecfonts} % Maybe this should change: + % Perhaps make sssec fonts scaled + % magstep half +\def\subsubsecheading #1#2#3#4#5{\subsubsecheadingi {#2.#3.#4.#5\enspace #1}} +\def\subsubsecheadingi #1{{\advance \subsecheadingskip by \parskip % +\subsecheadingbreak}% +{\subsubsecfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% +\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000} + + +\message{toc printing,} + +% Finish up the main text and prepare to read what we've written +% to \contentsfile. + +\newskip\contentsrightmargin \contentsrightmargin=1in +\def\startcontents#1{% + \pagealignmacro + \immediate\closeout \contentsfile + \ifnum \pageno>0 + \pageno = -1 % Request roman numbered pages. + \fi + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \unnumbchapmacro{#1}\def\thischapter{}% + \begingroup % Set up to handle contents files properly. + \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 + \catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. +} + + +% Normal (long) toc. +\outer\def\contents{% + \startcontents{\putwordTableofContents}% + \input \jobname.toc + \endgroup + \vfill \eject +} + +% And just the chapters. +\outer\def\summarycontents{% + \startcontents{\putwordShortContents}% + % + \let\chapentry = \shortchapentry + \let\unnumbchapentry = \shortunnumberedentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl + \rm + \advance\baselineskip by 1pt % Open it up a little. + \def\secentry ##1##2##3##4{} + \def\unnumbsecentry ##1##2{} + \def\subsecentry ##1##2##3##4##5{} + \def\unnumbsubsecentry ##1##2{} + \def\subsubsecentry ##1##2##3##4##5##6{} + \def\unnumbsubsubsecentry ##1##2{} + \input \jobname.toc + \endgroup + \vfill \eject +} +\let\shortcontents = \summarycontents + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapter-level things, for both the long and short contents. +\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}} + +% See comments in \dochapentry re vbox and related settings +\def\shortchapentry#1#2#3{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}% +} + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter. +% We could simplify the code here by writing out an \appendixentry +% command in the toc file for appendices, instead of using \chapentry +% for both, but it doesn't seem worth it. +\setbox0 = \hbox{\shortcontrm \putwordAppendix } +\newdimen\shortappendixwidth \shortappendixwidth = \wd0 + +\def\shortchaplabel#1{% + % We typeset #1 in a box of constant width, regardless of the text of + % #1, so the chapter titles will come out aligned. + \setbox0 = \hbox{#1}% + \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi + % + % This space should be plenty, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in in \shortchapentry above.) + \advance\dimen0 by 1.1em + \hbox to \dimen0{#1\hfil}% +} + +\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}} +\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}} + +% Sections. +\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}} +\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}} + +% Subsections. +\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}} +\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}} + +% And subsubsections. +\def\subsubsecentry#1#2#3#4#5#6{% + \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}} +\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}} + + +% This parameter controls the indentation of the various levels. +\newdimen\tocindent \tocindent = 3pc + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we would want to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno{#2}}% + \endgroup + \nobreak\vskip .25\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +% Final typesetting of a toc entry; we use the same \entry macro as for +% the index entries, but we want to suppress hyphenation here. (We +% can't do that in the \entry macro, since index entries might consist +% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.) +% +\def\tocentry#1#2{\begingroup + \hyphenpenalty = 10000 + \entry{#1}{#2}% +\endgroup} + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\let\subsecentryfonts = \textfonts +\let\subsubsecentryfonts = \textfonts + + +\message{environments,} + +% Since these characters are used in examples, it should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% Furthermore, these definitions must come after we define our fonts. +\newbox\dblarrowbox \newbox\longdblarrowbox +\newbox\pushcharbox \newbox\bullbox +\newbox\equivbox \newbox\errorbox + +\let\ptexequiv = \equiv + +%{\tentt +%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil} +%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil} +%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil} +%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil} +% Adapted from the manmac format (p.420 of TeXbook) +%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex +% depth .1ex\hfil} +%} + +\def\point{$\star$} + +\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} + +\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% Adapted from the TeXbook's \boxit. +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt} + +\global\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{ + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} + +% The @error{} command. +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\def\tex{\begingroup +\catcode `\\=0 \catcode `\{=1 \catcode `\}=2 +\catcode `\$=3 \catcode `\&=4 \catcode `\#=6 +\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie +\catcode `\%=14 +\catcode 43=12 +\catcode`\"=12 +\catcode`\==12 +\catcode`\|=12 +\catcode`\<=12 +\catcode`\>=12 +\escapechar=`\\ +% +\let\~=\ptextilde +\let\{=\ptexlbrace +\let\}=\ptexrbrace +\let\.=\ptexdot +\let\*=\ptexstar +\let\dots=\ptexdots +\def\@{@}% +\let\bullet=\ptexbullet +\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext \let\l=\ptexl +\let\L=\ptexL +% +\let\Etex=\endgroup} + +% Define @lisp ... @endlisp. +% @lisp does a \begingroup so it can rebind things, +% including the definition of @endlisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% Make each space character in the input produce a normal interword +% space in the output. Don't allow a line break at this space, as this +% is used only in environments like @example, where each line of input +% should produce a line of output anyway. +% +{\obeyspaces % +\gdef\sepspaces{\obeyspaces\let =\tie}} + +% Define \obeyedspace to be our active space, whatever it is. This is +% for use in \parsearg. +{\sepspaces% +\global\let\obeyedspace= } + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip +% +\def\aboveenvbreak{{\advance\envskipamount by \parskip +\endgraf \ifdim\lastskip<\envskipamount +\removelastskip \penalty-50 \vskip\envskipamount \fi}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins. +\let\nonarrowing=\relax + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \cartouche: draw rectangle w/rounded corners around argument +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\long\def\cartouche{% +\begingroup + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt %we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18pt % allow for 3pt kerns on either +% side, and for 6pt waste from +% each corner char + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing=\comment + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \hsize=\cartinner + \kern3pt + \begingroup + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip +\def\Ecartouche{% + \endgroup + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup +\endgroup +}} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\def\nonfillstart{% + \aboveenvbreak + \inENV % This group ends at the end of the body + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \singlespace + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + \parindent = 0pt + \emergencystretch = 0pt % don't try to avoid overfull boxes + % @cartouche defines \nonarrowing to inhibit narrowing + % at next level down. + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \let\exdent=\nofillexdent + \let\nonarrowing=\relax + \fi +} + +% To ending an @example-like environment, we first end the paragraph +% (via \afterenvbreak's vertical glue), and then the group. That way we +% keep the zero \parskip that the environments set -- \parskip glue +% will be inserted at the beginning of the next paragraph in the +% document, after the environment. +% +\def\nonfillfinish{\afterenvbreak\endgroup}% + +% This macro is +\def\lisp{\begingroup + \nonfillstart + \let\Elisp = \nonfillfinish + \tt + \rawbackslash % have \ input char produce \ char from current font + \gobble +} + +% Define the \E... control sequence only if we are inside the +% environment, so the error checking in \end will work. +% +% We must call \lisp last in the definition, since it reads the +% return following the @example (or whatever) command. +% +\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp} +\def\smallexample{\begingroup \def\Esmallexample{\nonfillfinish\endgroup}\lisp} +\def\smalllisp{\begingroup \def\Esmalllisp{\nonfillfinish\endgroup}\lisp} + +% @smallexample and @smalllisp. This is not used unless the @smallbook +% command is given. Originally contributed by Pavel@xerox. +% +\def\smalllispx{\begingroup + \nonfillstart + \let\Esmalllisp = \nonfillfinish + \let\Esmallexample = \nonfillfinish + % + % Smaller interline space and fonts for small examples. + \setleading{10pt}% + \indexfonts \tt + \rawbackslash % make \ output the \ character from the current font (tt) + \gobble +} + +% This is @display; same as @lisp except use roman font. +% +\def\display{\begingroup + \nonfillstart + \let\Edisplay = \nonfillfinish + \gobble +} + +% This is @format; same as @display except don't narrow margins. +% +\def\format{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eformat = \nonfillfinish + \gobble +} + +% @flushleft (same as @format) and @flushright. +% +\def\flushleft{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eflushleft = \nonfillfinish + \gobble +} +\def\flushright{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eflushright = \nonfillfinish + \advance\leftskip by 0pt plus 1fill + \gobble} + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. +% +\def\quotation{% + \begingroup\inENV %This group ends at the end of the @quotation body + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \singlespace + \parindent=0pt + % We have retained a nonzero parskip for the environment, since we're + % doing normal filling. So to avoid extra space below the environment... + \def\Equotation{\parskip = 0pt \nonfillfinish}% + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \let\nonarrowing = \relax + \fi +} + +\message{defuns,} +% Define formatter for defuns +% First, allow user to change definition object font (\df) internally +\def\setdeffont #1 {\csname DEF#1\endcsname} + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deftypemargin \deftypemargin=12pt +\newskip\deflastargmargin \deflastargmargin=18pt + +\newcount\parencount +% define \functionparens, which makes ( and ) and & do special things. +% \functionparens affects the group it is contained in. +\def\activeparens{% +\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active +\catcode`\[=\active \catcode`\]=\active} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +{\activeparens % Now, smart parens don't turn on until &foo (see \amprm) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +\global\let(=\lparen \global\let)=\rparen +\global\let[=\lbrack \global\let]=\rbrack + +\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 } +\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + +% Definitions of (, ) and & used in args for functions. +% This is the definition of ( outside of all parentheses. +\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested % +\global\advance\parencount by 1 } +% +% This is the definition of ( when already inside a level of parens. +\gdef\opnested{\char`\(\global\advance\parencount by 1 } +% +\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0. +% also in that case restore the outer-level definition of (. +\ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi +\global\advance \parencount by -1 } +% If we encounter &foo, then turn on ()-hacking afterwards +\gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ } +% +\gdef\normalparens{\boldbrax\let&=\ampnr} +} % End of definition inside \activeparens +%% These parens (in \boldbrax) actually are a little bolder than the +%% contained text. This is especially needed for [ and ] +\def\opnr{{\sf\char`\(}} \def\clnr{{\sf\char`\)}} \def\ampnr{\&} +\def\lbrb{{\bf\char`\[}} \def\rbrb{{\bf\char`\]}} + +% First, defname, which formats the header line itself. +% #1 should be the function name. +% #2 should be the type of definition, such as "Function". + +\def\defname #1#2{% +% Get the values of \leftskip and \rightskip as they were +% outside the @def... +\dimen2=\leftskip +\advance\dimen2 by -\defbodyindent +\dimen3=\rightskip +\advance\dimen3 by -\defbodyindent +\noindent % +\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}% +\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line +\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations +\parshape 2 0in \dimen0 \defargsindent \dimen1 % +% Now output arg 2 ("Function" or some such) +% ending at \deftypemargin from the right margin, +% but stuck inside a box of width 0 so it does not interfere with linebreaking +{% Adjust \hsize to exclude the ambient margins, +% so that \rightline will obey them. +\advance \hsize by -\dimen2 \advance \hsize by -\dimen3 +\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}}% +% Make all lines underfull and no complaints: +\tolerance=10000 \hbadness=10000 +\advance\leftskip by -\defbodyindent +\exdentamount=\defbodyindent +{\df #1}\enskip % Generate function name +} + +% Actually process the body of a definition +% #1 should be the terminating control sequence, such as \Edefun. +% #2 should be the "another name" control sequence, such as \defunx. +% #3 should be the control sequence that actually processes the header, +% such as \defunheader. + +\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\activeparens\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % 61 is `=' +\obeylines\activeparens\spacesplit#3} + +\def\defmethparsebody #1#2#3#4 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#4}}} + +\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#5}}} + +% These parsing functions are similar to the preceding ones +% except that they do not make parens into active characters. +% These are used for "variables" since they have no arguments. + +\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % +\obeylines\spacesplit#3} + +% This is used for \def{tp,vr}parsebody. It could probably be used for +% some of the others, too, with some judicious conditionals. +% +\def\parsebodycommon#1#2#3{% + \begingroup\inENV % + \medbreak % + % Define the end token that this defining construct specifies + % so that it will exit this group. + \def#1{\endgraf\endgroup\medbreak}% + \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% + \parindent=0in + \advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent + \exdentamount=\defbodyindent + \begingroup\obeylines +} + +\def\defvrparsebody#1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{#3{#4}}% +} + +% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the +% type is just `struct', because we lose the braces in `{struct +% termios}' when \spacesplit reads its undelimited argument. Sigh. +% \let\deftpparsebody=\defvrparsebody +% +% So, to get around this, we put \empty in with the type name. That +% way, TeX won't find exactly `{...}' as an undelimited argument, and +% won't strip off the braces. +% +\def\deftpparsebody #1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{\parsetpheaderline{#3{#4}}}\empty +} + +% Fine, but then we have to eventually remove the \empty *and* the +% braces (if any). That's what this does, putting the result in \tptemp. +% +\def\removeemptybraces\empty#1\relax{\def\tptemp{#1}}% + +% After \spacesplit has done its work, this is called -- #1 is the final +% thing to call, #2 the type name (which starts with \empty), and #3 +% (which might be empty) the arguments. +% +\def\parsetpheaderline#1#2#3{% + \removeemptybraces#2\relax + #1{\tptemp}{#3}% +}% + +\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\spacesplit{#3{#5}}} + +% Split up #2 at the first space token. +% call #1 with two arguments: +% the first is all of #2 before the space token, +% the second is all of #2 after that space token. +% If #2 contains no space token, all of it is passed as the first arg +% and the second is passed as empty. + +{\obeylines +\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}% +\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{% +\ifx\relax #3% +#1{#2}{}\else #1{#2}{#3#4}\fi}} + +% So much for the things common to all kinds of definitions. + +% Define @defun. + +% First, define the processing that is wanted for arguments of \defun +% Use this to expand the args and terminate the paragraph they make up + +\def\defunargs #1{\functionparens \sl +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +\hyphenchar\tensl=0 +#1% +\hyphenchar\tensl=45 +\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi% +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\penalty 10000\vskip -\parskip\penalty 10000% +} + +\def\deftypefunargs #1{% +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +\functionparens +\tclose{#1}% avoid \code because of side effects on active chars +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\penalty 10000\vskip -\parskip\penalty 10000% +} + +% Do complete processing of one @defun or @defunx line already parsed. + +% @deffn Command forward-char nchars + +\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader} + +\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}% +\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defun == @deffn Function + +\def\defun{\defparsebody\Edefun\defunx\defunheader} + +\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Function}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefun int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader} + +% #1 is the data type. #2 is the name and args. +\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax} +% #1 is the data type, #2 the name, #3 the args. +\def\deftypefunheaderx #1#2 #3\relax{% +\doind {fn}{\code{#2}}% Make entry in function index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Function}% +\deftypefunargs {#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader} + +% \defheaderxcond#1\relax$$$ +% puts #1 in @code, followed by a space, but does nothing if #1 is null. +\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi} + +% #1 is the classification. #2 is the data type. #3 is the name and args. +\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax} +% #1 is the classification, #2 the data type, #3 the name, #4 the args. +\def\deftypefnheaderx #1#2#3 #4\relax{% +\doind {fn}{\code{#3}}% Make entry in function index +\begingroup +\normalparens % notably, turn off `&' magic, which prevents +% at least some C++ text from working +\defname {\defheaderxcond#2\relax$$$#3}{#1}% +\deftypefunargs {#4}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defmac == @deffn Macro + +\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader} + +\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Macro}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defspec == @deffn Special Form + +\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader} + +\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Special Form}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% This definition is run if you use @defunx +% anywhere other than immediately after a @defun or @defunx. + +\def\deffnx #1 {\errmessage{@deffnx in invalid context}} +\def\defunx #1 {\errmessage{@defunx in invalid context}} +\def\defmacx #1 {\errmessage{@defmacx in invalid context}} +\def\defspecx #1 {\errmessage{@defspecx in invalid context}} +\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}} +\def\deftypeunx #1 {\errmessage{@deftypeunx in invalid context}} + +% @defmethod, and so on + +% @defop {Funny Method} foo-class frobnicate argument + +\def\defop #1 {\def\defoptype{#1}% +\defopparsebody\Edefop\defopx\defopheader\defoptype} + +\def\defopheader #1#2#3{% +\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index +\begingroup\defname {#2}{\defoptype{} on #1}% +\defunargs {#3}\endgroup % +} + +% @defmethod == @defop Method + +\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader} + +\def\defmethodheader #1#2#3{% +\dosubind {fn}{\code{#2}}{on #1}% entry in function index +\begingroup\defname {#2}{Method on #1}% +\defunargs {#3}\endgroup % +} + +% @defcv {Class Option} foo-class foo-flag + +\def\defcv #1 {\def\defcvtype{#1}% +\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype} + +\def\defcvarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{\defcvtype{} of #1}% +\defvarargs {#3}\endgroup % +} + +% @defivar == @defcv {Instance Variable} + +\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader} + +\def\defivarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{Instance Variable of #1}% +\defvarargs {#3}\endgroup % +} + +% These definitions are run if you use @defmethodx, etc., +% anywhere other than immediately after a @defmethod, etc. + +\def\defopx #1 {\errmessage{@defopx in invalid context}} +\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}} +\def\defcvx #1 {\errmessage{@defcvx in invalid context}} +\def\defivarx #1 {\errmessage{@defivarx in invalid context}} + +% Now @defvar + +% First, define the processing that is wanted for arguments of @defvar. +% This is actually simple: just print them in roman. +% This must expand the args and terminate the paragraph they make up +\def\defvarargs #1{\normalparens #1% +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000} + +% @defvr Counter foo-count + +\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader} + +\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}% +\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup} + +% @defvar == @defvr Variable + +\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader} + +\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{Variable}% +\defvarargs {#2}\endgroup % +} + +% @defopt == @defvr {User Option} + +\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader} + +\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{User Option}% +\defvarargs {#2}\endgroup % +} + +% @deftypevar int foobar + +\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader} + +% #1 is the data type. #2 is the name. +\def\deftypevarheader #1#2{% +\doind {vr}{\code{#2}}% Make entry in variables index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Variable}% +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000 +\endgroup} + +% @deftypevr {Global Flag} int enable + +\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader} + +\def\deftypevrheader #1#2#3{\doind {vr}{\code{#3}}% +\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1} +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000 +\endgroup} + +% This definition is run if you use @defvarx +% anywhere other than immediately after a @defvar or @defvarx. + +\def\defvrx #1 {\errmessage{@defvrx in invalid context}} +\def\defvarx #1 {\errmessage{@defvarx in invalid context}} +\def\defoptx #1 {\errmessage{@defoptx in invalid context}} +\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}} +\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}} + +% Now define @deftp +% Args are printed in bold, a slight difference from @defvar. + +\def\deftpargs #1{\bf \defvarargs{#1}} + +% @deftp Class window height width ... + +\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader} + +\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}% +\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup} + +% This definition is run if you use @deftpx, etc +% anywhere other than immediately after a @deftp, etc. + +\def\deftpx #1 {\errmessage{@deftpx in invalid context}} + +\message{cross reference,} +% Define cross-reference macros +\newwrite \auxfile + +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% \setref{foo} defines a cross-reference point named foo. + +\def\setref#1{% +\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Ysectionnumberandtype}} + +\def\unnumbsetref#1{% +\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Ynothing}} + +\def\appendixsetref#1{% +\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Yappendixletterandtype}} + +% \xref, \pxref, and \ref generate cross-references to specified points. +% For \xrefX, #1 is the node name, #2 the name of the Info +% cross-reference, #3 the printed node name, #4 the name of the Info +% file, #5 the name of the printed manual. All but the node name can be +% omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \def\printedmanual{\ignorespaces #5}% + \def\printednodename{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual}% + \setbox0=\hbox{\printednodename}% + \ifdim \wd0 = 0pt + % No printed node name was explicitly given. + \ifx\SETxref-automatic-section-title\relax % + % Use the actual chapter/section title appear inside + % the square brackets. Use the real section title if we have it. + \ifdim \wd1>0pt% + % It is in another manual, so we don't have it. + \def\printednodename{\ignorespaces #1}% + \else + \ifhavexrefs + % We know the real title if we have the xref values. + \def\printednodename{\refx{#1-title}}% + \else + % Otherwise just copy the Info node name. + \def\printednodename{\ignorespaces #1}% + \fi% + \fi + \def\printednodename{#1-title}% + \else + % Use the node name inside the square brackets. + \def\printednodename{\ignorespaces #1}% + \fi + \fi + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifdim \wd1 > 0pt + \putwordsection{} ``\printednodename'' in \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive \refx{#1-snt}{}}% + \space [\printednodename],\space + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi +\endgroup} + +% \dosetq is the interface for calls from other macros + +% Use \turnoffactive so that punctuation chars such as underscore +% work in node names. +\def\dosetq #1#2{{\let\folio=0 \turnoffactive% +\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}% +\next}} + +% \internalsetq {foo}{page} expands into +% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...} +% When the aux file is read, ' is the escape character + +\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}} + +% Things to be expanded by \internalsetq + +\def\Ypagenumber{\folio} + +\def\Ytitle{\thissection} + +\def\Ynothing{} + +\def\Ysectionnumberandtype{% +\ifnum\secno=0 \putwordChapter\xreftie\the\chapno % +\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\def\Yappendixletterandtype{% +\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}% +\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\gdef\xreftie{'tie} + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Non-3.0. +\else + \def\linenumber{\the\inputlineno:\space} +\fi + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. + +\def\refx#1#2{% + \expandafter\ifx\csname X#1\endcsname\relax + % If not defined, say something at least. + $\langle$un\-de\-fined$\rangle$% + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \else + % It's defined, so just use it. + \csname X#1\endcsname + \fi + #2% Output the suffix in any case. +} + +% Read the last existing aux file, if any. No error if none exists. + +% This is the macro invoked by entries in the aux file. +\def\xrdef #1#2{ +{\catcode`\'=\other\expandafter \gdef \csname X#1\endcsname {#2}}} + +\def\readauxfile{% +\begingroup +\catcode `\^^@=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\^^C=\other +\catcode `\^^D=\other +\catcode `\^^E=\other +\catcode `\^^F=\other +\catcode `\^^G=\other +\catcode `\^^H=\other +\catcode `\ =\other +\catcode `\^^L=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode `\=\other +\catcode 26=\other +\catcode `\^^[=\other +\catcode `\^^\=\other +\catcode `\^^]=\other +\catcode `\^^^=\other +\catcode `\^^_=\other +\catcode `\@=\other +\catcode `\^=\other +\catcode `\~=\other +\catcode `\[=\other +\catcode `\]=\other +\catcode`\"=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode `\$=\other +\catcode `\#=\other +\catcode `\&=\other +% `\+ does not work, so use 43. +\catcode 43=\other +% Make the characters 128-255 be printing characters +{% + \count 1=128 + \def\loop{% + \catcode\count 1=\other + \advance\count 1 by 1 + \ifnum \count 1<256 \loop \fi + }% +}% +% the aux file uses ' as the escape. +% Turn off \ as an escape so we do not lose on +% entries which were dumped with control sequences in their names. +% For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^ +% Reference to such entries still does not work the way one would wish, +% but at least they do not bomb out when the aux file is read in. +\catcode `\{=1 \catcode `\}=2 +\catcode `\%=\other +\catcode `\'=0 +\catcode `\\=\other +\openin 1 \jobname.aux +\ifeof 1 \else \closein 1 \input \jobname.aux \global\havexrefstrue +\global\warnedobstrue +\fi +% Open the new aux file. Tex will close it automatically at exit. +\openout \auxfile=\jobname.aux +\endgroup} + + +% Footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only.. +\let\footnotestyle=\comment + +\let\ptexfootnote=\footnote + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \footnotezzz +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +\long\gdef\footnotezzz#1{\insert\footins{% + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + % Hang the footnote text off the number. + \hang + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + #1\strut}% +} + +}%end \catcode `\@=11 + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +\def\setleading#1{% + \normalbaselineskip = #1\relax + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + + +% End of control word definitions. + +\message{and turning on texinfo input format.} + +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% Set some numeric style parameters, for 8.5 x 11 format. + +%\hsize = 6.5in +\newdimen\defaultparindent \defaultparindent = 15pt +\parindent = \defaultparindent +\parskip 18pt plus 1pt +\setleading{15pt} +\advance\topskip by 1.2cm + +% Prevent underfull vbox error messages. +\vbadness=10000 + +% Following George Bush, just get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. This makes it come to about 9pt for the 8.5x11 format. +% +\ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% +\else + \emergencystretch = \hsize + \divide\emergencystretch by 45 +\fi + +% Use @smallbook to reset parameters for 7x9.5 format (or else 7x9.25) +\def\smallbook{ + +% These values for secheadingskip and subsecheadingskip are +% experiments. RJC 7 Aug 1992 +\global\secheadingskip = 17pt plus 6pt minus 3pt +\global\subsecheadingskip = 14pt plus 6pt minus 3pt + +\global\lispnarrowing = 0.3in +\setleading{12pt} +\advance\topskip by -1cm +\global\parskip 3pt plus 1pt +\global\hsize = 5in +\global\vsize=7.5in +\global\tolerance=700 +\global\hfuzz=1pt +\global\contentsrightmargin=0pt +\global\deftypemargin=0pt +\global\defbodyindent=.5cm + +\global\pagewidth=\hsize +\global\pageheight=\vsize + +\global\let\smalllisp=\smalllispx +\global\let\smallexample=\smalllispx +\global\def\Esmallexample{\Esmalllisp} +} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{ +\global\tolerance=700 +\global\hfuzz=1pt +\setleading{12pt} +\global\parskip 15pt plus 1pt + +\global\vsize= 53\baselineskip +\advance\vsize by \topskip +%\global\hsize= 5.85in % A4 wide 10pt +\global\hsize= 6.5in +\global\outerhsize=\hsize +\global\advance\outerhsize by 0.5in +\global\outervsize=\vsize +\global\advance\outervsize by 0.6in + +\global\pagewidth=\hsize +\global\pageheight=\vsize +} + +% Allow control of the text dimensions. Parameters in order: textheight; +% textwidth; \voffset; \hoffset (!); binding offset. All require a dimension; +% header is additional; added length extends the bottom of the page. + +\def\changepagesizes#1#2#3#4#5 +{\global\vsize= #1 + \advance\vsize by \topskip + \global\voffset= #3 + \global\hsize= #2 + \global\outerhsize=\hsize + \global\advance\outerhsize by 0.5in + \global\outervsize=\vsize + \global\advance\outervsize by 0.6in + \global\pagewidth=\hsize + \global\pageheight=\vsize + \global\normaloffset= #4 + \global\bindingoffset= #5} + +% This layout is compatible with Latex on A4 paper. + +\def\afourlatex{\changepagesizes{22cm}{15cm}{7mm}{4.6mm}{5mm}} + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} + +% This macro is used to make a character print one way in ttfont +% where it can probably just be output, and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt \char '042}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt \char '176}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +% Subroutine for the previous macro. +\def\_{\lvvmode \kern.06em \vbox{\hrule width.3em height.1ex}} + +% \lvvmode is equivalent in function to \leavevmode. +% Using \leavevmode runs into trouble when written out to +% an index file due to the expansion of \leavevmode into ``\unhbox +% \voidb@x'' ---which looks to TeX like ``\unhbox \voidb\x'' due to our +% magic tricks with @. +\def\lvvmode{\vbox to 0pt{}} + +\catcode`\|=\active +\def|{{\tt \char '174}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +%\catcode 27=\active +%\def^^[{$\diamondsuit$} + +% Set up an active definition for =, but don't enable it most of the time. +{\catcode`\==\active +\global\def={{\tt \char 61}}} + +\catcode`\@=0 + +% \rawbackslashxx output one backslash character in current font +\global\chardef\rawbackslashxx=`\\ +%{\catcode`\\=\other +%@gdef@rawbackslashxx{\}} + +% \rawbackslash redefines \ as input to do \rawbackslashxx. +{\catcode`\\=\active +@gdef@rawbackslash{@let\=@rawbackslashxx }} + +% \normalbackslash outputs one backslash in fixed width font. +\def\normalbackslash{{\tt\rawbackslashxx}} + +% Say @foo, not \foo, in error messages. +\escapechar=`\@ + +% \catcode 17=0 % Define control-q +\catcode`\\=\active + +% Used sometimes to turn off (effectively) the active characters +% even after parsing them. +@def@turnoffactive{@let"=@normaldoublequote +@let\=@realbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus} + +@def@normalturnoffactive{@let"=@normaldoublequote +@let\=@normalbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus} + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\{ in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% +@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi} + +%% These look ok in all fonts, so just make them not special. The @rm below +%% makes sure that the current font starts out as the newly loaded cmr10 +@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other + +@textfonts +@rm + +@c Local variables: +@c page-delimiter: "^\\\\message" +@c End: diff --git a/find/Makefile.am b/find/Makefile.am new file mode 100644 index 0000000..d8096cd --- /dev/null +++ b/find/Makefile.am @@ -0,0 +1,14 @@ +PROGRAMS = find +find_SOURCES = find.c fstype.c parser.c pred.c tree.c util.c version.c +DIST_OTHER = defs.h +INCLUDES = -I.. -I$(top_srcdir)/lib +LDADD = ../lib/libfind.a +MANS = find.1 +CONFIG_HEADER = ../config.h + +$(PROGRAMS): ../lib/libfind.a + +parser.o: ../lib/modechange.h +find.o fstype.o parser.o pred.o: ../lib/modetype.h +find.o fstype.o parser.o pred.o tree.o util.o: defs.h +pred.o: ../lib/wait.h diff --git a/find/Makefile.in b/find/Makefile.in new file mode 100644 index 0000000..1f658cb --- /dev/null +++ b/find/Makefile.in @@ -0,0 +1,160 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +CC = @CC@ +LEX = @LEX@ +YACC = @YACC@ +ANSI2KNR = ./ansi2knr + +DEFS = @DEFS@ +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +find_OBJECTS = find.o fstype.o parser.o pred.o tree.o util.o version.o +NROFF = nroff + +SOURCES = ${find_SOURCES} +DIST_CONF = Makefile.am Makefile.in +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +PROGRAMS = find +find_SOURCES = find.c fstype.c parser.c pred.c tree.c util.c version.c +DIST_OTHER = defs.h +INCLUDES = -I.. -I$(top_srcdir)/lib +LDADD = ../lib/libfind.a +MANS = find.1 +CONFIG_HEADER = ../config.h + +all:: ${ALL} + +.c.o: + $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $< + +$(find_OBJECTS): ../config.h +install:: install-programs + +install-programs: $(PROGRAMS) $(SCRIPTS) + $(top_srcdir)/mkinstalldirs $(bindir) + for p in $(PROGRAMS) $(SCRIPTS); do \ + $(INSTALL_PROGRAM) $$p $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +uninstall:: uninstall-programs + +uninstall-programs: + for p in $(PROGRAMS) $(SCRIPTS); do \ + rm -f $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +find: $(find_OBJECTS) + $(CC) -o $@ $(find_OBJECTS) $(LDADD) $(LDFLAGS) $(LIBS) + +install:: install-man + +install-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\.\([0-9][a-z]*\)$$%\1%'`; \ + inst=`basename $$man $$sect|sed '$(transform)'`$$sect; \ + mdir=$(mandir)/man$$sect; \ + $(top_srcdir)/mkinstalldirs $$mdir; \ + echo installing $$man as $$mdir/$$inst; \ + $(INSTALL_DATA) $(srcdir)/$$man $$mdir/$$inst; \ + cdir=$(mandir)/cat$$sect; \ + if test -d $$cdir; then \ + echo formatting $$man as $$cdir/$$inst; \ + $(NROFF) -man $(srcdir)/$$man > $$cdir/$$inst; \ + fi; \ + done + +uninstall:: uninstall-man + +uninstall-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\(\.[0-9][a-z]*\)$$%\1%'; \ + inst=`basename $$man $sect|sed '$(transform)'`.$$sect; \ + mdir=$(mandir)/man$$sect; \ + cdir=$(mandir)/cat$$sect; \ + rm -f $$mdir/$$inst $$cdir/$$inst; \ + done + +mostlyclean: + rm -f *.o core + +clean: mostlyclean + rm -f $(PROGRAMS) $(LIBPROGRAMS) $(LIBFILES) $(TEXFILES) $(CLEANFILES) + +distclean: clean + rm -f Makefile *.tab.c $(DISTCLEANFILES) + rm -f config.cache config.log config.status ${CONFIG_HEADER} stamp-h + +realclean: distclean + rm -f TAGS $(INFOS) + +dist: $(DIST_FILES) $(DIST_DIRS) + -mkdir ../`cat ../distname`/$(subdir) + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file;}; \ + done + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +$(PROGRAMS): ../lib/libfind.a + +parser.o: ../lib/modechange.h +find.o fstype.o parser.o pred.o: ../lib/modetype.h +find.o fstype.o parser.o pred.o tree.o util.o: defs.h +pred.o: ../lib/wait.h diff --git a/find/defs.h b/find/defs.h new file mode 100644 index 0000000..ec029de --- /dev/null +++ b/find/defs.h @@ -0,0 +1,332 @@ +/* defs.h -- data types and declarations. + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#ifndef strchr +#define strchr index +#endif +#ifndef strrchr +#define strrchr rindex +#endif +#endif + +#include +#ifndef errno +extern int errno; +#endif + +#ifdef STDC_HEADERS +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include "regex.h" + +#if __STDC__ +# define P_(s) s +#else +# define P_(s) () +#endif + +/* Not char because of type promotion; NeXT gcc can't handle it. */ +typedef int boolean; +#define true 1 +#define false 0 + +/* Pointer to function returning boolean. */ +typedef boolean (*PFB)(); + +/* The number of seconds in a day. */ +#define DAYSECS 86400 + +/* Argument structures for predicates. */ + +enum comparison_type +{ + COMP_GT, + COMP_LT, + COMP_EQ +}; + +enum predicate_type +{ + NO_TYPE, + PRIMARY_TYPE, + UNI_OP, + BI_OP, + OPEN_PAREN, + CLOSE_PAREN +}; + +enum predicate_precedence +{ + NO_PREC, + COMMA_PREC, + OR_PREC, + AND_PREC, + NEGATE_PREC, + MAX_PREC +}; + +struct long_val +{ + enum comparison_type kind; + unsigned long l_val; +}; + +struct size_val +{ + enum comparison_type kind; + int blocksize; + unsigned long size; +}; + +struct path_arg +{ + short offset; /* Offset in `vec' of this arg. */ + short count; /* Number of path replacements in this arg. */ + char *origarg; /* Arg with "{}" intact. */ +}; + +struct exec_val +{ + struct path_arg *paths; /* Array of args with path replacements. */ + char **vec; /* Array of args to pass to program. */ +}; + +/* The format string for a -printf or -fprintf is chopped into one or + more `struct segment', linked together into a list. + Each stretch of plain text is a segment, and + each \c and `%' conversion is a segment. */ + +/* Special values for the `kind' field of `struct segment'. */ +#define KIND_PLAIN 0 /* Segment containing just plain text. */ +#define KIND_STOP 1 /* \c -- stop printing and flush output. */ + +struct segment +{ + int kind; /* Format chars or KIND_{PLAIN,STOP}. */ + char *text; /* Plain text or `%' format string. */ + int text_len; /* Length of `text'. */ + struct segment *next; /* Next segment for this predicate. */ +}; + +struct format_val +{ + struct segment *segment; /* Linked list of segments. */ + FILE *stream; /* Output stream to print on. */ +}; + +struct predicate +{ + /* Pointer to the function that implements this predicate. */ + PFB pred_func; + + /* Only used for debugging, but defined unconditionally so individual + modules can be compiled with -DDEBUG. */ + char *p_name; + + /* The type of this node. There are two kinds. The first is real + predicates ("primaries") such as -perm, -print, or -exec. The + other kind is operators for combining predicates. */ + enum predicate_type p_type; + + /* The precedence of this node. Only has meaning for operators. */ + enum predicate_precedence p_prec; + + /* True if this predicate node produces side effects. */ + boolean side_effects; + + /* True if this predicate node requires a stat system call to execute. */ + boolean need_stat; + + /* Information needed by the predicate processor. + Next to each member are listed the predicates that use it. */ + union + { + char *str; /* fstype [i]lname [i]name [i]path */ + struct re_pattern_buffer *regex; /* regex */ + struct exec_val exec_vec; /* exec ok */ + struct long_val info; /* atime ctime mtime inum links */ + struct size_val size; /* size */ + uid_t uid; /* user */ + gid_t gid; /* group */ + time_t time; /* newer */ + unsigned long perm; /* perm */ + unsigned long type; /* type */ + FILE *stream; /* fprint fprint0 */ + struct format_val printf_vec; /* printf fprintf */ + } args; + + /* The next predicate in the user input sequence, + which repesents the order in which the user supplied the + predicates on the command line. */ + struct predicate *pred_next; + + /* The right and left branches from this node in the expression + tree, which represents the order in which the nodes should be + processed. */ + struct predicate *pred_left; + struct predicate *pred_right; +}; + +/* find library function declarations. */ + +/* dirname.c */ +char *dirname P_((char *path)); + +/* error.c */ +void error P_((int status, int errnum, char *message, ...)); + +/* listfile.c */ +void list_file P_((char *name, char *relname, struct stat *statp, FILE *stream)); +char *get_link_name P_((char *name, char *relname)); + +/* savedir.c */ +char *savedir P_((char *dir, unsigned name_size)); + +/* stpcpy.c */ +#if !HAVE_STPCPY +char *stpcpy P_((char *dest, const char *src)); +#endif + +/* xgetcwd.c */ +char *xgetcwd P_((void)); + +/* xmalloc.c */ +#if __STDC__ +#define VOID void +#else +#define VOID char +#endif + +VOID *xmalloc P_((size_t n)); +VOID *xrealloc P_((VOID *p, size_t n)); + +/* xstrdup.c */ +char *xstrdup P_((char *string)); + +/* find global function declarations. */ + +/* fstype.c */ +char *filesystem_type P_((char *path, char *relpath, struct stat *statp)); + +/* parser.c */ +PFB find_parser P_((char *search_name)); +boolean parse_close P_((char *argv[], int *arg_ptr)); +boolean parse_open P_((char *argv[], int *arg_ptr)); +boolean parse_print P_((char *argv[], int *arg_ptr)); + +/* pred.c */ +boolean pred_amin P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_and P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_anewer P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_atime P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_close P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_cmin P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_cnewer P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_comma P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ctime P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_empty P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_exec P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_false P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fls P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fprint P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fprint0 P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fprintf P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_fstype P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_gid P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_group P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ilname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_iname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_inum P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ipath P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_links P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_lname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ls P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_mmin P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_mtime P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_name P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_negate P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_newer P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_nogroup P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_nouser P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_ok P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_open P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_or P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_path P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_perm P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_print P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_print0 P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_prune P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_regex P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_size P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_true P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_type P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_uid P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_used P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_user P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_xtype P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +char *find_pred_name P_((PFB pred_func)); +#ifdef DEBUG +void print_tree P_((struct predicate *node, int indent)); +void print_list P_((struct predicate *node)); +#endif /* DEBUG */ + +/* tree.c */ +struct predicate *get_expr P_((struct predicate **input, int prev_prec)); +boolean opt_expr P_((struct predicate **eval_treep)); +boolean mark_stat P_((struct predicate *tree)); + +/* util.c */ +char *basename P_((char *fname)); +struct predicate *get_new_pred P_((void)); +struct predicate *get_new_pred_chk_op P_((void)); +struct predicate *insert_primary P_((boolean (*pred_func )())); +void usage P_((char *msg)); + +extern char *program_name; +extern struct predicate *predicates; +extern struct predicate *last_pred; +extern boolean do_dir_first; +extern int maxdepth; +extern int mindepth; +extern int curdepth; +extern time_t cur_day_start; +extern boolean full_days; +extern boolean no_leaf_check; +extern boolean stay_on_filesystem; +extern boolean stop_at_current_level; +extern boolean have_stat; +extern char *rel_pathname; +#ifndef HAVE_FCHDIR +extern char *starting_dir; +#else +extern int starting_desc; +#endif +extern int exit_status; +extern int path_length; +extern int (*xstat) (); +extern boolean dereference; diff --git a/find/find.1 b/find/find.1 new file mode 100644 index 0000000..d6280d4 --- /dev/null +++ b/find/find.1 @@ -0,0 +1,455 @@ +.TH FIND 1L \" -*- nroff -*- +.SH NAME +find \- search for files in a directory hierarchy +.SH SYNOPSIS +.B find +[path...] [expression] +.SH DESCRIPTION +This manual page +documents the GNU version of +.BR find . +.B find +searches the directory tree rooted at each given file name by +evaluating the given expression from left to right, according to the +rules of precedence (see section OPERATORS), until the outcome is +known (the left hand side is false for \fIand\fR operations, true for +\fIor\fR), at which point +.B find +moves on to the next file name. +.PP +The first argument that begins with `\-', `(', `)', `,', or `!' is taken +to be the beginning of the expression; any arguments before it are +paths to search, and any arguments after it are the rest of the +expression. If no paths are given, the current directory is used. If +no expression is given, the expression `\-print' is used. +.PP +.B find +exits with status 0 if all files are processed successfully, greater +than 0 if errors occur. +.SH EXPRESSIONS +.P +The expression is made up of options (which affect overall operation +rather than the processing of a specific file, and always return true), +tests (which return a true or false value), and actions (which have side +effects and return a true or false value), all separated by operators. +\-and is assumed where the operator is omitted. If the expression contains +no actions other than \-prune, \-print is performed on all files +for which the expression is true. +.SS OPTIONS +.P +All options always return true. They always take effect, rather than +being processed only when their place in the expression is reached. +Therefore, for clarity, it is best to place them at the beginning of +the expression. +.IP \-daystart +Measure times (for \-amin, \-atime, \-cmin, \-ctime, \-mmin, and \-mtime) +from the beginning of today rather than from 24 hours ago. +.IP \-depth +Process each directory's contents before the directory itself. +.IP \-follow +Dereference symbolic links. Implies \-noleaf. +.IP "\-help, \-\-help" +Print a summary of the command-line usage of +.B find +and exit. +.IP "\-maxdepth \fIlevels\fR" +Descend at most \fIlevels\fR (a non-negative integer) levels of +directories below the command line arguments. `\-maxdepth 0' means +only apply the tests and actions to the command line arguments. +.IP "\-mindepth \fIlevels\fR" +Do not apply any tests or actions at levels less than \fIlevels\fR (a +non-negative integer). `\-mindepth 1' means process all files except +the command line arguments. +.IP \-mount +Don't descend directories on other filesystems. An alternate name for +\-xdev, for compatibility with some other versions of +.BR find . +.IP "\-noleaf" +Do not optimize by assuming that directories contain 2 fewer +subdirectories than their hard link count. This option is needed when +searching filesystems that do not follow the Unix directory-link +convention, such as CD-ROM or MS-DOS filesystems or AFS volume mount +points. Each directory on a normal Unix filesystem has at least 2 +hard links: its name and its `.' entry. Additionally, its +subdirectories (if any) each have a `..' entry linked to that +directory. When +.B find +is examining a directory, after it has statted 2 fewer subdirectories +than the directory's link count, it knows that the rest of the entries +in the directory are non-directories (`leaf' files in the directory +tree). If only the files' names need to be examined, there is no need +to stat them; this gives a significant increase in search speed. +.IP "\-version, \-\-version" +Print the \fBfind\fR version number and exit. +.IP \-xdev +Don't descend directories on other filesystems. +.SS TESTS +.P +Numeric arguments can be specified as +.IP \fI+n\fP +for greater than +.IR n , +.IP \fI\-n\fP +for less than +.IR n , +.IP \fIn\fP +for exactly +.IR n . +.IP "\-amin \fIn\fR" +File was last accessed \fIn\fR minutes ago. +.IP "\-anewer \fIfile\fR" +File was last accessed more recently than \fIfile\fR was modified. +\-anewer is affected by \-follow only if \-follow comes before +\-anewer on the command line. +.IP "\-atime \fIn\fR" +File was last accessed \fIn\fR*24 hours ago. +.IP "\-cmin \fIn\fR" +File's status was last changed \fIn\fR minutes ago. +.IP "\-cnewer \fIfile\fR" +File's status was last changed more recently than \fIfile\fR was modified. +\-cnewer is affected by \-follow only if \-follow comes before +\-cnewer on the command line. +.IP "\-ctime \fIn\fR" +File's status was last changed \fIn\fR*24 hours ago. +.IP \-empty +File is empty and is either a regular file or a directory. +.IP \-false +Always false. +.IP "\-fstype \fItype\fR" +File is on a filesystem of type \fItype\fR. The valid filesystem +types vary among different versions of Unix; an incomplete list of +filesystem types that are accepted on some version of Unix or another +is: ufs, 4.2, 4.3, nfs, tmp, mfs, S51K, S52K. You can use \-printf +with the %F directive to see the types of your filesystems. +.IP "\-gid \fIn\fR" +File's numeric group ID is \fIn\fR. +.IP "\-group \fIgname\fR" +File belongs to group \fIgname\fR (numeric group ID allowed). +.IP "\-ilname \fIpattern\fR" +Like \-lname, but the match is case insensitive. +.IP "\-iname \fIpattern\fR" +Like \-name, but the match is case insensitive. For example, the +patterns `fo*' and `F??' match the file names `Foo', `FOO', `foo', +`fOo', etc. +.IP "\-inum \fIn\fR" +File has inode number \fIn\fR. +.IP "\-ipath \fIpattern\fR" +Like \-path, but the match is case insensitive. +.IP "\-iregex \fIpattern\fR" +Like \-regex, but the match is case insensitive. +.IP "\-links \fIn\fR" +File has \fIn\fR links. +.IP "\-lname \fIpattern\fR" +File is a symbolic link whose contents match shell pattern +\fIpattern\fR. The metacharacters do not treat `/' or `.' specially. +.IP "\-mmin \fIn\fR" +File's data was last modified \fIn\fR minutes ago. +.IP "\-mtime \fIn\fR" +File's data was last modified \fIn\fR*24 hours ago. +.IP "\-name \fIpattern\fR" +Base of file name (the path with the leading directories removed) +matches shell pattern \fIpattern\fR. The metacharacters (`*', `?', +and `[]') do not match a `.' at the start of the base name. To ignore +a directory and the files under it, use \-prune; see an example in the +description of \-path. +.IP "\-newer \fIfile\fR" +File was modified more recently than \fIfile\fR. +\-newer is affected by \-follow only if \-follow comes before +\-newer on the command line. +.IP \-nouser +No user corresponds to file's numeric user ID. +.IP \-nogroup +No group corresponds to file's numeric group ID. +.IP "\-path \fIpattern\fR" +File name matches shell pattern \fIpattern\fR. The metacharacters do +not treat `/' or `.' specially; so, for example, +.br +.in +1i +find . \-path './sr*sc' +.br +.in -1i +will print an entry for a directory called './src/misc' (if one +exists). To ignore a whole directory tree, use \-prune rather than +checking every file in the tree. For example, to skip the +directory `src/emacs' and all files and directories under it, and +print the names of the other files found, do something like this: +.br +.in +1i +find . \-path './src/emacs' -prune -o -print +.br +.in -1i +.IP "\-perm \fImode\fR" +File's permission bits are exactly \fImode\fR (octal or symbolic). +Symbolic modes use mode 0 as a point of departure. +.IP "\-perm \-\fImode\fR" +All of the permission bits \fImode\fR are set for the file. +.IP "\-perm +\fImode\fR" +Any of the permission bits \fImode\fR are set for the file. +.IP "\-regex \fIpattern\fR" +File name matches regular expression \fIpattern\fR. This is a match +on the whole path, not a search. For example, to match a file named +`./fubar3', you can use the regular expression `.*bar.' or `.*b.*3', +but not `b.*r3'. +.IP "\-size \fIn\fR[bckw]" +File uses \fIn\fP units of space. The units are 512-byte blocks by +default or if `b' follows \fIn\fP, bytes if `c' follows \fIn\fP, +kilobytes if `k' follows \fIn\fP, or 2-byte words if `w' follows \fIn\fP. +The size does not count indirect blocks, but it does count blocks in +sparse files that are not actually allocated. +.IP \-true +Always true. +.IP "\-type \fIc\fR" +File is of type \fIc\fR: +.RS +.IP b +block (buffered) special +.IP c +character (unbuffered) special +.IP d +directory +.IP p +named pipe (FIFO) +.IP f +regular file +.IP l +symbolic link +.IP s +socket +.RE +.IP "\-uid \fIn\fR" +File's numeric user ID is \fIn\fR. +.IP "\-used \fIn\fR" +File was last accessed \fIn\fR days after its status was last changed. +.IP "\-user \fIuname\fR" +File is owned by user \fIuname\fR (numeric user ID allowed). +.IP "\-xtype \fIc\fR" +The same as \-type unless the file is a symbolic link. For symbolic +links: if \-follow has not been given, true if the file is a link to a +file of type \fIc\fR; if \-follow has been given, true if \fIc\fR is +`l'. In other words, for symbolic links, \-xtype checks the type of +the file that \-type does not check. +.SS ACTIONS +.IP "\-exec \fIcommand\fR ;" +Execute \fIcommand\fR; true if 0 status is returned. All following +arguments to +.B find +are taken to be arguments to the command until an argument consisting +of `;' is encountered. The string `{}' is replaced by the current +file name being processed everywhere it occurs in the arguments to the +command, not just in arguments where it is alone, as in some versions +of +.BR find . +Both of these constructions might need to be escaped (with a `\e') or +quoted to protect them from expansion by the shell. The command is +executed in the starting directory. +.IP "\-fls \fIfile\fR" +True; like \-ls but write to \fIfile\fR like \-fprint. +.IP "\-fprint \fIfile\fR" +True; print the full file name into file \fIfile\fR. If \fIfile\fR +does not exist when \fBfind\fR is run, it is created; if it does +exist, it is truncated. The file names ``/dev/stdout'' and +``/dev/stderr'' are handled specially; they refer to the standard +output and standard error output, respectively. +.IP "\-fprint0 \fIfile\fR" +True; like \-print0 but write to \fIfile\fR like \-fprint. +.IP "\-fprintf \fIfile\fR \fIformat\fR" +True; like \-printf but write to \fIfile\fR like \-fprint. +.IP "\-ok \fIcommand\fR ;" +Like \-exec but ask the user first (on the standard input); if the +response does not start with `y' or `Y', do not run the command, and +return false. +.IP \-print +True; print the full file name on the standard output, followed by a newline. +.IP \-print0 +True; print the full file name on the standard output, followed by a +null character. This allows file names that contain newlines to be +correctly interpreted by programs that process the \fBfind\fR output. +.IP "\-printf \fIformat\fR" +True; print \fIformat\fR on the standard output, interpreting `\e' +escapes and `%' directives. Field widths and precisions can be +specified as with the `printf' C function. Unlike \-print, \-printf +does not add a newline at the end of the string. The escapes and +directives are: +.RS +.IP \ea +Alarm bell. +.IP \eb +Backspace. +.IP \ec +Stop printing from this format immediately and flush the output. +.IP \ef +Form feed. +.IP \en +Newline. +.IP \er +Carriage return. +.IP \et +Horizontal tab. +.IP \ev +Vertical tab. +.IP \e\e +A literal backslash (`\e'). +.PP +A `\e' character followed by any other character is treated as an +ordinary character, so they both are printed. +.IP %% +A literal percent sign. +.IP %a +File's last access time in the format returned by the C `ctime' function. +.IP %A\fIk\fP +File's last access time in the format specified by \fIk\fR, which is +either `@' or a directive for the C `strftime' function. The possible +values for \fIk\fR are listed below; some of them might not be +available on all systems, due to differences in `strftime' between +systems. +.RS +.IP @ +seconds since Jan. 1, 1970, 00:00 GMT. +.PP +Time fields: +.IP H +hour (00..23) +.IP I +hour (01..12) +.IP k +hour ( 0..23) +.IP l +hour ( 1..12) +.IP M +minute (00..59) +.IP p +locale's AM or PM +.IP r +time, 12-hour (hh:mm:ss [AP]M) +.IP S +second (00..61) +.IP T +time, 24-hour (hh:mm:ss) +.IP X +locale's time representation (H:M:S) +.IP Z +time zone (e.g., EDT), or nothing if no time zone is determinable +.PP +Date fields: +.IP a +locale's abbreviated weekday name (Sun..Sat) +.IP A +locale's full weekday name, variable length (Sunday..Saturday) +.IP b +locale's abbreviated month name (Jan..Dec) +.IP B +locale's full month name, variable length (January..December) +.IP c +locale's date and time (Sat Nov 04 12:02:33 EST 1989) +.IP d +day of month (01..31) +.IP D +date (mm/dd/yy) +.IP h +same as b +.IP j +day of year (001..366) +.IP m +month (01..12) +.IP U +week number of year with Sunday as first day of week (00..53) +.IP w +day of week (0..6) +.IP W +week number of year with Monday as first day of week (00..53) +.IP x +locale's date representation (mm/dd/yy) +.IP y +last two digits of year (00..99) +.IP Y +year (1970...) +.RE +.IP %b +File's size in 512-byte blocks (rounded up). +.IP %c +File's last status change time in the format returned by the C `ctime' +function. +.IP %C\fIk\fP +File's last status change time in the format specified by \fIk\fR, +which is the same as for %A. +.IP %d +File's depth in the directory tree; 0 means the file is a command line +argument. +.IP %f +File's name with any leading directories removed (only the last element). +.IP %F +Type of the filesystem the file is on; this value can be used for +\-fstype. +.IP %g +File's group name, or numeric group ID if the group has no name. +.IP %G +File's numeric group ID. +.IP %h +Leading directories of file's name (all but the last element). +.IP %H +Command line argument under which file was found. +.IP %i +File's inode number (in decimal). +.IP %k +File's size in 1K blocks (rounded up). +.IP %l +Object of symbolic link (empty string if file is not a symbolic link). +.IP %m +File's permission bits (in octal). +.IP %n +Number of hard links to file. +.IP %p +File's name. +.IP %P +File's name with the name of the command line argument under which +it was found removed. +.IP %s +File's size in bytes. +.IP %t +File's last modification time in the format returned by the C `ctime' +function. +.IP %T\fIk\fP +File's last modification time in the format specified by \fIk\fR, +which is the same as for %A. +.IP %u +File's user name, or numeric user ID if the user has no name. +.IP %U +File's numeric user ID. +.PP +A `%' character followed by any other character is discarded (but the +other character is printed). +.RE +.IP \-prune +If \-depth is not given, true; do not descend the current directory. +.br +If \-depth is given, false; no effect. +.IP \-ls +True; list current file in `ls \-dils' format on standard output. +The block counts are of 1K blocks, unless the environment variable +POSIXLY_CORRECT is set, in which case 512-byte blocks are used. +.SS OPERATORS +.P +Listed in order of decreasing precedence: +.IP "( \fIexpr\fR )" +Force precedence. +.IP "! \fIexpr\fR" +True if \fIexpr\fR is false. +.IP "\-not \fIexpr\fR" +Same as ! \fIexpr\fR. +.IP "\fIexpr1 expr2\fR" +And (implied); \fIexpr2\fR is not evaluated if \fIexpr1\fR is false. +.IP "\fIexpr1\fR \-a \fIexpr2\fR" +Same as \fIexpr1 expr2\fR. +.IP "\fIexpr1\fR \-and \fIexpr2\fR" +Same as \fIexpr1 expr2\fR. +.IP "\fIexpr1\fR \-o \fIexpr2\fR" +Or; \fIexpr2\fR is not evaluated if \fIexpr1\fR is true. +.IP "\fIexpr1\fR \-or \fIexpr2\fR" +Same as \fIexpr1\fR \-o \fIexpr2\fR. +.IP "\fIexpr1\fR , \fIexpr2\fR" +List; both \fIexpr1\fR and \fIexpr2\fR are always evaluated. +The value of \fIexpr1\fR is discarded; the value of the list is the +value of \fIexpr2\fR. +.SH "SEE ALSO" +\fBlocate\fP(1L), \fBlocatedb\fP(5L), \fBupdatedb\fP(1L), \fBxargs\fP(1L) +\fBFinding Files\fP (on-line in Info, or printed) diff --git a/find/find.c b/find/find.c new file mode 100644 index 0000000..361f2c4 --- /dev/null +++ b/find/find.c @@ -0,0 +1,541 @@ +/* find -- search for files in a directory hierarchy + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* GNU find was written by Eric Decker , + with enhancements by David MacKenzie , + Jay Plett , + and Tim Wood . + The idea for -print0 and xargs -0 came from + Dan Bernstein . */ + +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#else +#include +#endif +#include "defs.h" +#include "modetype.h" + +#ifndef S_IFLNK +#define lstat stat +#endif + +int lstat (); +int stat (); + +#define apply_predicate(pathname, stat_buf_ptr, node) \ + (*(node)->pred_func)((pathname), (stat_buf_ptr), (node)) + +static void process_top_path P_((char *pathname)); +static int process_path P_((char *pathname, char *name, boolean leaf, char *parent)); +static void process_dir P_((char *pathname, char *name, int pathlen, struct stat *statp, char *parent)); +static boolean no_side_effects P_((struct predicate *pred)); + +/* Name this program was run with. */ +char *program_name; + +/* All predicates for each path to process. */ +struct predicate *predicates; + +/* The last predicate allocated. */ +struct predicate *last_pred; + +/* The root of the evaluation tree. */ +static struct predicate *eval_tree; + +/* If true, process directory before contents. True unless -depth given. */ +boolean do_dir_first; + +/* If >=0, don't descend more than this many levels of subdirectories. */ +int maxdepth; + +/* If >=0, don't process files above this level. */ +int mindepth; + +/* Current depth; 0 means current path is a command line arg. */ +int curdepth; + +/* Seconds between 00:00 1/1/70 and either one day before now + (the default), or the start of today (if -daystart is given). */ +time_t cur_day_start; + +/* If true, cur_day_start has been adjusted to the start of the day. */ +boolean full_days; + +/* If true, do not assume that files in directories with nlink == 2 + are non-directories. */ +boolean no_leaf_check; + +/* If true, don't cross filesystem boundaries. */ +boolean stay_on_filesystem; + +/* If true, don't descend past current directory. + Can be set by -prune, -maxdepth, and -xdev/-mount. */ +boolean stop_at_current_level; + +#ifndef HAVE_FCHDIR +/* The full path of the initial working directory. */ +char *starting_dir; +#else +/* A file descriptor open to the initial working directory. + Doing it this way allows us to work when the i.w.d. has + unreadable parents. */ +int starting_desc; +#endif + +/* If true, we have called stat on the current path. */ +boolean have_stat; + +/* The file being operated on, relative to the current directory. + Used for stat, readlink, and opendir. */ +char *rel_pathname; + +/* Length of current path. */ +int path_length; + +/* true if following symlinks. Should be consistent with xstat. */ +boolean dereference; + +/* Pointer to the function used to stat files. */ +int (*xstat) (); + +/* Status value to return to system. */ +int exit_status; + +#ifdef DEBUG_STAT +static int +debug_stat (file, bufp) + char *file; + struct stat *bufp; +{ + fprintf (stderr, "debug_stat (%s)\n", file); + return lstat (file, bufp); +} +#endif /* DEBUG_STAT */ + +void +main (argc, argv) + int argc; + char *argv[]; +{ + int i; + PFB parse_function; /* Pointer to who is to do the parsing. */ + struct predicate *cur_pred; + char *predicate_name; /* Name of predicate being parsed. */ + + program_name = argv[0]; + + predicates = NULL; + last_pred = NULL; + do_dir_first = true; + maxdepth = mindepth = -1; + cur_day_start = time ((time_t *) 0) - DAYSECS; + full_days = false; + no_leaf_check = false; + stay_on_filesystem = false; + exit_status = 0; + dereference = false; +#ifdef DEBUG_STAT + xstat = debug_stat; +#else /* !DEBUG_STAT */ + xstat = lstat; +#endif /* !DEBUG_STAT */ + +#ifdef DEBUG + printf ("cur_day_start = %s", ctime (&cur_day_start)); +#endif /* DEBUG */ + + /* Find where in ARGV the predicates begin. */ + for (i = 1; i < argc && strchr ("-!(),", argv[i][0]) == NULL; i++) + /* Do nothing. */ ; + + /* Enclose the expression in `( ... )' so a default -print will + apply to the whole expression. */ + parse_open (argv, &argc); + /* Build the input order list. */ + while (i < argc) + { + if (strchr ("-!(),", argv[i][0]) == NULL) + usage ("paths must precede expression"); + predicate_name = argv[i]; + parse_function = find_parser (predicate_name); + if (parse_function == NULL) + error (1, 0, "invalid predicate `%s'", predicate_name); + i++; + if (!(*parse_function) (argv, &i)) + { + if (argv[i] == NULL) + error (1, 0, "missing argument to `%s'", predicate_name); + else + error (1, 0, "invalid argument `%s' to `%s'", + argv[i], predicate_name); + } + } + if (predicates->pred_next == NULL) + { + /* No predicates that do something other than set a global variable + were given; remove the unneeded initial `(' and add `-print'. */ + cur_pred = predicates; + predicates = last_pred = predicates->pred_next; + free ((char *) cur_pred); + parse_print (argv, &argc); + } + else if (!no_side_effects (predicates->pred_next)) + { + /* One or more predicates that produce output were given; + remove the unneeded initial `('. */ + cur_pred = predicates; + predicates = predicates->pred_next; + free ((char *) cur_pred); + } + else + { + /* `( user-supplied-expression ) -print'. */ + parse_close (argv, &argc); + parse_print (argv, &argc); + } + +#ifdef DEBUG + printf ("Predicate List:\n"); + print_list (predicates); +#endif /* DEBUG */ + + /* Done parsing the predicates. Build the evaluation tree. */ + cur_pred = predicates; + eval_tree = get_expr (&cur_pred, NO_PREC); +#ifdef DEBUG + printf ("Eval Tree:\n"); + print_tree (eval_tree, 0); +#endif /* DEBUG */ + + /* Rearrange the eval tree in optimal-predicate order. */ + opt_expr (&eval_tree); + + /* Determine the point, if any, at which to stat the file. */ + mark_stat (eval_tree); + +#ifdef DEBUG + printf ("Optimized Eval Tree:\n"); + print_tree (eval_tree, 0); +#endif /* DEBUG */ + +#ifndef HAVE_FCHDIR + starting_dir = xgetcwd (); + if (starting_dir == NULL) + error (1, errno, "cannot get current directory"); +#else + starting_desc = open (".", O_RDONLY); + if (starting_desc < 0) + error (1, errno, "cannot open current directory"); +#endif + + /* If no paths are given, default to ".". */ + for (i = 1; i < argc && strchr ("-!(),", argv[i][0]) == NULL; i++) + process_top_path (argv[i]); + if (i == 1) + process_top_path ("."); + + exit (exit_status); +} + +/* Descend PATHNAME, which is a command-line argument. */ + +static void +process_top_path (pathname) + char *pathname; +{ + struct stat stat_buf; + + curdepth = 0; + path_length = strlen (pathname); + + /* We stat each pathname given on the command-line twice -- + once here and once in process_path. It's not too bad, though, + since the kernel can read the stat information out of its inode + cache the second time. */ + if ((*xstat) (pathname, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode)) + { + if (chdir (pathname) < 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return; + } + process_path (pathname, ".", false, "."); +#ifndef HAVE_FCHDIR + if (chdir (starting_dir) < 0) + error (1, errno, "%s", starting_dir); +#else + if (fchdir (starting_desc)) + error (1, errno, "cannot return to starting directory"); +#endif + } + else + process_path (pathname, pathname, false, "."); +} + +/* Info on each directory in the current tree branch, to avoid + getting stuck in symbolic link loops. */ +struct dir_id +{ + ino_t ino; + dev_t dev; +}; +static struct dir_id *dir_ids = NULL; +/* Entries allocated in `dir_ids'. */ +static int dir_alloc = 0; +/* Index in `dir_ids' of directory currently being searched. + This is always the last valid entry. */ +static int dir_curr = -1; +/* (Arbitrary) number of entries to grow `dir_ids' by. */ +#define DIR_ALLOC_STEP 32 + +/* Recursively descend path PATHNAME, applying the predicates. + LEAF is true if PATHNAME is known to be in a directory that has no + more unexamined subdirectories, and therefore it is not a directory. + Knowing this allows us to avoid calling stat as long as possible for + leaf files. + + NAME is PATHNAME relative to the current directory. We access NAME + but print PATHNAME. + + PARENT is the path of the parent of NAME, relative to find's + starting directory. + + Return nonzero iff PATHNAME is a directory. */ + +static int +process_path (pathname, name, leaf, parent) + char *pathname; + char *name; + boolean leaf; + char *parent; +{ + struct stat stat_buf; + static dev_t root_dev; /* Device ID of current argument pathname. */ + int i; + + /* Assume it is a non-directory initially. */ + stat_buf.st_mode = 0; + + rel_pathname = name; + + if (leaf) + have_stat = false; + else + { + if ((*xstat) (name, &stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return 0; + } + have_stat = true; + } + + if (!S_ISDIR (stat_buf.st_mode)) + { + if (curdepth >= mindepth) + apply_predicate (pathname, &stat_buf, eval_tree); + return 0; + } + + /* From here on, we're working on a directory. */ + + stop_at_current_level = maxdepth >= 0 && curdepth >= maxdepth; + + /* If we've already seen this directory on this branch, + don't descend it again. */ + for (i = 0; i <= dir_curr; i++) + if (stat_buf.st_ino == dir_ids[i].ino && + stat_buf.st_dev == dir_ids[i].dev) + stop_at_current_level = true; + + if (dir_alloc <= ++dir_curr) + { + dir_alloc += DIR_ALLOC_STEP; + dir_ids = (struct dir_id *) + xrealloc ((char *) dir_ids, dir_alloc * sizeof (struct dir_id)); + } + dir_ids[dir_curr].ino = stat_buf.st_ino; + dir_ids[dir_curr].dev = stat_buf.st_dev; + + if (stay_on_filesystem) + { + if (curdepth == 0) + root_dev = stat_buf.st_dev; + else if (stat_buf.st_dev != root_dev) + stop_at_current_level = true; + } + + if (do_dir_first && curdepth >= mindepth) + apply_predicate (pathname, &stat_buf, eval_tree); + + if (stop_at_current_level == false) + /* Scan directory on disk. */ + process_dir (pathname, name, strlen (pathname), &stat_buf, parent); + + if (do_dir_first == false && curdepth >= mindepth) + apply_predicate (pathname, &stat_buf, eval_tree); + + dir_curr--; + + return 1; +} + +/* Scan directory PATHNAME and recurse through process_path for each entry. + + PATHLEN is the length of PATHNAME. + + NAME is PATHNAME relative to the current directory. + + STATP is the results of *xstat on it. + + PARENT is the path of the parent of NAME, relative to find's + starting directory. */ + +static void +process_dir (pathname, name, pathlen, statp, parent) + char *pathname; + char *name; + int pathlen; + struct stat *statp; + char *parent; +{ + char *name_space; /* Names of files in PATHNAME. */ + int subdirs_left; /* Number of unexamined subdirs in PATHNAME. */ + + subdirs_left = statp->st_nlink - 2; /* Account for name and ".". */ + + errno = 0; + /* On some systems (VAX 4.3BSD+NFS), NFS mount points have st_size < 0. */ + name_space = savedir (name, statp->st_size > 0 ? statp->st_size : 512); + if (name_space == NULL) + { + if (errno) + { + error (0, errno, "%s", pathname); + exit_status = 1; + } + else + error (1, 0, "virtual memory exhausted"); + } + else + { + register char *namep; /* Current point in `name_space'. */ + char *cur_path; /* Full path of each file to process. */ + char *cur_name; /* Base name of each file to process. */ + unsigned cur_path_size; /* Bytes allocated for `cur_path'. */ + register unsigned file_len; /* Length of each path to process. */ + register unsigned pathname_len; /* PATHLEN plus trailing '/'. */ + + if (pathname[pathlen - 1] == '/') + pathname_len = pathlen + 1; /* For '\0'; already have '/'. */ + else + pathname_len = pathlen + 2; /* For '/' and '\0'. */ + cur_path_size = 0; + cur_path = NULL; + + if (strcmp (name, ".") && chdir (name) < 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return; + } + + for (namep = name_space; *namep; namep += file_len - pathname_len + 1) + { + /* Append this directory entry's name to the path being searched. */ + file_len = pathname_len + strlen (namep); + if (file_len > cur_path_size) + { + while (file_len > cur_path_size) + cur_path_size += 1024; + if (cur_path) + free (cur_path); + cur_path = xmalloc (cur_path_size); + strcpy (cur_path, pathname); + cur_path[pathname_len - 2] = '/'; + } + cur_name = cur_path + pathname_len - 1; + strcpy (cur_name, namep); + + curdepth++; + if (!no_leaf_check) + /* Normal case optimization. + On normal Unix filesystems, a directory that has no + subdirectories has two links: its name, and ".". Any + additional links are to the ".." entries of its + subdirectories. Once we have processed as many + subdirectories as there are additional links, we know + that the rest of the entries are non-directories -- + in other words, leaf files. */ + subdirs_left -= process_path (cur_path, cur_name, + subdirs_left == 0, pathname); + else + /* There might be weird (e.g., CD-ROM or MS-DOS) filesystems + mounted, which don't have Unix-like directory link counts. */ + process_path (cur_path, cur_name, false, pathname); + curdepth--; + } + + if (strcmp (name, ".")) + { + if (!dereference) + { + if (chdir ("..") < 0) + /* We could go back and do the next command-line arg instead, + maybe using longjmp. */ + error (1, errno, "%s", parent); + } + else + { +#ifndef HAVE_FCHDIR + if (chdir (starting_dir) || chdir (parent)) + error (1, errno, "%s", parent); +#else + if (fchdir (starting_desc) || chdir (parent)) + error (1, errno, "%s", parent); +#endif + } + } + + if (cur_path) + free (cur_path); + free (name_space); + } +} + +/* Return true if there are no side effects in any of the predicates in + predicate list PRED, false if there are any. */ + +static boolean +no_side_effects (pred) + struct predicate *pred; +{ + while (pred != NULL) + { + if (pred->side_effects) + return (false); + pred = pred->pred_next; + } + return (true); +} diff --git a/find/fstype.c b/find/fstype.c new file mode 100644 index 0000000..56492a5 --- /dev/null +++ b/find/fstype.c @@ -0,0 +1,386 @@ +/* fstype.c -- determine type of filesystems that files are on + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie . */ + +#include +#include +#include +#include +#include "defs.h" +#include "modetype.h" +#include +#ifdef STDC_HEADERS +#include +#else +extern int errno; +#endif + +char *strdup (); +char *strstr (); + +static char *filesystem_type_uncached P_((char *path, char *relpath, struct stat *statp)); +static int xatoi P_((char *cp)); + +#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */ +#include +#if !defined(MOUNTED) +# if defined(MNT_MNTTAB) /* HP-UX. */ +# define MOUNTED MNT_MNTTAB +# endif +# if defined(MNTTABNAME) /* Dynix. */ +# define MOUNTED MNTTABNAME +# endif +#endif +#endif + +#ifdef FSTYPE_GETMNT /* Ultrix. */ +#include +#include +#include +#endif + +#ifdef FSTYPE_USG_STATFS /* SVR3. */ +#include +#include +#endif + +#ifdef FSTYPE_STATVFS /* SVR4. */ +#include +#include +#endif + +#ifdef FSTYPE_STATFS /* 4.4BSD. */ +#include /* NetBSD needs this. */ +#include + +#ifndef MFSNAMELEN /* NetBSD defines this. */ +static char * +fstype_to_string (t) + short t; +{ +#ifdef INITMOUNTNAMES /* Defined in 4.4BSD, not in NET/2. */ + static char *mn[] = INITMOUNTNAMES; + if (t >= 0 && t <= MOUNT_MAXTYPE) + return mn[t]; + else + return "?"; +#else /* !INITMOUNTNAMES */ + switch (t) + { + case MOUNT_UFS: + return "ufs"; + case MOUNT_NFS: + return "nfs"; +#ifdef MOUNT_PC + case MOUNT_PC: + return "pc"; +#endif +#ifdef MOUNT_MFS + case MOUNT_MFS: + return "mfs"; +#endif +#ifdef MOUNT_LO + case MOUNT_LO: + return "lofs"; +#endif +#ifdef MOUNT_TFS + case MOUNT_TFS: + return "tfs"; +#endif +#ifdef MOUNT_TMP + case MOUNT_TMP: + return "tmp"; +#endif +#ifdef MOUNT_MSDOS + case MOUNT_MSDOS: + return "msdos"; +#endif +#ifdef MOUNT_ISO9660 + case MOUNT_ISO9660: + return "iso9660fs"; +#endif + default: + return "?"; + } +#endif /* !INITMOUNTNAMES */ +} +#endif /* !MFSNAMELEN */ +#endif /* FSTYPE_STATFS */ + +#ifdef FSTYPE_AIX_STATFS /* AIX. */ +#include +#include + +#define FSTYPE_STATFS /* Otherwise like 4.4BSD. */ +#define f_type f_vfstype + +static char * +fstype_to_string (t) + short t; +{ + switch (t) + { + case MNT_AIX: +#if 0 /* NFS filesystems are actually MNT_AIX. */ + return "aix"; +#endif + case MNT_NFS: + return "nfs"; + case MNT_JFS: + return "jfs"; + case MNT_CDROM: + return "cdrom"; + default: + return "?"; + } +} +#endif /* FSTYPE_AIX_STATFS */ + +#ifdef AFS +#include +#include +#if __STDC__ +/* On SunOS 4, afs/vice.h defines this to rely on a pre-ANSI cpp. */ +#undef _VICEIOCTL +#define _VICEIOCTL(id) ((unsigned int ) _IOW('V', id, struct ViceIoctl)) +#endif +#ifndef _IOW +/* AFS on Solaris 2.3 doesn't get this definition. */ +#include +#endif + +static int +in_afs (path) + char *path; +{ + static char space[2048]; + struct ViceIoctl vi; + + vi.in_size = 0; + vi.out_size = sizeof (space); + vi.out = space; + + if (pioctl (path, VIOC_FILE_CELL_NAME, &vi, 1) + && (errno == EINVAL || errno == ENOENT)) + return 0; + return 1; +} +#endif /* AFS */ + +/* Nonzero if the current filesystem's type is known. */ +static int fstype_known = 0; + +/* Return a static string naming the type of filesystem that the file PATH, + described by STATP, is on. + RELPATH is the file name relative to the current directory. + Return "unknown" if its filesystem type is unknown. */ + +char * +filesystem_type (path, relpath, statp) + char *path; + char *relpath; + struct stat *statp; +{ + static char *current_fstype = NULL; + static dev_t current_dev; + + if (current_fstype != NULL) + { + if (fstype_known && statp->st_dev == current_dev) + return current_fstype; /* Cached value. */ + free (current_fstype); + } + current_dev = statp->st_dev; + current_fstype = filesystem_type_uncached (path, relpath, statp); + return current_fstype; +} + +/* Return a newly allocated string naming the type of filesystem that the + file PATH, described by STATP, is on. + RELPATH is the file name relative to the current directory. + Return "unknown" if its filesystem type is unknown. */ + +static char * +filesystem_type_uncached (path, relpath, statp) + char *path; + char *relpath; + struct stat *statp; +{ + char *type = NULL; + +#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */ + char *table = MOUNTED; + FILE *mfp; + struct mntent *mnt; + + mfp = setmntent (table, "r"); + if (mfp == NULL) + error (1, errno, "%s", table); + + /* Find the entry with the same device number as STATP, and return + that entry's fstype. */ + while (type == NULL && (mnt = getmntent (mfp))) + { + char *devopt; + dev_t dev; + struct stat disk_stats; + +#ifdef MNTTYPE_IGNORE + if (!strcmp (mnt->mnt_type, MNTTYPE_IGNORE)) + continue; +#endif + + /* Newer systems like SunOS 4.1 keep the dev number in the mtab, + in the options string. For older systems, we need to stat the + directory that the filesystem is mounted on to get it. + + Unfortunately, the HPUX 9.x mnttab entries created by automountq + contain a dev= option but the option value does not match the + st_dev value of the file (maybe the lower 16 bits match?). */ + +#if !defined(hpux) && !defined(__hpux__) + devopt = strstr (mnt->mnt_opts, "dev="); + if (devopt) + { + if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X')) + dev = xatoi (devopt + 6); + else + dev = xatoi (devopt + 4); + } + else +#endif /* not hpux */ + { + if (stat (mnt->mnt_dir, &disk_stats) == -1) + error (1, errno, "error in %s: %s", table, mnt->mnt_dir); + dev = disk_stats.st_dev; + } + + if (dev == statp->st_dev) + type = mnt->mnt_type; + } + + if (endmntent (mfp) == 0) + error (0, errno, "%s", table); +#endif + +#ifdef FSTYPE_GETMNT /* Ultrix. */ + int offset = 0; + struct fs_data fsd; + + while (type == NULL + && getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, 0) > 0) + { + if (fsd.fd_req.dev == statp->st_dev) + type = gt_names[fsd.fd_req.fstype]; + } +#endif + +#ifdef FSTYPE_USG_STATFS /* SVR3. */ + struct statfs fss; + char typebuf[FSTYPSZ]; + + if (statfs (relpath, &fss, sizeof (struct statfs), 0) == -1) + { + /* Don't die if a file was just removed. */ + if (errno != ENOENT) + error (1, errno, "%s", path); + } + else if (!sysfs (GETFSTYP, fss.f_fstyp, typebuf)) + type = typebuf; +#endif + +#ifdef FSTYPE_STATVFS /* SVR4. */ + struct statvfs fss; + + if (statvfs (relpath, &fss) == -1) + { + /* Don't die if a file was just removed. */ + if (errno != ENOENT) + error (1, errno, "%s", path); + } + else + type = fss.f_basetype; +#endif + +#ifdef FSTYPE_STATFS /* 4.4BSD. */ + struct statfs fss; + char *p; + + if (S_ISLNK (statp->st_mode)) + p = dirname (relpath); + else + p = relpath; + + if (statfs (p, &fss) == -1) + { + /* Don't die if symlink to nonexisting file, or a file that was + just removed. */ + if (errno != ENOENT) + error (1, errno, "%s", path); + } + else + { +#ifdef MFSNAMELEN /* NetBSD. */ + type = xstrdup (fss.f_fstypename); +#else + type = fstype_to_string (fss.f_type); +#endif + } + if (p != relpath) + free (p); +#endif + +#ifdef AFS + if ((!type || !strcmp (type, "xx")) && in_afs (relpath)) + type = "afs"; +#endif + + /* An unknown value can be caused by an ENOENT error condition. + Don't cache those values. */ + fstype_known = (type != NULL); + + return xstrdup (type ? type : "unknown"); +} + +#ifdef FSTYPE_MNTENT /* 4.3BSD etc. */ +/* Return the value of the hexadecimal number represented by CP. + No prefix (like '0x') or suffix (like 'h') is expected to be + part of CP. */ + +static int +xatoi (cp) + char *cp; +{ + int val; + + val = 0; + while (*cp) + { + if (*cp >= 'a' && *cp <= 'f') + val = val * 16 + *cp - 'a' + 10; + else if (*cp >= 'A' && *cp <= 'F') + val = val * 16 + *cp - 'A' + 10; + else if (*cp >= '0' && *cp <= '9') + val = val * 16 + *cp - '0'; + else + break; + cp++; + } + return val; +} +#endif diff --git a/find/parser.c b/find/parser.c new file mode 100644 index 0000000..3d85a9a --- /dev/null +++ b/find/parser.c @@ -0,0 +1,1884 @@ +/* parser.c -- convert the command line args into an expression tree. + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include "modechange.h" +#include "defs.h" +#include "modetype.h" + +#if !defined (isascii) || defined (STDC_HEADERS) +#ifdef isascii +#undef isascii +#endif +#define isascii(c) 1 +#endif + +#define ISDIGIT(c) (isascii (c) && isdigit (c)) +#define ISUPPER(c) (isascii (c) && isupper (c)) + +#ifndef _POSIX_VERSION +/* POSIX.1 header files should declare these. */ +struct group *getgrnam (); +struct passwd *getpwnam (); +#endif + +#ifdef CACHE_IDS +/* These two aren't specified by POSIX.1. */ +struct group *getgrent (); +struct passwd *getpwent (); +#endif + +#ifndef S_IFLNK +#define lstat stat +#endif + +char *strstr (); +int lstat (); +int stat (); +#ifndef atol /* for Linux */ +long atol (); +#endif +struct tm *localtime (); + +#ifdef _POSIX_SOURCE +#define endgrent() +#define endpwent() +#else +void endgrent (); +void endpwent (); +#endif + +static boolean parse_amin P_((char *argv[], int *arg_ptr)); +static boolean parse_and P_((char *argv[], int *arg_ptr)); +static boolean parse_anewer P_((char *argv[], int *arg_ptr)); +static boolean parse_atime P_((char *argv[], int *arg_ptr)); +boolean parse_close P_((char *argv[], int *arg_ptr)); +static boolean parse_cmin P_((char *argv[], int *arg_ptr)); +static boolean parse_cnewer P_((char *argv[], int *arg_ptr)); +static boolean parse_comma P_((char *argv[], int *arg_ptr)); +static boolean parse_ctime P_((char *argv[], int *arg_ptr)); +static boolean parse_daystart P_((char *argv[], int *arg_ptr)); +static boolean parse_depth P_((char *argv[], int *arg_ptr)); +static boolean parse_empty P_((char *argv[], int *arg_ptr)); +static boolean parse_exec P_((char *argv[], int *arg_ptr)); +static boolean parse_false P_((char *argv[], int *arg_ptr)); +static boolean parse_fls P_((char *argv[], int *arg_ptr)); +static boolean parse_fprintf P_((char *argv[], int *arg_ptr)); +static boolean parse_follow P_((char *argv[], int *arg_ptr)); +static boolean parse_fprint P_((char *argv[], int *arg_ptr)); +static boolean parse_fprint0 P_((char *argv[], int *arg_ptr)); +static boolean parse_fstype P_((char *argv[], int *arg_ptr)); +static boolean parse_gid P_((char *argv[], int *arg_ptr)); +static boolean parse_group P_((char *argv[], int *arg_ptr)); +static boolean parse_help P_((char *argv[], int *arg_ptr)); +static boolean parse_ilname P_((char *argv[], int *arg_ptr)); +static boolean parse_iname P_((char *argv[], int *arg_ptr)); +static boolean parse_inum P_((char *argv[], int *arg_ptr)); +static boolean parse_ipath P_((char *argv[], int *arg_ptr)); +static boolean parse_iregex P_((char *argv[], int *arg_ptr)); +static boolean parse_links P_((char *argv[], int *arg_ptr)); +static boolean parse_lname P_((char *argv[], int *arg_ptr)); +static boolean parse_ls P_((char *argv[], int *arg_ptr)); +static boolean parse_maxdepth P_((char *argv[], int *arg_ptr)); +static boolean parse_mindepth P_((char *argv[], int *arg_ptr)); +static boolean parse_mmin P_((char *argv[], int *arg_ptr)); +static boolean parse_mtime P_((char *argv[], int *arg_ptr)); +static boolean parse_name P_((char *argv[], int *arg_ptr)); +static boolean parse_negate P_((char *argv[], int *arg_ptr)); +static boolean parse_newer P_((char *argv[], int *arg_ptr)); +static boolean parse_noleaf P_((char *argv[], int *arg_ptr)); +static boolean parse_nogroup P_((char *argv[], int *arg_ptr)); +static boolean parse_nouser P_((char *argv[], int *arg_ptr)); +static boolean parse_ok P_((char *argv[], int *arg_ptr)); +boolean parse_open P_((char *argv[], int *arg_ptr)); +static boolean parse_or P_((char *argv[], int *arg_ptr)); +static boolean parse_path P_((char *argv[], int *arg_ptr)); +static boolean parse_perm P_((char *argv[], int *arg_ptr)); +boolean parse_print P_((char *argv[], int *arg_ptr)); +static boolean parse_print0 P_((char *argv[], int *arg_ptr)); +static boolean parse_printf P_((char *argv[], int *arg_ptr)); +static boolean parse_prune P_((char *argv[], int *arg_ptr)); +static boolean parse_regex P_((char *argv[], int *arg_ptr)); +static boolean insert_regex P_((char *argv[], int *arg_ptr, boolean ignore_case)); +static boolean parse_size P_((char *argv[], int *arg_ptr)); +static boolean parse_true P_((char *argv[], int *arg_ptr)); +static boolean parse_type P_((char *argv[], int *arg_ptr)); +static boolean parse_uid P_((char *argv[], int *arg_ptr)); +static boolean parse_used P_((char *argv[], int *arg_ptr)); +static boolean parse_user P_((char *argv[], int *arg_ptr)); +static boolean parse_version P_((char *argv[], int *arg_ptr)); +static boolean parse_xdev P_((char *argv[], int *arg_ptr)); +static boolean parse_xtype P_((char *argv[], int *arg_ptr)); + +static boolean insert_regex P_((char *argv[], int *arg_ptr, boolean ignore_case)); +static boolean insert_type P_((char *argv[], int *arg_ptr, boolean (*which_pred )())); +static boolean insert_fprintf P_((FILE *fp, boolean (*func )(), char *argv[], int *arg_ptr)); +static struct segment **make_segment P_((struct segment **segment, char *format, int len, int kind)); +static boolean insert_exec_ok P_((boolean (*func )(), char *argv[], int *arg_ptr)); +static boolean get_num_days P_((char *str, unsigned long *num_days, enum comparison_type *comp_type)); +static boolean insert_time P_((char *argv[], int *arg_ptr, PFB pred)); +static boolean get_num P_((char *str, unsigned long *num, enum comparison_type *comp_type)); +static boolean insert_num P_((char *argv[], int *arg_ptr, PFB pred)); +static FILE *open_output_file P_((char *path)); + +#ifdef DEBUG +char *find_pred_name _P((PFB pred_func)); +#endif /* DEBUG */ + +struct parser_table +{ + char *parser_name; + PFB parser_func; +}; + +/* GNU find predicates that are not mentioned in POSIX.2 are marked `GNU'. + If they are in some Unix versions of find, they are marked `Unix'. */ + +static struct parser_table const parse_table[] = +{ + {"!", parse_negate}, + {"not", parse_negate}, /* GNU */ + {"(", parse_open}, + {")", parse_close}, + {",", parse_comma}, /* GNU */ + {"a", parse_and}, + {"amin", parse_amin}, /* GNU */ + {"and", parse_and}, /* GNU */ + {"anewer", parse_anewer}, /* GNU */ + {"atime", parse_atime}, + {"cmin", parse_cmin}, /* GNU */ + {"cnewer", parse_cnewer}, /* GNU */ +#ifdef UNIMPLEMENTED_UNIX + /* It's pretty ugly for find to know about archive formats. + Plus what it could do with cpio archives is very limited. + Better to leave it out. */ + {"cpio", parse_cpio}, /* Unix */ +#endif + {"ctime", parse_ctime}, + {"daystart", parse_daystart}, /* GNU */ + {"depth", parse_depth}, + {"empty", parse_empty}, /* GNU */ + {"exec", parse_exec}, + {"false", parse_false}, /* GNU */ + {"fls", parse_fls}, /* GNU */ + {"follow", parse_follow}, /* GNU, Unix */ + {"fprint", parse_fprint}, /* GNU */ + {"fprint0", parse_fprint0}, /* GNU */ + {"fprintf", parse_fprintf}, /* GNU */ + {"fstype", parse_fstype}, /* GNU, Unix */ + {"gid", parse_gid}, /* GNU */ + {"group", parse_group}, + {"help", parse_help}, /* GNU */ + {"-help", parse_help}, /* GNU */ + {"ilname", parse_ilname}, /* GNU */ + {"iname", parse_iname}, /* GNU */ + {"inum", parse_inum}, /* GNU, Unix */ + {"ipath", parse_ipath}, /* GNU */ + {"iregex", parse_iregex}, /* GNU */ + {"links", parse_links}, + {"lname", parse_lname}, /* GNU */ + {"ls", parse_ls}, /* GNU, Unix */ + {"maxdepth", parse_maxdepth}, /* GNU */ + {"mindepth", parse_mindepth}, /* GNU */ + {"mmin", parse_mmin}, /* GNU */ + {"mount", parse_xdev}, /* Unix */ + {"mtime", parse_mtime}, + {"name", parse_name}, +#ifdef UNIMPLEMENTED_UNIX + {"ncpio", parse_ncpio}, /* Unix */ +#endif + {"newer", parse_newer}, + {"noleaf", parse_noleaf}, /* GNU */ + {"nogroup", parse_nogroup}, + {"nouser", parse_nouser}, + {"o", parse_or}, + {"or", parse_or}, /* GNU */ + {"ok", parse_ok}, + {"path", parse_path}, /* GNU, HP-UX */ + {"perm", parse_perm}, + {"print", parse_print}, + {"print0", parse_print0}, /* GNU */ + {"printf", parse_printf}, /* GNU */ + {"prune", parse_prune}, + {"regex", parse_regex}, /* GNU */ + {"size", parse_size}, + {"true", parse_true}, /* GNU */ + {"type", parse_type}, + {"uid", parse_uid}, /* GNU */ + {"used", parse_used}, /* GNU */ + {"user", parse_user}, + {"version", parse_version}, /* GNU */ + {"-version", parse_version}, /* GNU */ + {"xdev", parse_xdev}, + {"xtype", parse_xtype}, /* GNU */ + {0, 0} +}; + +/* Return a pointer to the parser function to invoke for predicate + SEARCH_NAME. + Return NULL if SEARCH_NAME is not a valid predicate name. */ + +PFB +find_parser (search_name) + char *search_name; +{ + int i; + + if (*search_name == '-') + search_name++; + for (i = 0; parse_table[i].parser_name != 0; i++) + if (strcmp (parse_table[i].parser_name, search_name) == 0) + return (parse_table[i].parser_func); + return (NULL); +} + +/* The parsers are responsible to continue scanning ARGV for + their arguments. Each parser knows what is and isn't + allowed for itself. + + ARGV is the argument array. + *ARG_PTR is the index to start at in ARGV, + updated to point beyond the last element consumed. + + The predicate structure is updated with the new information. */ + +static boolean +parse_amin (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred_amin); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start + DAYSECS - num * 60; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_and (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = get_new_pred (); + our_pred->pred_func = pred_and; +#ifdef DEBUG + our_pred->p_name = find_pred_name (pred_and); +#endif /* DEBUG */ + our_pred->p_type = BI_OP; + our_pred->p_prec = AND_PREC; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_anewer (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + struct stat stat_newer; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if ((*xstat) (argv[*arg_ptr], &stat_newer)) + error (1, errno, "%s", argv[*arg_ptr]); + our_pred = insert_primary (pred_anewer); + our_pred->args.time = stat_newer.st_mtime; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_atime (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_time (argv, arg_ptr, pred_atime)); +} + +boolean +parse_close (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = get_new_pred (); + our_pred->pred_func = pred_close; +#ifdef DEBUG + our_pred->p_name = find_pred_name (pred_close); +#endif /* DEBUG */ + our_pred->p_type = CLOSE_PAREN; + our_pred->p_prec = NO_PREC; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_cmin (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred_cmin); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start + DAYSECS - num * 60; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_cnewer (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + struct stat stat_newer; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if ((*xstat) (argv[*arg_ptr], &stat_newer)) + error (1, errno, "%s", argv[*arg_ptr]); + our_pred = insert_primary (pred_cnewer); + our_pred->args.time = stat_newer.st_mtime; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_comma (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = get_new_pred (); + our_pred->pred_func = pred_comma; +#ifdef DEBUG + our_pred->p_name = find_pred_name (pred_comma); +#endif /* DEBUG */ + our_pred->p_type = BI_OP; + our_pred->p_prec = COMMA_PREC; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_ctime (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_time (argv, arg_ptr, pred_ctime)); +} + +static boolean +parse_daystart (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct tm *local; + + if (full_days == false) + { + cur_day_start += DAYSECS; + local = localtime (&cur_day_start); + cur_day_start -= local->tm_sec + local->tm_min * 60 + + local->tm_hour * 3600; + full_days = true; + } + return (true); +} + +static boolean +parse_depth (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + do_dir_first = false; + return (true); +} + +static boolean +parse_empty (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + insert_primary (pred_empty); + return (true); +} + +static boolean +parse_exec (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_exec_ok (pred_exec, argv, arg_ptr)); +} + +static boolean +parse_false (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_false); + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_fls (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fls); + our_pred->args.stream = open_output_file (argv[*arg_ptr]); + our_pred->side_effects = true; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_fprintf (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + FILE *fp; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (argv[*arg_ptr + 1] == NULL) + { + /* Ensure we get "missing arg" message, not "invalid arg". */ + (*arg_ptr)++; + return (false); + } + fp = open_output_file (argv[*arg_ptr]); + (*arg_ptr)++; + return (insert_fprintf (fp, pred_fprintf, argv, arg_ptr)); +} + +static boolean +parse_follow (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + dereference = true; + xstat = stat; + no_leaf_check = true; + return (true); +} + +static boolean +parse_fprint (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fprint); + our_pred->args.stream = open_output_file (argv[*arg_ptr]); + our_pred->side_effects = true; + our_pred->need_stat = false; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_fprint0 (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fprint0); + our_pred->args.stream = open_output_file (argv[*arg_ptr]); + our_pred->side_effects = true; + our_pred->need_stat = false; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_fstype (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_fstype); + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_gid (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_num (argv, arg_ptr, pred_gid)); +} + +static boolean +parse_group (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct group *cur_gr; + struct predicate *our_pred; + gid_t gid; + int gid_len; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + cur_gr = getgrnam (argv[*arg_ptr]); + endgrent (); + if (cur_gr != NULL) + gid = cur_gr->gr_gid; + else + { + gid_len = strspn (argv[*arg_ptr], "0123456789"); + if ((gid_len == 0) || (argv[*arg_ptr][gid_len] != '\0')) + return (false); + gid = atoi (argv[*arg_ptr]); + } + our_pred = insert_primary (pred_group); + our_pred->args.gid = gid; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_help (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + printf ("\ +Usage: %s [path...] [expression]\n", program_name); + printf ("\ +default path is the current directory; default expression is -print\n\ +expression may consist of:\n\ +operators (decreasing precedence; -and is implicit where no others are given):\n\ + ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2\n"); + printf ("\ + EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2\n\ +options (always true): -daystart -depth -follow --help\n\ + -maxdepth LEVELS -mindepth LEVELS -mount -noleaf --version -xdev\n\ +tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n"); + printf ("\ + -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME\n\ + -ilname PATTERN -iname PATTERN -inum N -ipath PATTERN -iregex PATTERN\n\ + -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE\n"); + printf ("\ + -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\ + -size N[bckw] -true -type [bcdpfls] -uid N -used N -user NAME\n\ + -xtype [bcdpfls]\n"); + printf ("\ +actions: -exec COMMAND ; -fprint FILE -fprint0 FILE -fprintf FILE FORMAT\n\ + -ok COMMAND ; -print -print0 -printf FORMAT -prune -ls\n"); + exit (0); +} + +static boolean +parse_ilname (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_ilname); + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_iname (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_iname); + our_pred->need_stat = false; + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_inum (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_num (argv, arg_ptr, pred_inum)); +} + +static boolean +parse_ipath (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_ipath); + our_pred->need_stat = false; + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_iregex (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return insert_regex (argv, arg_ptr, true); +} + +static boolean +parse_links (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_num (argv, arg_ptr, pred_links)); +} + +static boolean +parse_lname (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_lname); + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_ls (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_ls); + our_pred->side_effects = true; + return (true); +} + +static boolean +parse_maxdepth (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + int depth_len; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + depth_len = strspn (argv[*arg_ptr], "0123456789"); + if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0')) + return (false); + maxdepth = atoi (argv[*arg_ptr]); + if (maxdepth < 0) + return (false); + (*arg_ptr)++; + return (true); +} + +static boolean +parse_mindepth (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + int depth_len; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + depth_len = strspn (argv[*arg_ptr], "0123456789"); + if ((depth_len == 0) || (argv[*arg_ptr][depth_len] != '\0')) + return (false); + mindepth = atoi (argv[*arg_ptr]); + if (mindepth < 0) + return (false); + (*arg_ptr)++; + return (true); +} + +static boolean +parse_mmin (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred_mmin); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start + DAYSECS - num * 60; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_mtime (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_time (argv, arg_ptr, pred_mtime)); +} + +static boolean +parse_name (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_name); + our_pred->need_stat = false; + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_negate (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = get_new_pred_chk_op (); + our_pred->pred_func = pred_negate; +#ifdef DEBUG + our_pred->p_name = find_pred_name (pred_negate); +#endif /* DEBUG */ + our_pred->p_type = UNI_OP; + our_pred->p_prec = NEGATE_PREC; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_newer (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + struct stat stat_newer; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if ((*xstat) (argv[*arg_ptr], &stat_newer)) + error (1, errno, "%s", argv[*arg_ptr]); + our_pred = insert_primary (pred_newer); + our_pred->args.time = stat_newer.st_mtime; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_noleaf (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + no_leaf_check = true; + return true; +} + +#ifdef CACHE_IDS +/* Arbitrary amount by which to increase size + of `uid_unused' and `gid_unused'. */ +#define ALLOC_STEP 2048 + +/* Boolean: if uid_unused[n] is nonzero, then UID n has no passwd entry. */ +char *uid_unused = NULL; + +/* Number of elements in `uid_unused'. */ +unsigned uid_allocated; + +/* Similar for GIDs and group entries. */ +char *gid_unused = NULL; +unsigned gid_allocated; +#endif + +static boolean +parse_nogroup (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_nogroup); +#ifdef CACHE_IDS + if (gid_unused == NULL) + { + struct group *gr; + + gid_allocated = ALLOC_STEP; + gid_unused = xmalloc (gid_allocated); + memset (gid_unused, 1, gid_allocated); + setgrent (); + while ((gr = getgrent ()) != NULL) + { + if ((unsigned) gr->gr_gid >= gid_allocated) + { + unsigned new_allocated = (unsigned) gr->gr_gid + ALLOC_STEP; + gid_unused = xrealloc (gid_unused, new_allocated); + memset (gid_unused + gid_allocated, 1, + new_allocated - gid_allocated); + gid_allocated = new_allocated; + } + gid_unused[(unsigned) gr->gr_gid] = 0; + } + endgrent (); + } +#endif + return (true); +} + +static boolean +parse_nouser (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_nouser); +#ifdef CACHE_IDS + if (uid_unused == NULL) + { + struct passwd *pw; + + uid_allocated = ALLOC_STEP; + uid_unused = xmalloc (uid_allocated); + memset (uid_unused, 1, uid_allocated); + setpwent (); + while ((pw = getpwent ()) != NULL) + { + if ((unsigned) pw->pw_uid >= uid_allocated) + { + unsigned new_allocated = (unsigned) pw->pw_uid + ALLOC_STEP; + uid_unused = xrealloc (uid_unused, new_allocated); + memset (uid_unused + uid_allocated, 1, + new_allocated - uid_allocated); + uid_allocated = new_allocated; + } + uid_unused[(unsigned) pw->pw_uid] = 0; + } + endpwent (); + } +#endif + return (true); +} + +static boolean +parse_ok (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_exec_ok (pred_ok, argv, arg_ptr)); +} + +boolean +parse_open (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = get_new_pred_chk_op (); + our_pred->pred_func = pred_open; +#ifdef DEBUG + our_pred->p_name = find_pred_name (pred_open); +#endif /* DEBUG */ + our_pred->p_type = OPEN_PAREN; + our_pred->p_prec = NO_PREC; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_or (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = get_new_pred (); + our_pred->pred_func = pred_or; +#ifdef DEBUG + our_pred->p_name = find_pred_name (pred_or); +#endif /* DEBUG */ + our_pred->p_type = BI_OP; + our_pred->p_prec = OR_PREC; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_path (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_path); + our_pred->need_stat = false; + our_pred->args.str = argv[*arg_ptr]; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_perm (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + unsigned long perm_val; + int mode_start = 0; + struct mode_change *change; + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + + switch (argv[*arg_ptr][0]) + { + case '-': + case '+': + mode_start = 1; + break; + default: + /* empty */ + break; + } + + change = mode_compile (argv[*arg_ptr] + mode_start, MODE_MASK_PLUS); + if (change == MODE_INVALID) + error (1, 0, "invalid mode `%s'", argv[*arg_ptr]); + else if (change == MODE_MEMORY_EXHAUSTED) + error (1, 0, "virtual memory exhausted"); + perm_val = mode_adjust (0, change); + mode_free (change); + + our_pred = insert_primary (pred_perm); + + switch (argv[*arg_ptr][0]) + { + case '-': + /* Set magic flag to indicate true if at least the given bits are set. */ + our_pred->args.perm = (perm_val & 07777) | 010000; + break; + case '+': + /* Set magic flag to indicate true if any of the given bits are set. */ + our_pred->args.perm = (perm_val & 07777) | 020000; + break; + default: + /* True if exactly the given bits are set. */ + our_pred->args.perm = (perm_val & 07777); + break; + } + (*arg_ptr)++; + return (true); +} + +boolean +parse_print (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_print); + /* -print has the side effect of printing. This prevents us + from doing undesired multiple printing when the user has + already specified -print. */ + our_pred->side_effects = true; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_print0 (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_print0); + /* -print0 has the side effect of printing. This prevents us + from doing undesired multiple printing when the user has + already specified -print0. */ + our_pred->side_effects = true; + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_printf (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + return (insert_fprintf (stdout, pred_fprintf, argv, arg_ptr)); +} + +static boolean +parse_prune (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_prune); + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_regex (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return insert_regex (argv, arg_ptr, false); +} + +static boolean +insert_regex (argv, arg_ptr, ignore_case) + char *argv[]; + int *arg_ptr; + boolean ignore_case; +{ + struct predicate *our_pred; + struct re_pattern_buffer *re; + const char *error_message; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + our_pred = insert_primary (pred_regex); + our_pred->need_stat = false; + re = (struct re_pattern_buffer *) + xmalloc (sizeof (struct re_pattern_buffer)); + our_pred->args.regex = re; + re->allocated = 100; + re->buffer = (unsigned char *) xmalloc (re->allocated); + re->fastmap = NULL; + + if (ignore_case) + { + unsigned i; + + re->translate = xmalloc (256); + /* Map uppercase characters to corresponding lowercase ones. */ + for (i = 0; i < 256; i++) + re->translate[i] = ISUPPER (i) ? tolower (i) : i; + } + else + re->translate = NULL; + + error_message = re_compile_pattern (argv[*arg_ptr], strlen (argv[*arg_ptr]), + re); + if (error_message) + error (1, 0, "%s", error_message); + (*arg_ptr)++; + return (true); +} + +static boolean +parse_size (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; + int blksize = 512; + int len; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + len = strlen (argv[*arg_ptr]); + if (len == 0) + error (1, 0, "invalid null argument to -size"); + switch (argv[*arg_ptr][len - 1]) + { + case 'b': + blksize = 512; + argv[*arg_ptr][len - 1] = '\0'; + break; + + case 'c': + blksize = 1; + argv[*arg_ptr][len - 1] = '\0'; + break; + + case 'k': + blksize = 1024; + argv[*arg_ptr][len - 1] = '\0'; + break; + + case 'w': + blksize = 2; + argv[*arg_ptr][len - 1] = '\0'; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + + default: + error (1, 0, "invalid -size type `%c'", argv[*arg_ptr][len - 1]); + } + if (!get_num (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred_size); + our_pred->args.size.kind = c_type; + our_pred->args.size.blocksize = blksize; + our_pred->args.size.size = num; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_true (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct predicate *our_pred; + + our_pred = insert_primary (pred_true); + our_pred->need_stat = false; + return (true); +} + +static boolean +parse_type (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return insert_type (argv, arg_ptr, pred_type); +} + +static boolean +parse_uid (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return (insert_num (argv, arg_ptr, pred_uid)); +} + +static boolean +parse_used (argv, arg_ptr) + char *argv[]; + int *arg_ptr; + +{ + struct predicate *our_pred; + unsigned long num_days; + enum comparison_type c_type; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num (argv[*arg_ptr], &num_days, &c_type)) + return (false); + our_pred = insert_primary (pred_used); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = num_days * DAYSECS; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_user (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + struct passwd *cur_pwd; + struct predicate *our_pred; + uid_t uid; + int uid_len; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + cur_pwd = getpwnam (argv[*arg_ptr]); + endpwent (); + if (cur_pwd != NULL) + uid = cur_pwd->pw_uid; + else + { + uid_len = strspn (argv[*arg_ptr], "0123456789"); + if ((uid_len == 0) || (argv[*arg_ptr][uid_len] != '\0')) + return (false); + uid = atoi (argv[*arg_ptr]); + } + our_pred = insert_primary (pred_user); + our_pred->args.uid = uid; + (*arg_ptr)++; + return (true); +} + +static boolean +parse_version (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + extern char *version_string; + + fflush (stderr); + printf ("GNU find version %s\n", version_string); + exit (0); +} + +static boolean +parse_xdev (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + stay_on_filesystem = true; + return true; +} + +static boolean +parse_xtype (argv, arg_ptr) + char *argv[]; + int *arg_ptr; +{ + return insert_type (argv, arg_ptr, pred_xtype); +} + +static boolean +insert_type (argv, arg_ptr, which_pred) + char *argv[]; + int *arg_ptr; + boolean (*which_pred) (); +{ + unsigned long type_cell; + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL) + || (strlen (argv[*arg_ptr]) != 1)) + return (false); + switch (argv[*arg_ptr][0]) + { + case 'b': /* block special */ + type_cell = S_IFBLK; + break; + case 'c': /* character special */ + type_cell = S_IFCHR; + break; + case 'd': /* directory */ + type_cell = S_IFDIR; + break; + case 'f': /* regular file */ + type_cell = S_IFREG; + break; +#ifdef S_IFLNK + case 'l': /* symbolic link */ + type_cell = S_IFLNK; + break; +#endif +#ifdef S_IFIFO + case 'p': /* pipe */ + type_cell = S_IFIFO; + break; +#endif +#ifdef S_IFSOCK + case 's': /* socket */ + type_cell = S_IFSOCK; + break; +#endif + default: /* None of the above ... nuke 'em. */ + return (false); + } + our_pred = insert_primary (which_pred); + our_pred->args.type = type_cell; + (*arg_ptr)++; /* Move on to next argument. */ + return (true); +} + +/* If true, we've determined that the current fprintf predicate + uses stat information. */ +static boolean fprintf_stat_needed; + +static boolean +insert_fprintf (fp, func, argv, arg_ptr) + FILE *fp; + boolean (*func) (); + char *argv[]; + int *arg_ptr; +{ + char *format; /* Beginning of unprocessed format string. */ + register char *scan; /* Current address in scanning `format'. */ + register char *scan2; /* Address inside of element being scanned. */ + struct segment **segmentp; /* Address of current segment. */ + struct predicate *our_pred; + + format = argv[(*arg_ptr)++]; + + fprintf_stat_needed = false; /* Might be overridden later. */ + our_pred = insert_primary (func); + our_pred->side_effects = true; + our_pred->args.printf_vec.stream = fp; + segmentp = &our_pred->args.printf_vec.segment; + *segmentp = NULL; + + for (scan = format; *scan; scan++) + { + if (*scan == '\\') + { + scan2 = scan + 1; + if (*scan2 >= '0' && *scan2 <= '7') + { + register int n, i; + + for (i = n = 0; i < 3 && (*scan2 >= '0' && *scan2 <= '7'); + i++, scan2++) + n = 8 * n + *scan2 - '0'; + scan2--; + *scan = n; + } + else + { + switch (*scan2) + { + case 'a': + *scan = 7; + break; + case 'b': + *scan = '\b'; + break; + case 'c': + make_segment (segmentp, format, scan - format, KIND_STOP); + our_pred->need_stat = fprintf_stat_needed; + return (true); + case 'f': + *scan = '\f'; + break; + case 'n': + *scan = '\n'; + break; + case 'r': + *scan = '\r'; + break; + case 't': + *scan = '\t'; + break; + case 'v': + *scan = '\v'; + break; + case '\\': + /* *scan = '\\'; * it already is */ + break; + default: + error (0, 0, "warning: unrecognized escape `\\%c'", *scan2); + scan++; + continue; + } + } + segmentp = make_segment (segmentp, format, scan - format + 1, + KIND_PLAIN); + format = scan2 + 1; /* Move past the escape. */ + scan = scan2; /* Incremented immediately by `for'. */ + } + else if (*scan == '%') + { + if (scan[1] == '%') + { + segmentp = make_segment (segmentp, format, scan - format + 1, + KIND_PLAIN); + scan++; + format = scan + 1; + continue; + } + /* Scan past flags, width and precision, to verify kind. */ + for (scan2 = scan; *++scan2 && strchr ("-+ #", *scan2);) + /* Do nothing. */ ; + while (ISDIGIT (*scan2)) + scan2++; + if (*scan2 == '.') + for (scan2++; ISDIGIT (*scan2); scan2++) + /* Do nothing. */ ; + if (strchr ("abcdfFgGhHiklmnpPstuU", *scan2)) + { + segmentp = make_segment (segmentp, format, scan2 - format, + (int) *scan2); + scan = scan2; + format = scan + 1; + } + else if (strchr ("ACT", *scan2) && scan2[1]) + { + segmentp = make_segment (segmentp, format, scan2 - format, + *scan2 | (scan2[1] << 8)); + scan = scan2 + 1; + format = scan + 1; + continue; + } + else + { + /* An unrecognized % escape. Print the char after the %. */ + error (0, 0, "warning: unrecognized format directive `%%%c'", + *scan2); + segmentp = make_segment (segmentp, format, scan - format, + KIND_PLAIN); + format = scan + 1; + continue; + } + } + } + + if (scan > format) + make_segment (segmentp, format, scan - format, KIND_PLAIN); + our_pred->need_stat = fprintf_stat_needed; + return (true); +} + +/* Create a new fprintf segment in *SEGMENT, with type KIND, + from the text in FORMAT, which has length LEN. + Return the address of the `next' pointer of the new segment. */ + +static struct segment ** +make_segment (segment, format, len, kind) + struct segment **segment; + char *format; + int len, kind; +{ + char *fmt; + + *segment = (struct segment *) xmalloc (sizeof (struct segment)); + + (*segment)->kind = kind; + (*segment)->next = NULL; + (*segment)->text_len = len; + + fmt = (*segment)->text = xmalloc (len + 3); /* room for "ld\0" */ + strncpy (fmt, format, len); + fmt += len; + + switch (kind & 0xff) + { + case KIND_PLAIN: /* Plain text string, no % conversion. */ + case KIND_STOP: /* Terminate argument, no newline. */ + break; + + case 'a': /* atime in `ctime' format */ + case 'c': /* ctime in `ctime' format */ + case 'F': /* filesystem type */ + case 'g': /* group name */ + case 'l': /* object of symlink */ + case 't': /* mtime in `ctime' format */ + case 'u': /* user name */ + case 'A': /* atime in user-specified strftime format */ + case 'C': /* ctime in user-specified strftime format */ + case 'T': /* mtime in user-specified strftime format */ + fprintf_stat_needed = true; + /* FALLTHROUGH */ + case 'f': /* basename of path */ + case 'h': /* leading directories part of path */ + case 'H': /* ARGV element file was found under */ + case 'p': /* pathname */ + case 'P': /* pathname with ARGV element stripped */ + *fmt++ = 's'; + break; + + case 'b': /* size in 512-byte blocks */ + case 'k': /* size in 1K blocks */ + case 's': /* size in bytes */ + *fmt++ = 'l'; + /*FALLTHROUGH*/ + case 'n': /* number of links */ + fprintf_stat_needed = true; + /* FALLTHROUGH */ + case 'd': /* depth in search tree (0 = ARGV element) */ + *fmt++ = 'd'; + break; + + case 'i': /* inode number */ + *fmt++ = 'l'; + /*FALLTHROUGH*/ + case 'G': /* GID number */ + case 'U': /* UID number */ + *fmt++ = 'u'; + fprintf_stat_needed = true; + break; + + case 'm': /* mode as octal number (perms only) */ + *fmt++ = 'o'; + fprintf_stat_needed = true; + break; + } + *fmt = '\0'; + + return (&(*segment)->next); +} + +static boolean +insert_exec_ok (func, argv, arg_ptr) + boolean (*func) (); + char *argv[]; + int *arg_ptr; +{ + int start, end; /* Indexes in ARGV of start & end of cmd. */ + int num_paths; /* Number of args with path replacements. */ + int path_pos; /* Index in array of path replacements. */ + int vec_pos; /* Index in array of args. */ + struct predicate *our_pred; + struct exec_val *execp; /* Pointer for efficiency. */ + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + + /* Count the number of args with path replacements, up until the ';'. */ + start = *arg_ptr; + for (end = start, num_paths = 0; + (argv[end] != NULL) + && ((argv[end][0] != ';') || (argv[end][1] != '\0')); + end++) + if (strstr (argv[end], "{}")) + num_paths++; + /* Fail if no command given or no semicolon found. */ + if ((end == start) || (argv[end] == NULL)) + { + *arg_ptr = end; + return (false); + } + + our_pred = insert_primary (func); + our_pred->side_effects = true; + execp = &our_pred->args.exec_vec; + execp->paths = + (struct path_arg *) xmalloc (sizeof (struct path_arg) * (num_paths + 1)); + execp->vec = (char **) xmalloc (sizeof (char *) * (end - start + 1)); + /* Record the positions of all args, and the args with path replacements. */ + for (end = start, path_pos = vec_pos = 0; + (argv[end] != NULL) + && ((argv[end][0] != ';') || (argv[end][1] != '\0')); + end++) + { + register char *p; + + execp->paths[path_pos].count = 0; + for (p = argv[end]; *p; ++p) + if (p[0] == '{' && p[1] == '}') + { + execp->paths[path_pos].count++; + ++p; + } + if (execp->paths[path_pos].count) + { + execp->paths[path_pos].offset = vec_pos; + execp->paths[path_pos].origarg = argv[end]; + path_pos++; + } + execp->vec[vec_pos++] = argv[end]; + } + execp->paths[path_pos].offset = -1; + execp->vec[vec_pos] = NULL; + + if (argv[end] == NULL) + *arg_ptr = end; + else + *arg_ptr = end + 1; + return (true); +} + +/* Get a number of days and comparison type. + STR is the ASCII representation. + Set *NUM_DAYS to the number of days, taken as being from + the current moment (or possibly midnight). Thus the sense of the + comparison type appears to be reversed. + Set *COMP_TYPE to the kind of comparison that is requested. + + Return true if all okay, false if input error. + + Used by -atime, -ctime and -mtime (parsers) to + get the appropriate information for a time predicate processor. */ + +static boolean +get_num_days (str, num_days, comp_type) + char *str; + unsigned long *num_days; + enum comparison_type *comp_type; +{ + int len_days; /* length of field */ + + if (str == NULL) + return (false); + switch (str[0]) + { + case '+': + *comp_type = COMP_LT; + str++; + break; + case '-': + *comp_type = COMP_GT; + str++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *comp_type = COMP_EQ; + break; + default: + return (false); + } + + /* We know the first char has been reasonable. Find the + number of days to play with. */ + len_days = strspn (str, "0123456789"); + if ((len_days == 0) || (str[len_days] != '\0')) + return (false); + *num_days = (unsigned long) atol (str); + return (true); +} + +/* Insert a time predicate PRED. + ARGV is a pointer to the argument array. + ARG_PTR is a pointer to an index into the array, incremented if + all went well. + + Return true if input is valid, false if not. + + A new predicate node is assigned, along with an argument node + obtained with malloc. + + Used by -atime, -ctime, and -mtime parsers. */ + +static boolean +insert_time (argv, arg_ptr, pred) + char *argv[]; + int *arg_ptr; + PFB pred; +{ + struct predicate *our_pred; + unsigned long num_days; + enum comparison_type c_type; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num_days (argv[*arg_ptr], &num_days, &c_type)) + return (false); + our_pred = insert_primary (pred); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = cur_day_start - num_days * DAYSECS + + ((c_type == COMP_GT) ? DAYSECS - 1 : 0); + (*arg_ptr)++; +#ifdef DEBUG + printf ("inserting %s\n", our_pred->p_name); + printf (" type: %s %s ", + (c_type == COMP_GT) ? "gt" : + ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")), + (c_type == COMP_GT) ? " >" : + ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?"))); + printf ("%ld %s", our_pred->args.info.l_val, + ctime (&our_pred->args.info.l_val)); + if (c_type == COMP_EQ) + { + our_pred->args.info.l_val += DAYSECS; + printf (" < %ld %s", our_pred->args.info.l_val, + ctime (&our_pred->args.info.l_val)); + our_pred->args.info.l_val -= DAYSECS; + } +#endif /* DEBUG */ + return (true); +} + +/* Get a number with comparision information. + The sense of the comparision information is 'normal'; that is, + '+' looks for inums or links > than the number and '-' less than. + + STR is the ASCII representation of the number. + Set *NUM to the number. + Set *COMP_TYPE to the kind of comparison that is requested. + + Return true if all okay, false if input error. + + Used by the -inum and -links predicate parsers. */ + +static boolean +get_num (str, num, comp_type) + char *str; + unsigned long *num; + enum comparison_type *comp_type; +{ + int len_num; /* Length of field. */ + + if (str == NULL) + return (false); + switch (str[0]) + { + case '+': + *comp_type = COMP_GT; + str++; + break; + case '-': + *comp_type = COMP_LT; + str++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *comp_type = COMP_EQ; + break; + default: + return (false); + } + + /* We know the first char has been reasonable. Find the number of + days to play with. */ + len_num = strspn (str, "0123456789"); + if ((len_num == 0) || (str[len_num] != '\0')) + return (false); + *num = (unsigned long) atol (str); + return (true); +} + +/* Insert a number predicate. + ARGV is a pointer to the argument array. + *ARG_PTR is an index into ARGV, incremented if all went well. + *PRED is the predicate processor to insert. + + Return true if input is valid, false if error. + + A new predicate node is assigned, along with an argument node + obtained with malloc. + + Used by -inum and -links parsers. */ + +static boolean +insert_num (argv, arg_ptr, pred) + char *argv[]; + int *arg_ptr; + PFB pred; +{ + struct predicate *our_pred; + unsigned long num; + enum comparison_type c_type; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if (!get_num (argv[*arg_ptr], &num, &c_type)) + return (false); + our_pred = insert_primary (pred); + our_pred->args.info.kind = c_type; + our_pred->args.info.l_val = num; + (*arg_ptr)++; +#ifdef DEBUG + printf ("inserting %s\n", our_pred->p_name); + printf (" type: %s %s ", + (c_type == COMP_GT) ? "gt" : + ((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")), + (c_type == COMP_GT) ? " >" : + ((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?"))); + printf ("%ld\n", our_pred->args.info.l_val); +#endif /* DEBUG */ + return (true); +} + +static FILE * +open_output_file (path) + char *path; +{ + FILE *f; + + if (!strcmp (path, "/dev/stderr")) + return (stderr); + else if (!strcmp (path, "/dev/stdout")) + return (stdout); + f = fopen (path, "w"); + if (f == NULL) + error (1, errno, "%s", path); + return (f); +} diff --git a/find/pred.c b/find/pred.c new file mode 100644 index 0000000..9c8c682 --- /dev/null +++ b/find/pred.c @@ -0,0 +1,1525 @@ +/* pred.c -- execute the expression tree. + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "defs.h" +#include "modetype.h" +#include "wait.h" + +#if !defined(SIGCHLD) && defined(SIGCLD) +#define SIGCHLD SIGCLD +#endif + +#ifndef _POSIX_VERSION +struct passwd *getpwuid (); +struct group *getgrgid (); +#endif + +#if HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#ifdef CLOSEDIR_VOID +/* Fake a return value. */ +#define CLOSEDIR(d) (closedir (d), 0) +#else +#define CLOSEDIR(d) closedir (d) +#endif + +#ifndef S_IFLNK +#define lstat stat +#endif + +/* Extract or fake data from a `struct stat'. + ST_NBLOCKS: Number of 512-byte blocks in the file + (including indirect blocks). + HP-UX, perhaps uniquely, counts st_blocks in 1024-byte units. + This workaround loses when mixing HP-UX and 4BSD filesystems, though. */ +#ifdef _POSIX_SOURCE +# define ST_NBLOCKS(statp) (((statp)->st_size + 512 - 1) / 512) +#else +# ifndef HAVE_ST_BLOCKS +# define ST_NBLOCKS(statp) (st_blocks ((statp)->st_size)) +# else +# if defined(hpux) || defined(__hpux__) +# define ST_NBLOCKS(statp) ((statp)->st_blocks * 2) +# else +# define ST_NBLOCKS(statp) ((statp)->st_blocks) +# endif +# endif +#endif + +int lstat (); +int stat (); + +static boolean insert_lname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); +static boolean launch P_((struct predicate *pred_ptr)); +static char *format_date P_((time_t when, int kind)); + +#ifdef DEBUG +struct pred_assoc +{ + PFB pred_func; + char *pred_name; +}; + +struct pred_assoc pred_table[] = +{ + {pred_amin, "amin "}, + {pred_and, "and "}, + {pred_anewer, "anewer "}, + {pred_atime, "atime "}, + {pred_close, ") "}, + {pred_amin, "cmin "}, + {pred_cnewer, "cnewer "}, + {pred_comma, ", "}, + {pred_ctime, "ctime "}, + {pred_empty, "empty "}, + {pred_exec, "exec "}, + {pred_false, "false "}, + {pred_fprint, "fprint "}, + {pred_fprint0, "fprint0 "}, + {pred_fprintf, "fprintf "}, + {pred_fstype, "fstype "}, + {pred_gid, "gid "}, + {pred_group, "group "}, + {pred_ilname, "ilname "}, + {pred_iname, "iname "}, + {pred_inum, "inum "}, + {pred_ipath, "ipath "}, + {pred_links, "links "}, + {pred_lname, "lname "}, + {pred_ls, "ls "}, + {pred_amin, "mmin "}, + {pred_mtime, "mtime "}, + {pred_name, "name "}, + {pred_negate, "not "}, + {pred_newer, "newer "}, + {pred_nogroup, "nogroup "}, + {pred_nouser, "nouser "}, + {pred_ok, "ok "}, + {pred_open, "( "}, + {pred_or, "or "}, + {pred_path, "path "}, + {pred_perm, "perm "}, + {pred_print, "print "}, + {pred_print0, "print0 "}, + {pred_prune, "prune "}, + {pred_regex, "regex "}, + {pred_size, "size "}, + {pred_true, "true "}, + {pred_type, "type "}, + {pred_uid, "uid "}, + {pred_used, "used "}, + {pred_user, "user "}, + {pred_xtype, "xtype "}, + {0, "none "} +}; + +struct op_assoc +{ + short type; + char *type_name; +}; + +struct op_assoc type_table[] = +{ + {NO_TYPE, "no "}, + {PRIMARY_TYPE, "primary "}, + {UNI_OP, "uni_op "}, + {BI_OP, "bi_op "}, + {OPEN_PAREN, "open_paren "}, + {CLOSE_PAREN, "close_paren "}, + {-1, "unknown "} +}; + +struct prec_assoc +{ + short prec; + char *prec_name; +}; + +struct prec_assoc prec_table[] = +{ + {NO_PREC, "no "}, + {COMMA_PREC, "comma "}, + {OR_PREC, "or "}, + {AND_PREC, "and "}, + {NEGATE_PREC, "negate "}, + {MAX_PREC, "max "}, + {-1, "unknown "} +}; +#endif /* DEBUG */ + +/* Predicate processing routines. + + PATHNAME is the full pathname of the file being checked. + *STAT_BUF contains information about PATHNAME. + *PRED_PTR contains information for applying the predicate. + + Return true if the file passes this predicate, false if not. */ + +boolean +pred_amin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_atime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_atime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; + } + return (false); +} + +boolean +pred_and (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (pred_ptr->pred_left == NULL + || (*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left)) + { + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); + } + else + return (false); +} + +boolean +pred_anewer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (stat_buf->st_atime > pred_ptr->args.time) + return (true); + return (false); +} + +boolean +pred_atime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_atime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_atime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; + } + return (false); +} + +boolean +pred_close (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + return (true); +} + +boolean +pred_cmin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_ctime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_ctime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; + } + return (false); +} + +boolean +pred_cnewer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (stat_buf->st_ctime > pred_ptr->args.time) + return (true); + return (false); +} + +boolean +pred_comma (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (pred_ptr->pred_left != NULL) + (*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left); + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); +} + +boolean +pred_ctime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_ctime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_ctime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; + } + return (false); +} + +boolean +pred_empty (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (S_ISDIR (stat_buf->st_mode)) + { + DIR *d; + struct dirent *dp; + boolean empty = true; + + errno = 0; + d = opendir (rel_pathname); + if (d == NULL) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + for (dp = readdir (d); dp; dp = readdir (d)) + { + if (dp->d_name[0] != '.' + || (dp->d_name[1] != '\0' + && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) + { + empty = false; + break; + } + } + if (CLOSEDIR (d)) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + return (empty); + } + else if (S_ISREG (stat_buf->st_mode)) + return (stat_buf->st_size == 0); + else + return (false); +} + +boolean +pred_exec (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + int i; + int path_pos; + struct exec_val *execp; /* Pointer for efficiency. */ + + execp = &pred_ptr->args.exec_vec; + + /* Replace "{}" with the real path in each affected arg. */ + for (path_pos = 0; execp->paths[path_pos].offset >= 0; path_pos++) + { + register char *from, *to; + + i = execp->paths[path_pos].offset; + execp->vec[i] = + xmalloc (strlen (execp->paths[path_pos].origarg) + 1 + + (strlen (pathname) - 2) * execp->paths[path_pos].count); + for (from = execp->paths[path_pos].origarg, to = execp->vec[i]; *from; ) + if (from[0] == '{' && from[1] == '}') + { + to = stpcpy (to, pathname); + from += 2; + } + else + *to++ = *from++; + *to = *from; /* Copy null. */ + } + + i = launch (pred_ptr); + + /* Free the temporary args. */ + for (path_pos = 0; execp->paths[path_pos].offset >= 0; path_pos++) + free (execp->vec[execp->paths[path_pos].offset]); + + return (i); +} + +boolean +pred_false (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + return (false); +} + +boolean +pred_fls (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + list_file (pathname, rel_pathname, stat_buf, pred_ptr->args.stream); + return (true); +} + +boolean +pred_fprint (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + fputs (pathname, pred_ptr->args.stream); + putc ('\n', pred_ptr->args.stream); + return (true); +} + +boolean +pred_fprint0 (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + fputs (pathname, pred_ptr->args.stream); + putc (0, pred_ptr->args.stream); + return (true); +} + +boolean +pred_fprintf (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + FILE *fp = pred_ptr->args.printf_vec.stream; + struct segment *segment; + char *cp; + + for (segment = pred_ptr->args.printf_vec.segment; segment; + segment = segment->next) + { + if (segment->kind & 0xff00) /* Component of date. */ + { + time_t t; + + switch (segment->kind & 0xff) + { + case 'A': + t = stat_buf->st_atime; + break; + case 'C': + t = stat_buf->st_ctime; + break; + case 'T': + t = stat_buf->st_mtime; + break; + default: + abort (); + } + fprintf (fp, segment->text, + format_date (t, (segment->kind >> 8) & 0xff)); + continue; + } + + switch (segment->kind) + { + case KIND_PLAIN: /* Plain text string (no % conversion). */ + fwrite (segment->text, 1, segment->text_len, fp); + break; + case KIND_STOP: /* Terminate argument and flush output. */ + fwrite (segment->text, 1, segment->text_len, fp); + fflush (fp); + return (true); + case 'a': /* atime in `ctime' format. */ + cp = ctime (&stat_buf->st_atime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); + break; + case 'b': /* size in 512-byte blocks */ + fprintf (fp, segment->text, ST_NBLOCKS (stat_buf)); + break; + case 'c': /* ctime in `ctime' format */ + cp = ctime (&stat_buf->st_ctime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); + break; + case 'd': /* depth in search tree */ + fprintf (fp, segment->text, curdepth); + break; + case 'f': /* basename of path */ + cp = strrchr (pathname, '/'); + if (cp) + cp++; + else + cp = pathname; + fprintf (fp, segment->text, cp); + break; + case 'F': /* filesystem type */ + fprintf (fp, segment->text, + filesystem_type (pathname, rel_pathname, stat_buf)); + break; + case 'g': /* group name */ + { + struct group *g; + + g = getgrgid (stat_buf->st_gid); + if (g) + { + segment->text[segment->text_len] = 's'; + fprintf (fp, segment->text, g->gr_name); + break; + } + /* else fallthru */ + } + case 'G': /* GID number */ + segment->text[segment->text_len] = 'u'; + fprintf (fp, segment->text, stat_buf->st_gid); + break; + case 'h': /* leading directories part of path */ + { + char cc; + + cp = strrchr (pathname, '/'); + if (cp == NULL) /* No leading directories. */ + break; + cc = *cp; + *cp = '\0'; + fprintf (fp, segment->text, pathname); + *cp = cc; + break; + } + case 'H': /* ARGV element file was found under */ + { + char cc = pathname[path_length]; + + pathname[path_length] = '\0'; + fprintf (fp, segment->text, pathname); + pathname[path_length] = cc; + break; + } + case 'i': /* inode number */ + fprintf (fp, segment->text, stat_buf->st_ino); + break; + case 'k': /* size in 1K blocks */ + fprintf (fp, segment->text, (ST_NBLOCKS (stat_buf) + 1) / 2); + break; + case 'l': /* object of symlink */ +#ifdef S_ISLNK + { + char *linkname = 0; + + if (S_ISLNK (stat_buf->st_mode)) + { + linkname = get_link_name (pathname, rel_pathname); + if (linkname == 0) + exit_status = 1; + } + if (linkname) + { + fprintf (fp, segment->text, linkname); + free (linkname); + } + else + fprintf (fp, segment->text, ""); + } +#endif /* S_ISLNK */ + break; + case 'm': /* mode as octal number (perms only) */ + fprintf (fp, segment->text, stat_buf->st_mode & 07777); + break; + case 'n': /* number of links */ + fprintf (fp, segment->text, stat_buf->st_nlink); + break; + case 'p': /* pathname */ + fprintf (fp, segment->text, pathname); + break; + case 'P': /* pathname with ARGV element stripped */ + if (curdepth) + { + cp = pathname + path_length; + if (*cp == '/') + /* Move past the slash between the ARGV element + and the rest of the pathname. But if the ARGV element + ends in a slash, we didn't add another, so we've + already skipped past it. */ + cp++; + } + else + cp = ""; + fprintf (fp, segment->text, cp); + break; + case 's': /* size in bytes */ + fprintf (fp, segment->text, stat_buf->st_size); + break; + case 't': /* mtime in `ctime' format */ + cp = ctime (&stat_buf->st_mtime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); + break; + case 'u': /* user name */ + { + struct passwd *p; + + p = getpwuid (stat_buf->st_uid); + if (p) + { + segment->text[segment->text_len] = 's'; + fprintf (fp, segment->text, p->pw_name); + break; + } + /* else fallthru */ + } + case 'U': /* UID number */ + segment->text[segment->text_len] = 'u'; + fprintf (fp, segment->text, stat_buf->st_uid); + break; + } + } + return (true); +} + +boolean +pred_fstype (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (strcmp (filesystem_type (pathname, rel_pathname, stat_buf), + pred_ptr->args.str) == 0) + return (true); + return (false); +} + +boolean +pred_gid (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_gid > pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_gid < pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if (stat_buf->st_gid == pred_ptr->args.info.l_val) + return (true); + break; + } + return (false); +} + +boolean +pred_group (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (pred_ptr->args.gid == stat_buf->st_gid) + return (true); + else + return (false); +} + +boolean +pred_ilname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + return insert_lname (pathname, stat_buf, pred_ptr, true); +} + +boolean +pred_iname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + char *base; + + base = basename (pathname); + if (fnmatch (pred_ptr->args.str, base, FNM_PERIOD | FNM_CASEFOLD) == 0) + return (true); + return (false); +} + +boolean +pred_inum (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_ino > pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_ino < pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if (stat_buf->st_ino == pred_ptr->args.info.l_val) + return (true); + break; + } + return (false); +} + +boolean +pred_ipath (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (fnmatch (pred_ptr->args.str, pathname, FNM_CASEFOLD) == 0) + return (true); + return (false); +} + +boolean +pred_links (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_nlink > pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_nlink < pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if (stat_buf->st_nlink == pred_ptr->args.info.l_val) + return (true); + break; + } + return (false); +} + +boolean +pred_lname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + return insert_lname (pathname, stat_buf, pred_ptr, false); +} + +static boolean +insert_lname (pathname, stat_buf, pred_ptr, ignore_case) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; + boolean ignore_case; +{ + boolean ret = false; +#ifdef S_ISLNK + if (S_ISLNK (stat_buf->st_mode)) + { + char *linkname = get_link_name (pathname, rel_pathname); + if (linkname) + { + if (fnmatch (pred_ptr->args.str, linkname, + ignore_case ? FNM_CASEFOLD : 0) == 0) + ret = true; + free (linkname); + } + } +#endif /* S_ISLNK */ + return (ret); +} + +boolean +pred_ls (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + list_file (pathname, rel_pathname, stat_buf, stdout); + return (true); +} + +boolean +pred_mmin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_mtime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_mtime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; + } + return (false); +} + +boolean +pred_mtime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_mtime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_mtime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; + } + return (false); +} + +boolean +pred_name (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + char *base; + + base = basename (pathname); + if (fnmatch (pred_ptr->args.str, base, FNM_PERIOD) == 0) + return (true); + return (false); +} + +boolean +pred_negate (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return (!(*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); +} + +boolean +pred_newer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (stat_buf->st_mtime > pred_ptr->args.time) + return (true); + return (false); +} + +boolean +pred_nogroup (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ +#ifdef CACHE_IDS + extern char *gid_unused; + + return gid_unused[(unsigned) stat_buf->st_gid]; +#else + return getgrgid (stat_buf->st_gid) == NULL; +#endif +} + +boolean +pred_nouser (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ +#ifdef CACHE_IDS + extern char *uid_unused; + + return uid_unused[(unsigned) stat_buf->st_uid]; +#else + return getpwuid (stat_buf->st_uid) == NULL; +#endif +} + +boolean +pred_ok (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + int i, yes; + + fflush (stdout); + fprintf (stderr, "< %s ... %s > ? ", + pred_ptr->args.exec_vec.vec[0], pathname); + fflush (stderr); + i = getchar (); + yes = (i == 'y' || i == 'Y'); + while (i != EOF && i != '\n') + i = getchar (); + if (!yes) + return (false); + return pred_exec (pathname, stat_buf, pred_ptr); +} + +boolean +pred_open (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + return (true); +} + +boolean +pred_or (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (pred_ptr->pred_left == NULL + || !(*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left)) + { + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); + } + else + return (true); +} + +boolean +pred_path (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (fnmatch (pred_ptr->args.str, pathname, 0) == 0) + return (true); + return (false); +} + +boolean +pred_perm (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (pred_ptr->args.perm & 010000) + { + /* Magic flag set in parse_perm: + true if at least the given bits are set. */ + if ((stat_buf->st_mode & 07777 & pred_ptr->args.perm) + == (pred_ptr->args.perm & 07777)) + return (true); + } + else if (pred_ptr->args.perm & 020000) + { + /* Magic flag set in parse_perm: + true if any of the given bits are set. */ + if ((stat_buf->st_mode & 07777) & pred_ptr->args.perm) + return (true); + } + else + { + /* True if exactly the given bits are set. */ + if ((stat_buf->st_mode & 07777) == pred_ptr->args.perm) + return (true); + } + return (false); +} + +boolean +pred_print (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + puts (pathname); + return (true); +} + +boolean +pred_print0 (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + fputs (pathname, stdout); + putc (0, stdout); + return (true); +} + +boolean +pred_prune (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + stop_at_current_level = true; + return (do_dir_first); /* This is what SunOS find seems to do. */ +} + +boolean +pred_regex (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + int len = strlen (pathname); + if (re_match (pred_ptr->args.regex, pathname, len, 0, + (struct re_registers *) NULL) == len) + return (true); + return (false); +} + +boolean +pred_size (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + unsigned long f_val; + + f_val = (stat_buf->st_size + pred_ptr->args.size.blocksize - 1) + / pred_ptr->args.size.blocksize; + switch (pred_ptr->args.size.kind) + { + case COMP_GT: + if (f_val > pred_ptr->args.size.size) + return (true); + break; + case COMP_LT: + if (f_val < pred_ptr->args.size.size) + return (true); + break; + case COMP_EQ: + if (f_val == pred_ptr->args.size.size) + return (true); + break; + } + return (false); +} + +boolean +pred_true (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + return (true); +} + +boolean +pred_type (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + unsigned long mode = stat_buf->st_mode; + unsigned long type = pred_ptr->args.type; + +#ifndef S_IFMT + /* POSIX system; check `mode' the slow way. */ + if ((S_ISBLK (mode) && type == S_IFBLK) + || (S_ISCHR (mode) && type == S_IFCHR) + || (S_ISDIR (mode) && type == S_IFDIR) + || (S_ISREG (mode) && type == S_IFREG) +#ifdef S_IFLNK + || (S_ISLNK (mode) && type == S_IFLNK) +#endif +#ifdef S_IFIFO + || (S_ISFIFO (mode) && type == S_IFIFO) +#endif +#ifdef S_IFSOCK + || (S_ISSOCK (mode) && type == S_IFSOCK) +#endif + ) +#else /* S_IFMT */ + /* Unix system; check `mode' the fast way. */ + if ((mode & S_IFMT) == type) +#endif /* S_IFMT */ + return (true); + else + return (false); +} + +boolean +pred_uid (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_uid > pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_uid < pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if (stat_buf->st_uid == pred_ptr->args.info.l_val) + return (true); + break; + } + return (false); +} + +boolean +pred_used (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + time_t delta; + + delta = stat_buf->st_atime - stat_buf->st_ctime; /* Use difftime? */ + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (delta > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (delta < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((delta >= (time_t) pred_ptr->args.info.l_val) + && (delta < (time_t) pred_ptr->args.info.l_val + DAYSECS)) + return (true); + break; + } + return (false); +} + +boolean +pred_user (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (pred_ptr->args.uid == stat_buf->st_uid) + return (true); + else + return (false); +} + +boolean +pred_xtype (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + struct stat sbuf; + int (*ystat) (); + + ystat = xstat == lstat ? stat : lstat; + if ((*ystat) (rel_pathname, &sbuf) != 0) + { + if (ystat == stat && errno == ENOENT) + /* Mimic behavior of ls -lL. */ + return (pred_type (pathname, stat_buf, pred_ptr)); + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + return (pred_type (pathname, &sbuf, pred_ptr)); +} + +/* 1) fork to get a child; parent remembers the child pid + 2) child execs the command requested + 3) parent waits for child; checks for proper pid of child + + Possible returns: + + ret errno status(h) status(l) + + pid x signal# 0177 stopped + pid x exit arg 0 term by _exit + pid x 0 signal # term by signal + -1 EINTR parent got signal + -1 other some other kind of error + + Return true only if the pid matches, status(l) is + zero, and the exit arg (status high) is 0. + Otherwise return false, possibly printing an error message. */ + +static boolean +launch (pred_ptr) + struct predicate *pred_ptr; +{ + int status; + pid_t wait_ret, child_pid; + struct exec_val *execp; /* Pointer for efficiency. */ + static int first_time = 1; + + execp = &pred_ptr->args.exec_vec; + + /* Make sure output of command doesn't get mixed with find output. */ + fflush (stdout); + fflush (stderr); + + /* Make sure to listen for the kids. */ + if (first_time) + { + first_time = 0; + signal (SIGCHLD, SIG_DFL); + } + + child_pid = fork (); + if (child_pid == -1) + error (1, errno, "cannot fork"); + if (child_pid == 0) + { + /* We be the child. */ +#ifndef HAVE_FCHDIR + if (chdir (starting_dir) < 0) + { + error (0, errno, "%s", starting_dir); + _exit (1); + } +#else + if (fchdir (starting_desc) < 0) + { + error (0, errno, "cannot return to starting directory"); + _exit (1); + } +#endif + execvp (execp->vec[0], execp->vec); + error (0, errno, "%s", execp->vec[0]); + _exit (1); + } + + wait_ret = wait (&status); + if (wait_ret == -1) + { + error (0, errno, "error waiting for %s", execp->vec[0]); + exit_status = 1; + return (false); + } + if (wait_ret != child_pid) + { + error (0, 0, "wait got pid %d, expected pid %d", wait_ret, child_pid); + exit_status = 1; + return (false); + } + if (WIFSTOPPED (status)) + { + error (0, 0, "%s stopped by signal %d", + execp->vec[0], WSTOPSIG (status)); + exit_status = 1; + return (false); + } + if (WIFSIGNALED (status)) + { + error (0, 0, "%s terminated by signal %d", + execp->vec[0], WTERMSIG (status)); + exit_status = 1; + return (false); + } + return (!WEXITSTATUS (status)); +} + +/* Return a static string formatting the time WHEN according to the + strftime format character KIND. */ + +static char * +format_date (when, kind) + time_t when; + int kind; +{ + static char fmt[3]; + static char buf[64]; /* More than enough space. */ + + if (kind == '@') + { + sprintf (buf, "%ld", when); + return (buf); + } + else + { + fmt[0] = '%'; + fmt[1] = kind; + fmt[2] = '\0'; + if (strftime (buf, sizeof (buf), fmt, localtime (&when))) + return (buf); + } + return ""; +} + +#ifdef DEBUG +/* Return a pointer to the string representation of + the predicate function PRED_FUNC. */ + +char * +find_pred_name (pred_func) + PFB pred_func; +{ + int i; + + for (i = 0; pred_table[i].pred_func != 0; i++) + if (pred_table[i].pred_func == pred_func) + break; + return (pred_table[i].pred_name); +} + +static char * +type_name (type) + short type; +{ + int i; + + for (i = 0; type_table[i].type != (short) -1; i++) + if (type_table[i].type == type) + break; + return (type_table[i].type_name); +} + +static char * +prec_name (prec) + short prec; +{ + int i; + + for (i = 0; prec_table[i].prec != (short) -1; i++) + if (prec_table[i].prec == prec) + break; + return (prec_table[i].prec_name); +} + +/* Walk the expression tree NODE to stdout. + INDENT is the number of levels to indent the left margin. */ + +void +print_tree (node, indent) + struct predicate *node; + int indent; +{ + int i; + + if (node == NULL) + return; + for (i = 0; i < indent; i++) + printf (" "); + printf ("pred = %s type = %s prec = %s addr = %x\n", + find_pred_name (node->pred_func), + type_name (node->p_type), prec_name (node->p_prec), node); + for (i = 0; i < indent; i++) + printf (" "); + printf ("left:\n"); + print_tree (node->pred_left, indent + 1); + for (i = 0; i < indent; i++) + printf (" "); + printf ("right:\n"); + print_tree (node->pred_right, indent + 1); +} + +/* Copy STR into BUF and trim blanks from the end of BUF. + Return BUF. */ + +static char * +blank_rtrim (str, buf) + char *str; + char *buf; +{ + int i; + + if (str == NULL) + return (NULL); + strcpy (buf, str); + i = strlen (buf) - 1; + while ((i >= 0) && ((buf[i] == ' ') || buf[i] == '\t')) + i--; + buf[++i] = '\0'; + return (buf); +} + +/* Print out the predicate list starting at NODE. */ + +void +print_list (node) + struct predicate *node; +{ + struct predicate *cur; + char name[256]; + + cur = node; + while (cur != NULL) + { + printf ("%s ", blank_rtrim (find_pred_name (cur->pred_func), name)); + cur = cur->pred_next; + } + printf ("\n"); +} +#endif /* DEBUG */ diff --git a/find/tree.c b/find/tree.c new file mode 100644 index 0000000..2209242 --- /dev/null +++ b/find/tree.c @@ -0,0 +1,433 @@ +/* tree.c -- helper functions to build and evaluate the expression tree. + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include "defs.h" + +static struct predicate *scan_rest P_((struct predicate **input, struct predicate *head, int prev_prec)); +static void merge_pred P_((struct predicate *beg_list, struct predicate *end_list, struct predicate **last_p)); +static struct predicate *set_new_parent P_((struct predicate *curr, enum predicate_precedence high_prec, struct predicate **prevp)); + +/* Return a pointer to a tree that represents the + expression prior to non-unary operator *INPUT. + Set *INPUT to point at the next input predicate node. + + Only accepts the following: + + + expression [operators of higher precedence] + + (arbitrary expression) + (arbitrary expression) + + In other words, you can not start out with a bi_op or close_paren. + + If the following operator (if any) is of a higher precedence than + PREV_PREC, the expression just nabbed is part of a following + expression, which really is the expression that should be handed to + our caller, so get_expr recurses. */ + +struct predicate * +get_expr (input, prev_prec) + struct predicate **input; + short prev_prec; +{ + struct predicate *next; + + if (*input == NULL) + error (1, 0, "invalid expression"); + switch ((*input)->p_type) + { + case NO_TYPE: + case BI_OP: + case CLOSE_PAREN: + error (1, 0, "invalid expression"); + break; + + case PRIMARY_TYPE: + next = *input; + *input = (*input)->pred_next; + break; + + case UNI_OP: + next = *input; + *input = (*input)->pred_next; + next->pred_right = get_expr (input, NEGATE_PREC); + break; + + case OPEN_PAREN: + *input = (*input)->pred_next; + next = get_expr (input, NO_PREC); + if ((*input == NULL) + || ((*input)->p_type != CLOSE_PAREN)) + error (1, 0, "invalid expression"); + *input = (*input)->pred_next; /* move over close */ + break; + + default: + error (1, 0, "oops -- invalid expression type!"); + break; + } + + /* We now have the first expression and are positioned to check + out the next operator. If NULL, all done. Otherwise, if + PREV_PREC < the current node precedence, we must continue; + the expression we just nabbed is more tightly bound to the + following expression than to the previous one. */ + if (*input == NULL) + return (next); + if ((int) (*input)->p_prec > (int) prev_prec) + { + next = scan_rest (input, next, prev_prec); + if (next == NULL) + error (1, 0, "invalid expression"); + } + return (next); +} + +/* Scan across the remainder of a predicate input list starting + at *INPUT, building the rest of the expression tree to return. + Stop at the first close parenthesis or the end of the input list. + Assumes that get_expr has been called to nab the first element + of the expression tree. + + *INPUT points to the current input predicate list element. + It is updated as we move along the list to point to the + terminating input element. + HEAD points to the predicate element that was obtained + by the call to get_expr. + PREV_PREC is the precedence of the previous predicate element. */ + +static struct predicate * +scan_rest (input, head, prev_prec) + struct predicate **input; + struct predicate *head; + short prev_prec; +{ + struct predicate *tree; /* The new tree we are building. */ + + if ((*input == NULL) || ((*input)->p_type == CLOSE_PAREN)) + return (NULL); + tree = head; + while ((*input != NULL) && ((int) (*input)->p_prec > (int) prev_prec)) + { + switch ((*input)->p_type) + { + case NO_TYPE: + case PRIMARY_TYPE: + case UNI_OP: + case OPEN_PAREN: + error (1, 0, "invalid expression"); + break; + + case BI_OP: + (*input)->pred_left = tree; + tree = *input; + *input = (*input)->pred_next; + tree->pred_right = get_expr (input, tree->p_prec); + break; + + case CLOSE_PAREN: + return (tree); + + default: + error (1, 0, "oops -- invalid expression type!"); + break; + } + } + return (tree); +} + +/* Optimize the ordering of the predicates in the tree. Rearrange + them to minimize work. Strategies: + * Evaluate predicates that don't need inode information first; + the predicates are divided into 1 or more groups separated by + predicates (if any) which have "side effects", such as printing. + The grouping implements the partial ordering on predicates which + those with side effects impose. + * Place -name, -path, and -regex at the front of a group, with + -name and -path ahead of -regex. Predicates that are moved to the + front of a group by definition do not have side effects. + + This routine "normalizes" the predicate tree by ensuring that + all expression predicates have AND (or OR or COMMA) parent nodes + which are linked along the left edge of the expression tree. + This makes manipulation of subtrees easier. + + EVAL_TREEP points to the root pointer of the predicate tree + to be rearranged. opt_expr may return a new root pointer there. + Return true if the tree contains side effects, false if not. */ + +boolean +opt_expr (eval_treep) + struct predicate **eval_treep; +{ + /* List of -name and -path predicates to move. */ + struct predicate *name_list = NULL; + struct predicate *end_name_list = NULL; + /* List of -regex predicates to move. */ + struct predicate *regex_list = NULL; + struct predicate *end_regex_list = NULL; + struct predicate *curr; + struct predicate **prevp; /* Address of `curr' node. */ + struct predicate **last_sidep; /* Last predicate with side effects. */ + PFB pred_func; + enum predicate_type p_type; + boolean has_side_effects = false; /* Return value. */ + enum predicate_precedence prev_prec, /* precedence of last BI_OP in branch */ + biop_prec; /* topmost BI_OP precedence in branch */ + + + if (eval_treep == NULL || *eval_treep == NULL) + return (false); + + /* Set up to normalize tree as a left-linked list of ANDs or ORs. + Set `curr' to the leftmost node, `prevp' to its address, and + `pred_func' to the predicate type of its parent. */ + prevp = eval_treep; + prev_prec = AND_PREC; + curr = *prevp; + while (curr->pred_left != NULL) + { + prevp = &curr->pred_left; + prev_prec = curr->p_prec; /* must be a BI_OP */ + curr = curr->pred_left; + } + + /* Link in the appropriate BI_OP for the last expression, if needed. */ + if (curr->p_type != BI_OP) + set_new_parent (curr, prev_prec, prevp); + +#ifdef DEBUG + /* Normalized tree. */ + printf ("Normalized Eval Tree:\n"); + print_tree (*eval_treep, 0); +#endif + + /* Rearrange the predicates. */ + prevp = eval_treep; + if ((*prevp) && (*prevp)->p_type == BI_OP) + biop_prec = (*prevp)->p_prec; + while ((curr = *prevp) != NULL) + { + /* If there is a BI_OP of different precedence from the first + in the pred_left chain, create a new parent of the + original precedence, link the new parent to the left of the + previous and link CURR to the right of the new parent. + This preserves the precedence of expressions in the tree + in case we rearrange them. */ + if (curr->p_type == BI_OP) + { + if (curr->p_prec != biop_prec) + curr = set_new_parent(curr, biop_prec, prevp); + else + biop_prec = curr->p_prec; + } + + /* See which predicate type we have. */ + p_type = curr->pred_right->p_type; + pred_func = curr->pred_right->pred_func; + + switch (p_type) + { + case NO_TYPE: + case PRIMARY_TYPE: + /* If it's one of our special primaries, move it to the + front of the list for that primary. */ + if (pred_func == pred_name || pred_func == pred_path) + { + *prevp = curr->pred_left; + curr->pred_left = name_list; + name_list = curr; + + if (end_name_list == NULL) + end_name_list = curr; + + continue; + } + + if (pred_func == pred_regex) + { + *prevp = curr->pred_left; + curr->pred_left = regex_list; + regex_list = curr; + + if (end_regex_list == NULL) + end_regex_list = curr; + + continue; + } + + break; + + case UNI_OP: + /* For NOT, check the expression trees below the NOT. */ + curr->pred_right->side_effects + = opt_expr (&curr->pred_right->pred_right); + break; + + case BI_OP: + /* For nested AND or OR, recurse (AND/OR form layers on the left of + the tree), and continue scanning this level of AND or OR. */ + curr->pred_right->side_effects = opt_expr (&curr->pred_right); + break; + + /* At this point, get_expr and scan_rest have already removed + all of the user's parentheses. */ + + default: + error (1, 0, "oops -- invalid expression type!"); + break; + } + + if (curr->pred_right->side_effects == true) + { + last_sidep = prevp; + + /* Incorporate lists and reset list pointers for this group. */ + if (name_list != NULL) + { + merge_pred (name_list, end_name_list, last_sidep); + name_list = end_name_list = NULL; + } + + if (regex_list != NULL) + { + merge_pred (regex_list, end_regex_list, last_sidep); + regex_list = end_regex_list = NULL; + } + + has_side_effects = true; + } + + prevp = &curr->pred_left; + } + + /* Do final list merges. */ + last_sidep = prevp; + if (name_list != NULL) + merge_pred (name_list, end_name_list, last_sidep); + if (regex_list != NULL) + merge_pred (regex_list, end_regex_list, last_sidep); + + return (has_side_effects); +} + +/* Link in a new parent BI_OP node for CURR, at *PREVP, with precedence + HIGH_PREC. */ + +static struct predicate * +set_new_parent (curr, high_prec, prevp) + struct predicate *curr; + enum predicate_precedence high_prec; + struct predicate **prevp; +{ + struct predicate *new_parent; + + new_parent = (struct predicate *) xmalloc (sizeof (struct predicate)); + new_parent->p_type = BI_OP; + new_parent->p_prec = high_prec; + new_parent->need_stat = false; + + switch (high_prec) + { + case COMMA_PREC: + new_parent->pred_func = pred_comma; + break; + case OR_PREC: + new_parent->pred_func = pred_or; + break; + case AND_PREC: + new_parent->pred_func = pred_and; + break; + default: + ; /* empty */ + } + + new_parent->side_effects = false; + new_parent->args.str = NULL; + new_parent->pred_next = NULL; + + /* Link in new_parent. + Pushes rest of left branch down 1 level to new_parent->pred_right. */ + new_parent->pred_left = NULL; + new_parent->pred_right = curr; + *prevp = new_parent; + +#ifdef DEBUG + new_parent->p_name = (char *) find_pred_name (new_parent->pred_func); +#endif /* DEBUG */ + + return (new_parent); +} + +/* Merge the predicate list that starts at BEG_LIST and ends at END_LIST + into the tree at LAST_P. */ + +static void +merge_pred (beg_list, end_list, last_p) + struct predicate *beg_list, *end_list, **last_p; +{ + end_list->pred_left = *last_p; + *last_p = beg_list; +} + +/* Find the first node in expression tree TREE that requires + a stat call and mark the operator above it as needing a stat + before calling the node. Since the expression precedences + are represented in the tree, some preds that need stat may not + get executed (because the expression value is determined earlier.) + So every expression needing stat must be marked as such, not just + the earliest, to be sure to obtain the stat. This still guarantees + that a stat is made as late as possible. Return true if the top node + in TREE requires a stat, false if not. */ + +boolean +mark_stat (tree) + struct predicate *tree; +{ + /* The tree is executed in-order, so walk this way (apologies to Aerosmith) + to find the first predicate for which the stat is needed. */ + switch (tree->p_type) + { + case NO_TYPE: + case PRIMARY_TYPE: + return tree->need_stat; + + case UNI_OP: + if (mark_stat (tree->pred_right)) + tree->need_stat = true; + return (false); + + case BI_OP: + /* ANDs and ORs are linked along ->left ending in NULL. */ + if (tree->pred_left != NULL) + mark_stat (tree->pred_left); + + if (mark_stat (tree->pred_right)) + tree->need_stat = true; + + return (false); + + default: + error (1, 0, "oops -- invalid expression type!"); + return (false); + } +} diff --git a/find/util.c b/find/util.c new file mode 100644 index 0000000..95ce57e --- /dev/null +++ b/find/util.c @@ -0,0 +1,158 @@ +/* util.c -- functions for initializing new tree elements, and other things. + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include +#include "defs.h" + +/* Return the last component of pathname FNAME, with leading slashes + compressed into one slash. */ + +char * +basename (fname) + char *fname; +{ + char *p; + + /* For "/", "//", etc., return "/". */ + for (p = fname; *p == '/'; ++p) + /* Do nothing. */ ; + if (*p == '\0') + return p - 1; + p = strrchr (fname, '/'); + return (p == NULL ? fname : p + 1); +} + +/* Return a pointer to a new predicate structure, which has been + linked in as the last one in the predicates list. + + Set `predicates' to point to the start of the predicates list. + Set `last_pred' to point to the new last predicate in the list. + + Set all cells in the new structure to the default values. */ + +struct predicate * +get_new_pred () +{ + register struct predicate *new_pred; + + if (predicates == NULL) + { + predicates = (struct predicate *) + xmalloc (sizeof (struct predicate)); + last_pred = predicates; + } + else + { + new_pred = (struct predicate *) xmalloc (sizeof (struct predicate)); + last_pred->pred_next = new_pred; + last_pred = new_pred; + } + last_pred->pred_func = NULL; +#ifdef DEBUG + last_pred->p_name = NULL; +#endif /* DEBUG */ + last_pred->p_type = NO_TYPE; + last_pred->p_prec = NO_PREC; + last_pred->side_effects = false; + last_pred->need_stat = true; + last_pred->args.str = NULL; + last_pred->pred_next = NULL; + last_pred->pred_left = NULL; + last_pred->pred_right = NULL; + return (last_pred); +} + +/* Return a pointer to a new predicate, with operator check. + Like get_new_pred, but it checks to make sure that the previous + predicate is an operator. If it isn't, the AND operator is inserted. */ + +struct predicate * +get_new_pred_chk_op () +{ + struct predicate *new_pred; + + if (last_pred) + switch (last_pred->p_type) + { + case NO_TYPE: + error (1, 0, "oops -- invalid default insertion of and!"); + break; + + case PRIMARY_TYPE: + case CLOSE_PAREN: + new_pred = get_new_pred (); + new_pred->pred_func = pred_and; +#ifdef DEBUG + new_pred->p_name = find_pred_name (pred_and); +#endif /* DEBUG */ + new_pred->p_type = BI_OP; + new_pred->p_prec = AND_PREC; + new_pred->need_stat = false; + new_pred->args.str = NULL; + + default: + break; + } + return (get_new_pred ()); +} + +/* Add a primary of predicate type PRED_FUNC to the predicate input list. + + Return a pointer to the predicate node just inserted. + + Fills in the following cells of the new predicate node: + + pred_func PRED_FUNC + args(.str) NULL + p_type PRIMARY_TYPE + p_prec NO_PREC + + Other cells that need to be filled in are defaulted by + get_new_pred_chk_op, which is used to insure that the prior node is + either not there at all (we are the very first node) or is an + operator. */ + +struct predicate * +insert_primary (pred_func) + boolean (*pred_func) (); +{ + struct predicate *new_pred; + + new_pred = get_new_pred_chk_op (); + new_pred->pred_func = pred_func; +#ifdef DEBUG + new_pred->p_name = find_pred_name (pred_func); +#endif /* DEBUG */ + new_pred->args.str = NULL; + new_pred->p_type = PRIMARY_TYPE; + new_pred->p_prec = NO_PREC; + return (new_pred); +} + +void +usage (msg) + char *msg; +{ + if (msg) + fprintf (stderr, "%s: %s\n", program_name, msg); + fprintf (stderr, "\ +Usage: %s [path...] [expression]\n", program_name); + exit (1); +} diff --git a/find/version.c b/find/version.c new file mode 100644 index 0000000..460883f --- /dev/null +++ b/find/version.c @@ -0,0 +1 @@ +char *version_string = "4.1"; diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..ab74c88 --- /dev/null +++ b/install-sh @@ -0,0 +1,238 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +tranformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..568061f --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,19 @@ +LIBRARIES = find +find_SOURCES = regex.c dirname.c error.c filemode.c getopt.c getopt1.c \ +idcache.c listfile.c modechange.c nextelem.c savedir.c xmalloc.c getline.c \ +xstrdup.c xgetcwd.c fnmatch.c $(find_OPT_SOURCES) + +find_OPT_SOURCES = fileblocks.c memcmp.c memset.c mktime.c stpcpy.c strdup.c \ +strftime.c strspn.c strstr.c strtol.c alloca.c + +DIST_OTHER = fnmatch.h getopt.h modechange.h modetype.h pathmax.h \ +regex.h wait.h + +CONFIG_HEADER = ../config.h +INCLUDES = -I.. -I$(srcdir) + +fnmatch.o: fnmatch.h +getopt1.o: getopt.h +listfile.o xgetcwd.o: pathmax.h +modechange.o: modechange.h +regex.o: regex.h diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..534b294 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,136 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +CC = @CC@ +LEX = @LEX@ +YACC = @YACC@ +ANSI2KNR = ./ansi2knr + +DEFS = @DEFS@ +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +find_OBJECTS = regex.o dirname.o error.o filemode.o getopt.o getopt1.o idcache.o listfile.o modechange.o nextelem.o savedir.o xmalloc.o getline.o xstrdup.o xgetcwd.o fnmatch.o +LIBFILES = libfind.a +AR = ar +RANLIB = @RANLIB@ +ALLOCA = @ALLOCA@ +LIBOBJS = @LIBOBJS@ + +SOURCES = ${find_SOURCES} +DIST_CONF = Makefile.am Makefile.in +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +LIBRARIES = find +find_SOURCES = regex.c dirname.c error.c filemode.c getopt.c getopt1.c \ +idcache.c listfile.c modechange.c nextelem.c savedir.c xmalloc.c getline.c \ +xstrdup.c xgetcwd.c fnmatch.c $(find_OPT_SOURCES) + +find_OPT_SOURCES = fileblocks.c memcmp.c memset.c mktime.c stpcpy.c strdup.c \ +strftime.c strspn.c strstr.c strtol.c alloca.c + +DIST_OTHER = fnmatch.h getopt.h modechange.h modetype.h pathmax.h \ +regex.h wait.h + +CONFIG_HEADER = ../config.h +INCLUDES = -I.. -I$(srcdir) + +all:: ${ALL} + +.c.o: + $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $< + +$(find_OBJECTS): ../config.h +install:: install-libraries + +install-libraries: $(LIBFILES) + +uninstall:: uninstall-libraries + +uninstall-libraries: + +libfind.a: $(find_OBJECTS) @LIBOBJS@ @ALLOCA@ + rm -f libfind.a + $(AR) cru libfind.a $(find_OBJECTS) @LIBOBJS@ @ALLOCA@ + $(RANLIB) libfind.a + +mostlyclean: + rm -f *.o core + +clean: mostlyclean + rm -f $(PROGRAMS) $(LIBPROGRAMS) $(LIBFILES) $(TEXFILES) $(CLEANFILES) + +distclean: clean + rm -f Makefile *.tab.c $(DISTCLEANFILES) + rm -f config.cache config.log config.status ${CONFIG_HEADER} stamp-h + +realclean: distclean + rm -f TAGS $(INFOS) + +dist: $(DIST_FILES) $(DIST_DIRS) + -mkdir ../`cat ../distname`/$(subdir) + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file;}; \ + done + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +fnmatch.o: fnmatch.h +getopt1.o: getopt.h +listfile.o xgetcwd.o: pathmax.h +modechange.o: modechange.h +regex.o: regex.h diff --git a/lib/alloca.c b/lib/alloca.c new file mode 100644 index 0000000..7020f32 --- /dev/null +++ b/lib/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 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 +#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 +#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/lib/dirname.c b/lib/dirname.c new file mode 100644 index 0000000..15d2596 --- /dev/null +++ b/lib/dirname.c @@ -0,0 +1,70 @@ +/* dirname.c -- return all but the last element in a path + Copyright (C) 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +#endif +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#else +#include +#ifndef strrchr +#define strrchr rindex +#endif +#endif + +/* Return the leading directories part of PATH, + allocated with malloc. If out of memory, return 0. + Assumes that trailing slashes have already been + removed. */ + +char * +dirname (path) + char *path; +{ + char *newpath; + char *slash; + int length; /* Length of result, not including NUL. */ + + slash = strrchr (path, '/'); + if (slash == 0) + { + /* File is in the current directory. */ + path = "."; + length = 1; + } + else + { + /* Remove any trailing slashes from the result. */ + while (slash > path && *slash == '/') + --slash; + + length = slash - path + 1; + } + newpath = (char *) malloc (length + 1); + if (newpath == 0) + return 0; + strncpy (newpath, path, length); + newpath[length] = 0; + return newpath; +} diff --git a/lib/error.c b/lib/error.c new file mode 100644 index 0000000..19c2ba8 --- /dev/null +++ b/lib/error.c @@ -0,0 +1,119 @@ +/* error.c -- error handler for noninteractive utilities + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie . */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#if HAVE_VPRINTF || HAVE_DOPRNT +# if __STDC__ +# include +# define VA_START(args, lastarg) va_start(args, lastarg) +# else +# include +# define VA_START(args, lastarg) va_start(args) +# endif +#else +# define va_alist a1, a2, a3, a4, a5, a6, a7, a8 +# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8; +#endif + +#if STDC_HEADERS +# include +# include +#else +void exit (); +#endif + +/* If NULL, error will flush stdout, then print on stderr the program + name, a colon and a space. Otherwise, error will call this + function without parameters instead. */ +void (*error_print_progname) () = NULL; + +/* The calling program should define program_name and set it to the + name of the executing program. */ +extern char *program_name; + +#if HAVE_STRERROR +char *strerror (); +#else +static char * +private_strerror (errnum) + int errnum; +{ + extern char *sys_errlist[]; + extern int sys_nerr; + + if (errnum > 0 && errnum <= sys_nerr) + return sys_errlist[errnum]; + return "Unknown system error"; +} +#define strerror private_strerror +#endif + +/* Print the program name and error message MESSAGE, which is a printf-style + format string with optional args. + If ERRNUM is nonzero, print its corresponding system error message. + Exit with status STATUS if it is nonzero. */ +/* VARARGS */ + +void +#if defined(VA_START) && __STDC__ +error (int status, int errnum, const char *message, ...) +#else +error (status, errnum, message, va_alist) + int status; + int errnum; + char *message; + va_dcl +#endif +{ +#ifdef VA_START + va_list args; +#endif + + if (error_print_progname) + (*error_print_progname) (); + else + { + fflush (stdout); + fprintf (stderr, "%s: ", program_name); + } + +#ifdef VA_START + VA_START (args, message); +# if HAVE_VPRINTF + vfprintf (stderr, message, args); +# else + _doprnt (message, args, stderr); +# endif + va_end (args); +#else + fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); +#endif + + if (errnum) + fprintf (stderr, ": %s", strerror (errnum)); + putc ('\n', stderr); + fflush (stderr); + if (status) + exit (status); +} diff --git a/lib/fileblocks.c b/lib/fileblocks.c new file mode 100644 index 0000000..83ac04f --- /dev/null +++ b/lib/fileblocks.c @@ -0,0 +1,66 @@ +/* Convert file size to number of blocks on System V-like machines. + Copyright (C) 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Brian L. Matthews, blm@6sceng.UUCP. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if !defined (HAVE_ST_BLOCKS) && !defined(_POSIX_SOURCE) +#include +#include + +#ifndef NINDIR +/* Some SysV's, like Irix, seem to lack these. Hope they're correct. */ +/* Size of a indirect block, in bytes. */ +#ifndef BSIZE +#define BSIZE 1024 +#endif + +/* Number of inode pointers per indirect block. */ +#define NINDIR (BSIZE/sizeof(daddr_t)) +#endif /* !NINDIR */ + +/* Number of direct block addresses in an inode. */ +#define NDIR 10 + +/* Return the number of 512-byte blocks in a file of SIZE bytes. */ + +long +st_blocks (size) + long size; +{ + long datablks = (size + 512 - 1) / 512; + long indrblks = 0; + + if (datablks > NDIR) + { + indrblks = (datablks - NDIR - 1) / NINDIR + 1; + + if (datablks > NDIR + NINDIR) + { + indrblks += (datablks - NDIR - NINDIR - 1) / (NINDIR * NINDIR) + 1; + + if (datablks > NDIR + NINDIR + NINDIR * NINDIR) + indrblks++; + } + } + + return datablks + indrblks; +} +#endif diff --git a/lib/filemode.c b/lib/filemode.c new file mode 100644 index 0000000..c86ee2f --- /dev/null +++ b/lib/filemode.c @@ -0,0 +1,237 @@ +/* filemode.c -- make a string describing file modes + Copyright (C) 1985, 1990, 1993 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifndef S_IREAD +#define S_IREAD S_IRUSR +#define S_IWRITE S_IWUSR +#define S_IEXEC S_IXUSR +#endif + +#ifdef STAT_MACROS_BROKEN +#undef S_ISBLK +#undef S_ISCHR +#undef S_ISDIR +#undef S_ISFIFO +#undef S_ISLNK +#undef S_ISMPB +#undef S_ISMPC +#undef S_ISNWK +#undef S_ISREG +#undef S_ISSOCK +#endif /* STAT_MACROS_BROKEN. */ + +#if !defined(S_ISBLK) && defined(S_IFBLK) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +void mode_string (); +static char ftypelet (); +static void rwx (); +static void setst (); + +/* filemodestring - fill in string STR with an ls-style ASCII + representation of the st_mode field of file stats block STATP. + 10 characters are stored in STR; no terminating null is added. + The characters stored in STR are: + + 0 File type. 'd' for directory, 'c' for character + special, 'b' for block special, 'm' for multiplex, + 'l' for symbolic link, 's' for socket, 'p' for fifo, + '-' for regular, '?' for any other file type + + 1 'r' if the owner may read, '-' otherwise. + + 2 'w' if the owner may write, '-' otherwise. + + 3 'x' if the owner may execute, 's' if the file is + set-user-id, '-' otherwise. + 'S' if the file is set-user-id, but the execute + bit isn't set. + + 4 'r' if group members may read, '-' otherwise. + + 5 'w' if group members may write, '-' otherwise. + + 6 'x' if group members may execute, 's' if the file is + set-group-id, '-' otherwise. + 'S' if it is set-group-id but not executable. + + 7 'r' if any user may read, '-' otherwise. + + 8 'w' if any user may write, '-' otherwise. + + 9 'x' if any user may execute, 't' if the file is "sticky" + (will be retained in swap space after execution), '-' + otherwise. + 'T' if the file is sticky but not executable. */ + +void +filemodestring (statp, str) + struct stat *statp; + char *str; +{ + mode_string (statp->st_mode, str); +} + +/* Like filemodestring, but only the relevant part of the `struct stat' + is given as an argument. */ + +void +mode_string (mode, str) + unsigned short mode; + char *str; +{ + str[0] = ftypelet ((long) mode); + rwx ((mode & 0700) << 0, &str[1]); + rwx ((mode & 0070) << 3, &str[4]); + rwx ((mode & 0007) << 6, &str[7]); + setst (mode, str); +} + +/* Return a character indicating the type of file described by + file mode BITS: + 'd' for directories + 'b' for block special files + 'c' for character special files + 'm' for multiplexor files + 'l' for symbolic links + 's' for sockets + 'p' for fifos + '-' for regular files + '?' for any other file type. */ + +static char +ftypelet (bits) + long bits; +{ +#ifdef S_ISBLK + if (S_ISBLK (bits)) + return 'b'; +#endif + if (S_ISCHR (bits)) + return 'c'; + if (S_ISDIR (bits)) + return 'd'; + if (S_ISREG (bits)) + return '-'; +#ifdef S_ISFIFO + if (S_ISFIFO (bits)) + return 'p'; +#endif +#ifdef S_ISLNK + if (S_ISLNK (bits)) + return 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK (bits)) + return 's'; +#endif +#ifdef S_ISMPC + if (S_ISMPC (bits)) + return 'm'; +#endif +#ifdef S_ISNWK + if (S_ISNWK (bits)) + return 'n'; +#endif + return '?'; +} + +/* Look at read, write, and execute bits in BITS and set + flags in CHARS accordingly. */ + +static void +rwx (bits, chars) + unsigned short bits; + char *chars; +{ + chars[0] = (bits & S_IREAD) ? 'r' : '-'; + chars[1] = (bits & S_IWRITE) ? 'w' : '-'; + chars[2] = (bits & S_IEXEC) ? 'x' : '-'; +} + +/* Set the 's' and 't' flags in file attributes string CHARS, + according to the file mode BITS. */ + +static void +setst (bits, chars) + unsigned short bits; + char *chars; +{ +#ifdef S_ISUID + if (bits & S_ISUID) + { + if (chars[3] != 'x') + /* Set-uid, but not executable by owner. */ + chars[3] = 'S'; + else + chars[3] = 's'; + } +#endif +#ifdef S_ISGID + if (bits & S_ISGID) + { + if (chars[6] != 'x') + /* Set-gid, but not executable by group. */ + chars[6] = 'S'; + else + chars[6] = 's'; + } +#endif +#ifdef S_ISVTX + if (bits & S_ISVTX) + { + if (chars[9] != 'x') + /* Sticky, but not executable by others. */ + chars[9] = 'T'; + else + chars[9] = 't'; + } +#endif +} diff --git a/lib/fnmatch.c b/lib/fnmatch.c new file mode 100644 index 0000000..2fb65b5 --- /dev/null +++ b/lib/fnmatch.c @@ -0,0 +1,200 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + + +/* 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__) + + +#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS) +extern int errno; +#endif + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, nonzero if not. */ +int +fnmatch (pattern, string, flags) + const char *pattern; + const char *string; + int flags; +{ + register const char *p = pattern, *n = string; + register char c; + +/* Note that this evalutes C many times. */ +#define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c)) + + while ((c = *p++) != '\0') + { + c = FOLD (c); + + switch (c) + { + case '?': + if (*n == '\0') + return FNM_NOMATCH; + else if ((flags & FNM_FILE_NAME) && *n == '/') + return FNM_NOMATCH; + else if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + break; + + case '\\': + if (!(flags & FNM_NOESCAPE)) + { + c = *p++; + c = FOLD (c); + } + if (FOLD (*n) != c) + return FNM_NOMATCH; + break; + + case '*': + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + for (c = *p++; c == '?' || c == '*'; c = *p++, ++n) + if (((flags & FNM_FILE_NAME) && *n == '/') || + (c == '?' && *n == '\0')) + return FNM_NOMATCH; + + if (c == '\0') + return 0; + + { + char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; + c1 = FOLD (c1); + for (--p; *n != '\0'; ++n) + if ((c == '[' || FOLD (*n) == c1) && + fnmatch (p, n, flags & ~FNM_PERIOD) == 0) + return 0; + return FNM_NOMATCH; + } + + case '[': + { + /* Nonzero if the sense of the character class is inverted. */ + register int not; + + if (*n == '\0') + return FNM_NOMATCH; + + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + not = (*p == '!' || *p == '^'); + if (not) + ++p; + + c = *p++; + for (;;) + { + register char cstart = c, cend = c; + + if (!(flags & FNM_NOESCAPE) && c == '\\') + cstart = cend = *p++; + + cstart = cend = FOLD (cstart); + + if (c == '\0') + /* [ (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + c = FOLD (c); + + if ((flags & FNM_FILE_NAME) && c == '/') + /* [/] can never match. */ + return FNM_NOMATCH; + + if (c == '-' && *p != ']') + { + cend = *p++; + if (!(flags & FNM_NOESCAPE) && cend == '\\') + cend = *p++; + if (cend == '\0') + return FNM_NOMATCH; + cend = FOLD (cend); + + c = *p++; + } + + if (FOLD (*n) >= cstart && FOLD (*n) <= cend) + goto matched; + + if (c == ']') + break; + } + if (!not) + return FNM_NOMATCH; + break; + + matched:; + /* Skip the rest of the [...] that already matched. */ + while (c != ']') + { + if (c == '\0') + /* [... (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + if (!(flags & FNM_NOESCAPE) && c == '\\') + /* XXX 1003.2d11 is unclear if this is right. */ + ++p; + } + if (not) + return FNM_NOMATCH; + } + break; + + default: + if (c != FOLD (*n)) + return FNM_NOMATCH; + } + + ++n; + } + + if (*n == '\0') + return 0; + + if ((flags & FNM_LEADING_DIR) && *n == '/') + /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ + return 0; + + return FNM_NOMATCH; +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/lib/fnmatch.h b/lib/fnmatch.h new file mode 100644 index 0000000..69eab0c --- /dev/null +++ b/lib/fnmatch.h @@ -0,0 +1,67 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _FNMATCH_H + +#define _FNMATCH_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (__cplusplus) || (defined (__STDC__) && __STDC__) +#undef __P +#define __P(args) args +#else /* Not C++ or ANSI C. */ +#undef __P +#define __P(args) () +/* We can get away without defining `const' here only because in this file + it is used only inside the prototype for `fnmatch', which is elided in + non-ANSI C where `const' is problematical. */ +#endif /* C++ or ANSI C. */ + + +/* We #undef these before defining them because some losing systems + (HP-UX A.08.07 for example) define these in . */ +#undef FNM_PATHNAME +#undef FNM_NOESCAPE +#undef FNM_PERIOD + +/* Bits set in the FLAGS argument to `fnmatch'. */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE) +#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ +#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +#endif + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +extern int fnmatch __P ((const char *__pattern, const char *__string, + int __flags)); + +#ifdef __cplusplus +} +#endif + +#endif /* fnmatch.h */ diff --git a/lib/getline.c b/lib/getline.c new file mode 100644 index 0000000..519ff93 --- /dev/null +++ b/lib/getline.c @@ -0,0 +1,126 @@ +/* getline.c -- Replacement for GNU C library function getline + +Copyright (C) 1993 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 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. */ + +/* Written by Jan Brittenson, bson@gnu.ai.mit.edu. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#define NDEBUG +#include + +#if STDC_HEADERS +#include +#else +char *malloc (), *realloc (); +#endif + +/* Always add at least this many bytes when extending the buffer. */ +#define MIN_CHUNK 64 + +/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR + + OFFSET (and null-terminate it). *LINEPTR is a pointer returned from + malloc (or NULL), pointing to *N characters of space. It is realloc'd + as necessary. Return the number of characters read (not including the + null terminator), or -1 on error or EOF. */ + +int +getstr (lineptr, n, stream, terminator, offset) + char **lineptr; + size_t *n; + FILE *stream; + char terminator; + int offset; +{ + int nchars_avail; /* Allocated but unused chars in *LINEPTR. */ + char *read_pos; /* Where we're reading into *LINEPTR. */ + int ret; + + if (!lineptr || !n || !stream) + return -1; + + if (!*lineptr) + { + *n = MIN_CHUNK; + *lineptr = malloc (*n); + if (!*lineptr) + return -1; + } + + nchars_avail = *n - offset; + read_pos = *lineptr + offset; + + for (;;) + { + register int c = getc (stream); + + /* We always want at least one char left in the buffer, since we + always (unless we get an error while reading the first char) + NUL-terminate the line buffer. */ + + assert(*n - nchars_avail == read_pos - *lineptr); + if (nchars_avail < 1) + { + if (*n > MIN_CHUNK) + *n *= 2; + else + *n += MIN_CHUNK; + + nchars_avail = *n + *lineptr - read_pos; + *lineptr = realloc (*lineptr, *n); + if (!*lineptr) + return -1; + read_pos = *n - nchars_avail + *lineptr; + assert(*n - nchars_avail == read_pos - *lineptr); + } + + if (c == EOF || ferror (stream)) + { + /* Return partial line, if any. */ + if (read_pos == *lineptr) + return -1; + else + break; + } + + *read_pos++ = c; + nchars_avail--; + + if (c == terminator) + /* Return the line. */ + break; + } + + /* Done - NUL terminate and return the number of chars read. */ + *read_pos = '\0'; + + ret = read_pos - (*lineptr + offset); + return ret; +} + +int +getline (lineptr, n, stream) + char **lineptr; + size_t *n; + FILE *stream; +{ + return getstr (lineptr, n, stream, '\n', 0); +} diff --git a/lib/getopt.c b/lib/getopt.c new file mode 100644 index 0000000..43c0a6a --- /dev/null +++ b/lib/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 . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* 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 +#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 +#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/lib/getopt.h b/lib/getopt.h new file mode 100644 index 0000000..4ac33b7 --- /dev/null +++ b/lib/getopt.h @@ -0,0 +1,129 @@ +/* Declarations for getopt. + Copyright (C) 1989, 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. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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. */ + +extern char *optarg; + +/* 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. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/lib/getopt1.c b/lib/getopt1.c new file mode 100644 index 0000000..4580211 --- /dev/null +++ b/lib/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 +#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 + +/* 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 +#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 + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lib/idcache.c b/lib/idcache.c new file mode 100644 index 0000000..34dcc07 --- /dev/null +++ b/lib/idcache.c @@ -0,0 +1,210 @@ +/* idcache.c -- map user and group IDs, cached for speed + Copyright (C) 1985, 1988, 1989, 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#else +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifndef _POSIX_VERSION +struct passwd *getpwuid (); +struct passwd *getpwnam (); +struct group *getgrgid (); +struct group *getgrnam (); +#endif + +char *xmalloc (); +char *xstrdup (); + +struct userid +{ + union + { + uid_t u; + gid_t g; + } id; + char *name; + struct userid *next; +}; + +static struct userid *user_alist; + +/* The members of this list have names not in the local passwd file. */ +static struct userid *nouser_alist; + +/* Translate UID to a login name or a stringified number, + with cache. */ + +char * +getuser (uid) + uid_t uid; +{ + register struct userid *tail; + struct passwd *pwent; + char usernum_string[20]; + + for (tail = user_alist; tail; tail = tail->next) + if (tail->id.u == uid) + return tail->name; + + pwent = getpwuid (uid); + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->id.u = uid; + if (pwent == 0) + { + sprintf (usernum_string, "%u", (unsigned) uid); + tail->name = xstrdup (usernum_string); + } + else + tail->name = xstrdup (pwent->pw_name); + + /* Add to the head of the list, so most recently used is first. */ + tail->next = user_alist; + user_alist = tail; + return tail->name; +} + +/* Translate USER to a UID, with cache. + Return NULL if there is no such user. + (We also cache which user names have no passwd entry, + so we don't keep looking them up.) */ + +uid_t * +getuidbyname (user) + char *user; +{ + register struct userid *tail; + struct passwd *pwent; + + for (tail = user_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *user && !strcmp (tail->name, user)) + return &tail->id.u; + + for (tail = nouser_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *user && !strcmp (tail->name, user)) + return 0; + + pwent = getpwnam (user); + + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->name = xstrdup (user); + + /* Add to the head of the list, so most recently used is first. */ + if (pwent) + { + tail->id.u = pwent->pw_uid; + tail->next = user_alist; + user_alist = tail; + return &tail->id.u; + } + + tail->next = nouser_alist; + nouser_alist = tail; + return 0; +} + +/* Use the same struct as for userids. */ +static struct userid *group_alist; +static struct userid *nogroup_alist; + +/* Translate GID to a group name or a stringified number, + with cache. */ + +char * +getgroup (gid) + gid_t gid; +{ + register struct userid *tail; + struct group *grent; + char groupnum_string[20]; + + for (tail = group_alist; tail; tail = tail->next) + if (tail->id.g == gid) + return tail->name; + + grent = getgrgid (gid); + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->id.g = gid; + if (grent == 0) + { + sprintf (groupnum_string, "%u", (unsigned int) gid); + tail->name = xstrdup (groupnum_string); + } + else + tail->name = xstrdup (grent->gr_name); + + /* Add to the head of the list, so most recently used is first. */ + tail->next = group_alist; + group_alist = tail; + return tail->name; +} + +/* Translate GROUP to a UID, with cache. + Return NULL if there is no such group. + (We also cache which group names have no group entry, + so we don't keep looking them up.) */ + +gid_t * +getgidbyname (group) + char *group; +{ + register struct userid *tail; + struct group *grent; + + for (tail = group_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *group && !strcmp (tail->name, group)) + return &tail->id.g; + + for (tail = nogroup_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *group && !strcmp (tail->name, group)) + return 0; + + grent = getgrnam (group); + + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->name = xstrdup (group); + + /* Add to the head of the list, so most recently used is first. */ + if (grent) + { + tail->id.g = grent->gr_gid; + tail->next = group_alist; + group_alist = tail; + return &tail->id.g; + } + + tail->next = nogroup_alist; + nogroup_alist = tail; + return 0; +} diff --git a/lib/listfile.c b/lib/listfile.c new file mode 100644 index 0000000..7e32079 --- /dev/null +++ b/lib/listfile.c @@ -0,0 +1,273 @@ +/* listfile.c -- display a long listing of a file + Copyright (C) 1991, 1993 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "pathmax.h" + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#endif +#ifdef STDC_HEADERS +#include +#else +char *getenv (); +extern int errno; +#endif + +/* Since major is a function on SVR4, we can't use `ifndef major'. */ +#ifdef MAJOR_IN_MKDEV +#include +#define HAVE_MAJOR +#endif +#ifdef MAJOR_IN_SYSMACROS +#include +#define HAVE_MAJOR +#endif + +#ifdef STAT_MACROS_BROKEN +#undef S_ISCHR +#undef S_ISBLK +#undef S_ISLNK +#endif + +#ifndef S_ISCHR +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#ifndef S_ISBLK +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if defined(S_IFLNK) && !defined(S_ISLNK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif + +#if defined(S_ISLNK) +int readlink (); +#endif + +/* Extract or fake data from a `struct stat'. + ST_NBLOCKS: Number of 512-byte blocks in the file + (including indirect blocks). + HP-UX, perhaps uniquely, counts st_blocks in 1024-byte units. + This workaround loses when mixing HP-UX and 4BSD filesystems, though. */ +#ifdef _POSIX_SOURCE +# define ST_NBLOCKS(statp) (((statp)->st_size + 512 - 1) / 512) +#else +# ifndef HAVE_ST_BLOCKS +# define ST_NBLOCKS(statp) (st_blocks ((statp)->st_size)) +# else +# if defined(hpux) || defined(__hpux__) +# define ST_NBLOCKS(statp) ((statp)->st_blocks * 2) +# else +# define ST_NBLOCKS(statp) ((statp)->st_blocks) +# endif +# endif +#endif + +/* Convert B 512-byte blocks to kilobytes if K is nonzero, + otherwise return it unchanged. */ +#define convert_blocks(b, k) ((k) ? ((b) + 1) / 2 : (b)) + +#ifndef _POSIX_VERSION +struct passwd *getpwuid (); +struct group *getgrgid (); +#endif + +#ifdef major /* Might be defined in sys/types.h. */ +#define HAVE_MAJOR +#endif +#ifndef HAVE_MAJOR +#define major(dev) (((dev) >> 8) & 0xff) +#define minor(dev) ((dev) & 0xff) +#endif +#undef HAVE_MAJOR + +char *xmalloc (); +void error (); +void mode_string (); + +char *get_link_name (); +char *getgroup (); +char *getuser (); +void print_name_with_quoting (); + +/* NAME is the name to print. + RELNAME is the path to access it from the current directory. + STATP is the results of stat or lstat on it. + STREAM is the stdio stream to print on. */ + +void +list_file (name, relname, statp, stream) + char *name; + char *relname; + struct stat *statp; + FILE *stream; +{ + static int kilobytes = -1; /* -1 = uninitialized, 0 = 512, 1 = 1024. */ + char modebuf[20]; + char timebuf[40]; + time_t current_time = time ((time_t *) 0); + + if (kilobytes == -1) + kilobytes = getenv ("POSIXLY_CORRECT") == 0; + + mode_string (statp->st_mode, modebuf); + modebuf[10] = '\0'; + + strcpy (timebuf, ctime (&statp->st_mtime)); + if (current_time > statp->st_mtime + 6L * 30L * 24L * 60L * 60L /* Old. */ + || current_time < statp->st_mtime - 60L * 60L) /* In the future. */ + { + /* The file is fairly old or in the future. + POSIX says the cutoff is 6 months old; + approximate this by 6*30 days. + Allow a 1 hour slop factor for what is considered "the future", + to allow for NFS server/client clock disagreement. + Show the year instead of the time of day. */ + strcpy (timebuf + 11, timebuf + 19); + } + timebuf[16] = 0; + + fprintf (stream, "%6lu ", statp->st_ino); + + fprintf (stream, "%4u ", convert_blocks (ST_NBLOCKS (statp), kilobytes)); + + /* The space between the mode and the number of links is the POSIX + "optional alternate access method flag". */ + fprintf (stream, "%s %3u ", modebuf, statp->st_nlink); + + fprintf (stream, "%-8.8s ", getuser (statp->st_uid)); + + fprintf (stream, "%-8.8s ", getgroup (statp->st_gid)); + + if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode)) +#ifdef HAVE_ST_RDEV + fprintf (stream, "%3u, %3u ", major (statp->st_rdev), minor (statp->st_rdev)); +#else + fprintf (stream, " "); +#endif + else + fprintf (stream, "%8lu ", statp->st_size); + + fprintf (stream, "%s ", timebuf + 4); + + print_name_with_quoting (name, stream); + +#ifdef S_ISLNK + if (S_ISLNK (statp->st_mode)) + { + char *linkname = get_link_name (name, relname); + + if (linkname) + { + fputs (" -> ", stream); + print_name_with_quoting (linkname, stream); + free (linkname); + } + } +#endif + putc ('\n', stream); +} + +void +print_name_with_quoting (p, stream) + register char *p; + FILE *stream; +{ + register unsigned char c; + + while ((c = *p++) != '\0') + { + switch (c) + { + case '\\': + fprintf (stream, "\\\\"); + break; + + case '\n': + fprintf (stream, "\\n"); + break; + + case '\b': + fprintf (stream, "\\b"); + break; + + case '\r': + fprintf (stream, "\\r"); + break; + + case '\t': + fprintf (stream, "\\t"); + break; + + case '\f': + fprintf (stream, "\\f"); + break; + + case ' ': + fprintf (stream, "\\ "); + break; + + case '"': + fprintf (stream, "\\\""); + break; + + default: + if (c > 040 && c < 0177) + putc (c, stream); + else + fprintf (stream, "\\%03o", (unsigned int) c); + } + } +} + +#ifdef S_ISLNK +char * +get_link_name (name, relname) + char *name; + char *relname; +{ + register char *linkname; + register int linklen; + + /* st_size is wrong for symlinks on AIX, and on + mount points with some automounters. + So allocate a pessimistic PATH_MAX + 1 bytes. */ +#define LINK_BUF PATH_MAX + linkname = (char *) xmalloc (LINK_BUF + 1); + linklen = readlink (relname, linkname, LINK_BUF); + if (linklen < 0) + { + error (0, errno, "%s", name); + free (linkname); + return 0; + } + linkname[linklen] = '\0'; + return linkname; +} +#endif diff --git a/lib/memcmp.c b/lib/memcmp.c new file mode 100644 index 0000000..339b581 --- /dev/null +++ b/lib/memcmp.c @@ -0,0 +1,32 @@ +/* memcmp.c -- compare memory. + Return: + <0 if S1 < S2, + 0 if strings are identical, + >0 if S1 > S2. + Stops looking after N characters. Doesn't stop at nulls. + In the public domain. + By David MacKenzie . */ + +#include + +int +#if __STDC__ +memcmp (void const *v1, void const *v2, size_t n) +{ + register char *s1 = (char *) v1, *s2 = (char *) v2; +#else +memcmp (s1, s2, n) + register char *s1, *s2; + register unsigned n; +{ +#endif + register int diff; + + while (n--) + { + diff = *s1++ - *s2++; + if (diff) + return diff; + } + return 0; +} diff --git a/lib/memset.c b/lib/memset.c new file mode 100644 index 0000000..0e819f2 --- /dev/null +++ b/lib/memset.c @@ -0,0 +1,29 @@ +/* memset.c -- set an area of memory to a given value + Copyright (C) 1991 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +char * +memset (str, c, len) + char *str; + int c; + unsigned len; +{ + register char *st = str; + + while (len-- > 0) + *st++ = c; + return str; +} diff --git a/lib/mktime.c b/lib/mktime.c new file mode 100644 index 0000000..d441d8d --- /dev/null +++ b/lib/mktime.c @@ -0,0 +1,502 @@ +/* Copyright (C) 1993, 1994 Free Software Foundation, Inc. + Contributed by Noel Cragg (noel@cs.oberlin.edu), with fixes by + Michael E. Calwas (calwas@ttd.teradyne.com) and + Wade Hampton (tasi029@tmn.com). + +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* Define this to have a standalone program to test this implementation of + mktime. */ +/* #define DEBUG */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include /* Some systems define `time_t' here. */ +#include + + +#ifndef __isleap +/* Nonzero if YEAR is a leap year (every 4 years, + except every 100th isn't, and every 400th is). */ +#define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +#endif + +#ifndef __P +#if defined (__GNUC__) || (defined (__STDC__) && __STDC__) +#define __P(args) args +#else +#define __P(args) () +#endif /* GCC. */ +#endif /* Not __P. */ + +/* How many days are in each month. */ +const unsigned short int __mon_lengths[2][12] = + { + /* Normal years. */ + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + /* Leap years. */ + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + + +static int times_through_search; /* This library routine should never + hang -- make sure we always return + when we're searching for a value */ + + +#ifdef DEBUG + +#include +#include + +int debugging_enabled = 0; + +/* Print the values in a `struct tm'. */ +static void +printtm (it) + struct tm *it; +{ + printf ("%02d/%02d/%04d %02d:%02d:%02d (%s) yday:%03d dst:%d gmtoffset:%ld", + it->tm_mon + 1, + it->tm_mday, + it->tm_year + 1900, + it->tm_hour, + it->tm_min, + it->tm_sec, + it->tm_zone, + it->tm_yday, + it->tm_isdst, + it->tm_gmtoff); +} +#endif + + +static time_t +dist_tm (t1, t2) + struct tm *t1; + struct tm *t2; +{ + time_t distance = 0; + unsigned long int v1, v2; + int diff_flag = 0; + + v1 = v2 = 0; + +#define doit(x, secs) \ + v1 += t1->x * secs; \ + v2 += t2->x * secs; \ + if (!diff_flag) \ + { \ + if (t1->x < t2->x) \ + diff_flag = -1; \ + else if (t1->x > t2->x) \ + diff_flag = 1; \ + } + + doit (tm_year, 31536000); /* Okay, not all years have 365 days. */ + doit (tm_mon, 2592000); /* Okay, not all months have 30 days. */ + doit (tm_mday, 86400); + doit (tm_hour, 3600); + doit (tm_min, 60); + doit (tm_sec, 1); + +#undef doit + + /* We should also make sure that the sign of DISTANCE is correct -- if + DIFF_FLAG is positive, the distance should be positive and vice versa. */ + + distance = (v1 > v2) ? (v1 - v2) : (v2 - v1); + if (diff_flag < 0) + distance = -distance; + + if (times_through_search > 20) /* Arbitrary # of calls, but makes sure we + never hang if there's a problem with + this algorithm. */ + { + distance = diff_flag; + } + + /* We need this DIFF_FLAG business because it is forseeable that the + distance may be zero when, in actuality, the two structures are + different. This is usually the case when the dates are 366 days apart + and one of the years is a leap year. */ + + if (distance == 0 && diff_flag) + distance = 86400 * diff_flag; + + return distance; +} + + +/* MKTIME converts the values in a struct tm to a time_t. The values + in tm_wday and tm_yday are ignored; other values can be put outside + of legal ranges since they will be normalized. This routine takes + care of that normalization. */ + +void +do_normalization (tmptr) + struct tm *tmptr; +{ + +#define normalize(foo,x,y,bar); \ + while (tmptr->foo < x) \ + { \ + tmptr->bar--; \ + tmptr->foo = (y - (x - tmptr->foo) + 1); \ + } \ + while (tmptr->foo > y) \ + { \ + tmptr->foo = (x + (tmptr->foo - y) - 1); \ + tmptr->bar++; \ + } + + normalize (tm_sec, 0, 59, tm_min); + normalize (tm_min, 0, 59, tm_hour); + normalize (tm_hour, 0, 23, tm_mday); + + /* Do the month first, so day range can be found. */ + normalize (tm_mon, 0, 11, tm_year); + + /* Since the day range modifies the month, we should be careful how + we reference the array of month lengths -- it is possible that + the month will go negative, hence the modulo... + + Also, tm_year is the year - 1900, so we have to 1900 to have it + work correctly. */ + + normalize (tm_mday, 1, + __mon_lengths[__isleap (tmptr->tm_year + 1900)] + [((tmptr->tm_mon < 0) + ? (12 + (tmptr->tm_mon % 12)) + : (tmptr->tm_mon % 12)) ], + tm_mon); + + /* Do the month again, because the day may have pushed it out of range. */ + normalize (tm_mon, 0, 11, tm_year); + + /* Do the day again, because the month may have changed the range. */ + normalize (tm_mday, 1, + __mon_lengths[__isleap (tmptr->tm_year + 1900)] + [((tmptr->tm_mon < 0) + ? (12 + (tmptr->tm_mon % 12)) + : (tmptr->tm_mon % 12)) ], + tm_mon); + +#ifdef DEBUG + if (debugging_enabled) + { + printf (" After normalizing:\n "); + printtm (tmptr); + putchar ('\n'); + } +#endif + +} + + +/* Here's where the work gets done. */ + +#define BAD_STRUCT_TM ((time_t) -1) + +time_t +_mktime_internal (timeptr, producer) + struct tm *timeptr; + struct tm *(*producer) __P ((const time_t *)); +{ + struct tm our_tm; /* our working space */ + struct tm *me = &our_tm; /* a pointer to the above */ + time_t result; /* the value we return */ + + *me = *timeptr; /* copy the struct tm that was passed + in by the caller */ + + + /***************************/ + /* Normalize the structure */ + /***************************/ + + /* This routine assumes that the value of TM_ISDST is -1, 0, or 1. + If the user didn't pass it in that way, fix it. */ + + if (me->tm_isdst > 0) + me->tm_isdst = 1; + else if (me->tm_isdst < 0) + me->tm_isdst = -1; + + do_normalization (me); + + /* Get out of here if it's not possible to represent this struct. + If any of the values in the normalized struct tm are negative, + our algorithms won't work. Luckily, we only need to check the + year at this point; normalization guarantees that all values will + be in correct ranges EXCEPT the year. */ + + if (me->tm_year < 0) + return BAD_STRUCT_TM; + + /*************************************************/ + /* Find the appropriate time_t for the structure */ + /*************************************************/ + + /* Modified b-search -- make intelligent guesses as to where the + time might lie along the timeline, assuming that our target time + lies a linear distance (w/o considering time jumps of a + particular region). + + Assume that time does not fluctuate at all along the timeline -- + e.g., assume that a day will always take 86400 seconds, etc. -- + and come up with a hypothetical value for the time_t + representation of the struct tm TARGET, in relation to the guess + variable -- it should be pretty close! + + After testing this, the maximum number of iterations that I had + on any number that I tried was 3! Not bad. + + The reason this is not a subroutine is that we will modify some + fields in the struct tm (yday and mday). I've never felt good + about side-effects when writing structured code... */ + + { + struct tm *guess_tm; + time_t guess = 0; + time_t distance = 0; + time_t last_distance = 0; + + times_through_search = 0; + + do + { + guess += distance; + + times_through_search++; + + guess_tm = (*producer) (&guess); + +#ifdef DEBUG + if (debugging_enabled) + { + printf (" Guessing time_t == %d\n ", (int) guess); + printtm (guess_tm); + putchar ('\n'); + } +#endif + + /* How far is our guess from the desired struct tm? */ + distance = dist_tm (me, guess_tm); + + /* Handle periods of time where a period of time is skipped. + For example, 2:15 3 April 1994 does not exist, because DST + is in effect. The distance function will alternately + return values of 3600 and -3600, because it doesn't know + that the requested time doesn't exist. In these situations + (even if the skip is not exactly an hour) the distances + returned will be the same, but alternating in sign. We + want the later time, so check to see that the distance is + oscillating and we've chosen the correct of the two + possibilities. + + Useful: 3 Apr 94 765356300, 30 Oct 94 783496000 */ + + if ((distance == -last_distance) && (distance < last_distance)) + { + /* If the caller specified that the DST flag was off, it's + not possible to represent this time. */ + if (me->tm_isdst == 0) + { +#ifdef DEBUG + printf (" Distance is oscillating -- dst flag nixes struct!\n"); +#endif + return BAD_STRUCT_TM; + } + +#ifdef DEBUG + printf (" Distance is oscillating -- chose the later time.\n"); +#endif + distance = 0; + } + + if ((distance == 0) && (me->tm_isdst != -1) + && (me->tm_isdst != guess_tm->tm_isdst)) + { + /* If we're in this code, we've got the right time but the + wrong daylight savings flag. We need to move away from + the time that we have and approach the other time from + the other direction. That is, if I've requested the + non-DST version of a time and I get the DST version + instead, I want to put us forward in time and search + backwards to get the other time. I checked all of the + configuration files for the tz package -- no entry + saves more than two hours, so I think we'll be safe by + moving 24 hours in one direction. IF THE AMOUNT OF + TIME SAVED IN THE CONFIGURATION FILES CHANGES, THIS + VALUE MAY NEED TO BE ADJUSTED. Luckily, we can never + have more than one level of overlaps, or this would + never work. */ + +#define SKIP_VALUE 86400 + + if (guess_tm->tm_isdst == 0) + /* we got the later one, but want the earlier one */ + distance = -SKIP_VALUE; + else + distance = SKIP_VALUE; + +#ifdef DEBUG + printf (" Got the right time, wrong DST value -- adjusting\n"); +#endif + } + + last_distance = distance; + + } while (distance != 0); + + /* Check to see that the dst flag matches */ + + if (me->tm_isdst != -1) + { + if (me->tm_isdst != guess_tm->tm_isdst) + { +#ifdef DEBUG + printf (" DST flag doesn't match! FIXME?\n"); +#endif + return BAD_STRUCT_TM; + } + } + + result = guess; /* Success! */ + + /* On successful completion, the values of tm_wday and tm_yday + have to be set appropriately. */ + + /* me->tm_yday = guess_tm->tm_yday; + me->tm_mday = guess_tm->tm_mday; */ + + *me = *guess_tm; + } + + /* Update the caller's version of the structure */ + + *timeptr = *me; + + return result; +} + +time_t +#ifdef DEBUG /* make it work even if the system's + libc has it's own mktime routine */ +my_mktime (timeptr) +#else +mktime (timeptr) +#endif + struct tm *timeptr; +{ + return _mktime_internal (timeptr, localtime); +} + +#ifdef DEBUG +void +main (argc, argv) + int argc; + char *argv[]; +{ + int time; + int result_time; + struct tm *tmptr; + + if (argc == 1) + { + long q; + + printf ("starting long test...\n"); + + for (q = 10000000; q < 1000000000; q += 599) + { + struct tm *tm = localtime ((time_t *) &q); + if ((q % 10000) == 0) { printf ("%ld\n", q); fflush (stdout); } + if (q != my_mktime (tm)) + { printf ("failed for %ld\n", q); fflush (stdout); } + } + + printf ("test finished\n"); + + exit (0); + } + + if (argc != 2) + { + printf ("wrong # of args\n"); + exit (0); + } + + debugging_enabled = 1; /* We want to see the info */ + + ++argv; + time = atoi (*argv); + + tmptr = localtime ((time_t *) &time); + printf ("Localtime tells us that a time_t of %d represents\n ", time); + printtm (tmptr); + putchar ('\n'); + + printf (" Given localtime's return val, mktime returns %d which is\n ", + (int) my_mktime (tmptr)); + printtm (tmptr); + putchar ('\n'); + +#if 0 + tmptr->tm_sec -= 20; + tmptr->tm_min -= 20; + tmptr->tm_hour -= 20; + tmptr->tm_mday -= 20; + tmptr->tm_mon -= 20; + tmptr->tm_year -= 20; + tmptr->tm_gmtoff -= 20000; /* This has no effect! */ + tmptr->tm_zone = NULL; /* Nor does this! */ + tmptr->tm_isdst = -1; +#endif + + tmptr->tm_hour += 1; + tmptr->tm_isdst = -1; + + printf ("\n\nchanged ranges: "); + printtm (tmptr); + putchar ('\n'); + + result_time = my_mktime (tmptr); + printf ("\nmktime: %d\n", result_time); + + tmptr->tm_isdst = 0; + + printf ("\n\nchanged ranges: "); + printtm (tmptr); + putchar ('\n'); + + result_time = my_mktime (tmptr); + printf ("\nmktime: %d\n", result_time); +} +#endif /* DEBUG */ + + +/* +Local Variables: +compile-command: "gcc -g mktime.c -o mktime -DDEBUG" +End: +*/ diff --git a/lib/modechange.c b/lib/modechange.c new file mode 100644 index 0000000..3862599 --- /dev/null +++ b/lib/modechange.c @@ -0,0 +1,341 @@ +/* modechange.c -- file mode manipulation + Copyright (C) 1989, 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie */ + +/* The ASCII mode string is compiled into a linked list of `struct + modechange', which can then be applied to each file to be changed. + We do this instead of re-parsing the ASCII string for each file + because the compiled form requires less computation to use; when + changing the mode of many files, this probably results in a + performance gain. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "modechange.h" + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#ifdef STAT_MACROS_BROKEN +#undef S_ISDIR +#endif /* STAT_MACROS_BROKEN. */ + +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* Return newly allocated memory to hold one element of type TYPE. */ +#define talloc(type) ((type *) malloc (sizeof (type))) + +#define isodigit(c) ((c) >= '0' && (c) <= '7') + +static int oatoi (); + +/* Return a linked list of file mode change operations created from + MODE_STRING, an ASCII string that contains either an octal number + specifying an absolute mode, or symbolic mode change operations with + the form: + [ugoa...][[+-=][rwxXstugo...]...][,...] + MASKED_OPS is a bitmask indicating which symbolic mode operators (=+-) + should not affect bits set in the umask when no users are given. + Operators not selected in MASKED_OPS ignore the umask. + + Return MODE_INVALID if `mode_string' does not contain a valid + representation of file mode change operations; + return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */ + +struct mode_change * +mode_compile (mode_string, masked_ops) + register char *mode_string; + unsigned masked_ops; +{ + struct mode_change *head; /* First element of the linked list. */ + struct mode_change *change; /* An element of the linked list. */ + int i; /* General purpose temporary. */ + unsigned short umask_value; /* The umask value (surprise). */ + unsigned short affected_bits; /* Which bits in the mode are operated on. */ + unsigned short affected_masked; /* `affected_bits' modified by umask. */ + unsigned ops_to_mask; /* Operators to actually use umask on. */ + + i = oatoi (mode_string); + if (i >= 0) + { + if (i > 07777) + return MODE_INVALID; + head = talloc (struct mode_change); + if (head == NULL) + return MODE_MEMORY_EXHAUSTED; + head->next = NULL; + head->op = '='; + head->flags = 0; + head->value = i; + head->affected = 07777; /* Affect all permissions. */ + return head; + } + + umask_value = umask (0); + umask (umask_value); /* Restore the old value. */ + + head = NULL; +#ifdef lint + change = NULL; +#endif + --mode_string; + + /* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */ + do + { + affected_bits = 0; + ops_to_mask = 0; + /* Turn on all the bits in `affected_bits' for each group given. */ + for (++mode_string;; ++mode_string) + switch (*mode_string) + { + case 'u': + affected_bits |= 04700; + break; + case 'g': + affected_bits |= 02070; + break; + case 'o': + affected_bits |= 01007; + break; + case 'a': + affected_bits |= 07777; + break; + default: + goto no_more_affected; + } + + no_more_affected: + /* If none specified, affect all bits, except perhaps those + set in the umask. */ + if (affected_bits == 0) + { + affected_bits = 07777; + ops_to_mask = masked_ops; + } + + while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-') + { + /* Add the element to the tail of the list, so the operations + are performed in the correct order. */ + if (head == NULL) + { + head = talloc (struct mode_change); + if (head == NULL) + return MODE_MEMORY_EXHAUSTED; + change = head; + } + else + { + change->next = talloc (struct mode_change); + if (change->next == NULL) + { + mode_free (change); + return MODE_MEMORY_EXHAUSTED; + } + change = change->next; + } + + change->next = NULL; + change->op = *mode_string; /* One of "=+-". */ + affected_masked = affected_bits; + if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS + : *mode_string == '+' ? MODE_MASK_PLUS + : MODE_MASK_MINUS)) + affected_masked &= ~umask_value; + change->affected = affected_masked; + change->value = 0; + change->flags = 0; + + /* Set `value' according to the bits set in `affected_masked'. */ + for (++mode_string;; ++mode_string) + switch (*mode_string) + { + case 'r': + change->value |= 00444 & affected_masked; + break; + case 'w': + change->value |= 00222 & affected_masked; + break; + case 'X': + change->flags |= MODE_X_IF_ANY_X; + /* Fall through. */ + case 'x': + change->value |= 00111 & affected_masked; + break; + case 's': + /* Set the setuid/gid bits if `u' or `g' is selected. */ + change->value |= 06000 & affected_masked; + break; + case 't': + /* Set the "save text image" bit if `o' is selected. */ + change->value |= 01000 & affected_masked; + break; + case 'u': + /* Set the affected bits to the value of the `u' bits + on the same file. */ + if (change->value) + goto invalid; + change->value = 00700; + change->flags |= MODE_COPY_EXISTING; + break; + case 'g': + /* Set the affected bits to the value of the `g' bits + on the same file. */ + if (change->value) + goto invalid; + change->value = 00070; + change->flags |= MODE_COPY_EXISTING; + break; + case 'o': + /* Set the affected bits to the value of the `o' bits + on the same file. */ + if (change->value) + goto invalid; + change->value = 00007; + change->flags |= MODE_COPY_EXISTING; + break; + default: + goto no_more_values; + } + no_more_values:; + } + } while (*mode_string == ','); + if (*mode_string == 0) + return head; +invalid: + mode_free (head); + return MODE_INVALID; +} + +/* Return file mode OLDMODE, adjusted as indicated by the list of change + operations CHANGES. If OLDMODE is a directory, the type `X' + change affects it even if no execute bits were set in OLDMODE. + The returned value has the S_IFMT bits cleared. */ + +unsigned short +mode_adjust (oldmode, changes) + unsigned oldmode; + register struct mode_change *changes; +{ + unsigned short newmode; /* The adjusted mode and one operand. */ + unsigned short value; /* The other operand. */ + + newmode = oldmode & 07777; + + for (; changes; changes = changes->next) + { + if (changes->flags & MODE_COPY_EXISTING) + { + /* Isolate in `value' the bits in `newmode' to copy, given in + the mask `changes->value'. */ + value = newmode & changes->value; + + if (changes->value & 00700) + /* Copy `u' permissions onto `g' and `o'. */ + value |= (value >> 3) | (value >> 6); + else if (changes->value & 00070) + /* Copy `g' permissions onto `u' and `o'. */ + value |= (value << 3) | (value >> 3); + else + /* Copy `o' permissions onto `u' and `g'. */ + value |= (value << 3) | (value << 6); + + /* In order to change only `u', `g', or `o' permissions, + or some combination thereof, clear unselected bits. + This can not be done in mode_compile because the value + to which the `changes->affected' mask is applied depends + on the old mode of each file. */ + value &= changes->affected; + } + else + { + value = changes->value; + /* If `X', do not affect the execute bits if the file is not a + directory and no execute bits are already set. */ + if ((changes->flags & MODE_X_IF_ANY_X) + && !S_ISDIR (oldmode) + && (newmode & 00111) == 0) + value &= ~00111; /* Clear the execute bits. */ + } + + switch (changes->op) + { + case '=': + /* Preserve the previous values in `newmode' of bits that are + not affected by this change operation. */ + newmode = (newmode & ~changes->affected) | value; + break; + case '+': + newmode |= value; + break; + case '-': + newmode &= ~value; + break; + } + } + return newmode; +} + +/* Free the memory used by the list of file mode change operations + CHANGES. */ + +void +mode_free (changes) + register struct mode_change *changes; +{ + register struct mode_change *next; + + while (changes) + { + next = changes->next; + free (changes); + changes = next; + } +} + +/* Return a positive integer containing the value of the ASCII + octal number S. If S is not an octal number, return -1. */ + +static int +oatoi (s) + char *s; +{ + register int i; + + if (*s == 0) + return -1; + for (i = 0; isodigit (*s); ++s) + i = i * 8 + *s - '0'; + if (*s) + return -1; + return i; +} diff --git a/lib/modechange.h b/lib/modechange.h new file mode 100644 index 0000000..4a29883 --- /dev/null +++ b/lib/modechange.h @@ -0,0 +1,55 @@ +/* modechange.h -- definitions for file mode manipulation + Copyright (C) 1989, 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Masks for the `flags' field in a `struct mode_change'. */ + +/* Affect the execute bits only if at least one execute bit is set already, + or if the file is a directory. */ +#define MODE_X_IF_ANY_X 01 + +/* If set, copy some existing permissions for u, g, or o onto the other two. + Which of u, g, or o is copied is determined by which bits are set in the + `value' field. */ +#define MODE_COPY_EXISTING 02 + +struct mode_change +{ + char op; /* One of "=+-". */ + char flags; /* Special operations. */ + unsigned short affected; /* Set for u/g/o/s/s/t, if to be affected. */ + unsigned short value; /* Bits to add/remove. */ + struct mode_change *next; /* Link to next change in list. */ +}; + +/* Masks for mode_compile argument. */ +#define MODE_MASK_EQUALS 1 +#define MODE_MASK_PLUS 2 +#define MODE_MASK_MINUS 4 + +/* Error return values for mode_compile. */ +#define MODE_INVALID (struct mode_change *) 0 +#define MODE_MEMORY_EXHAUSTED (struct mode_change *) 1 + +#ifdef __STDC__ +struct mode_change *mode_compile (char *, unsigned); +unsigned short mode_adjust (unsigned, struct mode_change *); +void mode_free (struct mode_change *); +#else +struct mode_change *mode_compile (); +unsigned short mode_adjust (); +void mode_free (); +#endif diff --git a/lib/modetype.h b/lib/modetype.h new file mode 100644 index 0000000..fd4092b --- /dev/null +++ b/lib/modetype.h @@ -0,0 +1,81 @@ +/* modetype.h -- file type bits definitions for POSIX systems + Requires sys/types.h sys/stat.h. + Copyright (C) 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* POSIX.1 doesn't mention the S_IFMT bits; instead, it uses S_IStype + test macros. To make storing file types more convenient, define + them; the values don't need to correspond to what the kernel uses, + because of the way we use them. */ +#ifndef S_IFMT /* Doesn't have traditional Unix macros. */ +#define S_IFBLK 1 +#define S_IFCHR 2 +#define S_IFDIR 4 +#define S_IFREG 8 +#ifdef S_ISLNK +#define S_IFLNK 16 +#endif +#ifdef S_ISFIFO +#define S_IFIFO 32 +#endif +#ifdef S_ISSOCK +#define S_IFSOCK 64 +#endif +#endif /* !S_IFMT */ + +#ifdef STAT_MACROS_BROKEN +#undef S_ISBLK +#undef S_ISCHR +#undef S_ISDIR +#undef S_ISREG +#undef S_ISFIFO +#undef S_ISLNK +#undef S_ISSOCK +#undef S_ISMPB +#undef S_ISMPC +#undef S_ISNWK +#endif + +/* Do the reverse: define the POSIX.1 macros for traditional Unix systems + that don't have them. */ +#if !defined(S_ISBLK) && defined(S_IFBLK) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif diff --git a/lib/nextelem.c b/lib/nextelem.c new file mode 100644 index 0000000..4b9ddc1 --- /dev/null +++ b/lib/nextelem.c @@ -0,0 +1,106 @@ +/* Return the next element of a path. + Copyright (C) 1992 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie , + inspired by John P. Rouillard . */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#ifndef strchr +#define strchr index +#endif +#endif + +char *strdup (); +void free (); + +/* Return the next element of a colon-separated path. + A null entry in the path is equivalent to "." (the current directory). + + If NEW_PATH is non-NULL, set the path and return NULL. + If NEW_PATH is NULL, return the next item in the string, or + return NULL if there are no more elements. */ + +char * +next_element (new_path) + char *new_path; +{ + static char *path = NULL; /* Freshly allocated copy of NEW_PATH. */ + static char *end; /* Start of next element to return. */ + static int final_colon; /* If zero, path didn't end with a colon. */ + char *start; /* Start of path element to return. */ + + if (new_path) + { + if (path) + free (path); + end = path = strdup (new_path); + final_colon = 0; + return NULL; + } + + if (*end == '\0') + { + if (final_colon) + { + final_colon = 0; + return "."; + } + return NULL; + } + + start = end; + final_colon = 1; /* Maybe there will be one. */ + + end = strchr (start, ':'); + if (end == start) + { + /* An empty path element. */ + *end++ = '\0'; + return "."; + } + else if (end == NULL) + { + /* The last path element. */ + end = strchr (start, '\0'); + final_colon = 0; + } + else + *end++ = '\0'; + + return start; +} + +#ifdef TEST +int +main () +{ + char *p; + + next_element (getenv ("PATH")); + while (p = next_element (NULL)) + puts (p); + exit (0); +} +#endif /* TEST */ diff --git a/lib/pathmax.h b/lib/pathmax.h new file mode 100644 index 0000000..00fb0b5 --- /dev/null +++ b/lib/pathmax.h @@ -0,0 +1,53 @@ +/* Define PATH_MAX somehow. Requires sys/types.h. + Copyright (C) 1992 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _PATHMAX_H +#define _PATHMAX_H + +#ifdef HAVE_UNISTD_H +#include +#endif + +/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define + PATH_MAX but might cause redefinition warnings when sys/param.h is + later included (as on MORE/BSD 4.3). */ +#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__)) +#include +#endif + +#ifndef _POSIX_PATH_MAX +#define _POSIX_PATH_MAX 255 +#endif + +#if !defined(PATH_MAX) && defined(_PC_PATH_MAX) +#define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX)) +#endif + +/* Don't include sys/param.h if it already has been. */ +#if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN) +#include +#endif + +#if !defined(PATH_MAX) && defined(MAXPATHLEN) +#define PATH_MAX MAXPATHLEN +#endif + +#ifndef PATH_MAX +#define PATH_MAX _POSIX_PATH_MAX +#endif + +#endif /* _PATHMAX_H */ diff --git a/lib/regex.c b/lib/regex.c new file mode 100644 index 0000000..33c7ea2 --- /dev/null +++ b/lib/regex.c @@ -0,0 +1,5244 @@ +/* Extended regular expression matching and search library, + version 0.12. + (Implements POSIX draft P10003.2/D11.2, except for + internationalization features.) + + Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* AIX requires this to be the first thing in the file. */ +#if defined (_AIX) && !defined (REGEX_MALLOC) + #pragma alloca +#endif + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* We need this for `regex.h', and perhaps for the Emacs include files. */ +#include + +/* The `emacs' switch turns on certain matching commands + that make sense only in Emacs. */ +#ifdef emacs + +#include "lisp.h" +#include "buffer.h" +#include "syntax.h" + +/* Emacs uses `NULL' as a predicate. */ +#undef NULL + +#else /* not emacs */ + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +char *realloc (); +#endif + + +/* We used to test for `BSTRING' here, but only GCC and Emacs define + `BSTRING', as far as I know, and neither of them use this code. */ +#ifndef INHIBIT_STRING_HEADER +#if HAVE_STRING_H || STDC_HEADERS +#include +#ifndef bcmp +#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n)) +#endif +#ifndef bcopy +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif +#ifndef bzero +#define bzero(s, n) memset ((s), 0, (n)) +#endif +#else +#include +#endif +#endif + +/* Define the syntax stuff for \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +#ifndef Sword +#define Sword 1 +#endif + +#ifdef SYNTAX_TABLE + +extern char *re_syntax_table; + +#else /* not SYNTAX_TABLE */ + +/* How many characters in the character set. */ +#define CHAR_SET_SIZE 256 + +static char re_syntax_table[CHAR_SET_SIZE]; + +static void +init_syntax_once () +{ + register int c; + static int done = 0; + + if (done) + return; + + bzero (re_syntax_table, sizeof re_syntax_table); + + for (c = 'a'; c <= 'z'; c++) + re_syntax_table[c] = Sword; + + for (c = 'A'; c <= 'Z'; c++) + re_syntax_table[c] = Sword; + + for (c = '0'; c <= '9'; c++) + re_syntax_table[c] = Sword; + + re_syntax_table['_'] = Sword; + + done = 1; +} + +#endif /* not SYNTAX_TABLE */ + +#define SYNTAX(c) re_syntax_table[c] + +#endif /* not emacs */ + +/* Get the interface, including the syntax bits. */ +#include "regex.h" + +/* isalpha etc. are used for the character classes. */ +#include + +/* Jim Meyering writes: + + "... Some ctype macros are valid only for character codes that + isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when + using /bin/cc or gcc but without giving an ansi option). So, all + ctype uses should be through macros like ISPRINT... If + STDC_HEADERS is defined, then autoconf has verified that the ctype + macros don't need to be guarded with references to isascii. ... + Defining isascii to 1 should let any compiler worth its salt + eliminate the && through constant folding." */ + +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +#define ISASCII(c) 1 +#else +#define ISASCII(c) isascii(c) +#endif + +#ifdef isblank +#define ISBLANK(c) (ISASCII (c) && isblank (c)) +#else +#define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +#define ISGRAPH(c) (ISASCII (c) && isgraph (c)) +#else +#define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) +#endif + +#define ISPRINT(c) (ISASCII (c) && isprint (c)) +#define ISDIGIT(c) (ISASCII (c) && isdigit (c)) +#define ISALNUM(c) (ISASCII (c) && isalnum (c)) +#define ISALPHA(c) (ISASCII (c) && isalpha (c)) +#define ISCNTRL(c) (ISASCII (c) && iscntrl (c)) +#define ISLOWER(c) (ISASCII (c) && islower (c)) +#define ISPUNCT(c) (ISASCII (c) && ispunct (c)) +#define ISSPACE(c) (ISASCII (c) && isspace (c)) +#define ISUPPER(c) (ISASCII (c) && isupper (c)) +#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c)) + +#ifndef NULL +#define NULL 0 +#endif + +/* We remove any previous definition of `SIGN_EXTEND_CHAR', + since ours (we hope) works properly with all combinations of + machines, compilers, `char' and `unsigned char' argument types. + (Per Bothner suggested the basic approach.) */ +#undef SIGN_EXTEND_CHAR +#if __STDC__ +#define SIGN_EXTEND_CHAR(c) ((signed char) (c)) +#else /* not __STDC__ */ +/* As in Harbison and Steele. */ +#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128) +#endif + +/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we + use `alloca' instead of `malloc'. This is because using malloc in + re_search* or re_match* could cause memory leaks when C-g is used in + Emacs; also, malloc is slower and causes storage fragmentation. On + the other hand, malloc is more portable, and easier to debug. + + Because we sometimes use alloca, some routines have to be macros, + not functions -- `alloca'-allocated space disappears at the end of the + function it is called in. */ + +#ifdef REGEX_MALLOC + +#define REGEX_ALLOCATE malloc +#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize) + +#else /* not REGEX_MALLOC */ + +/* Emacs already defines alloca, sometimes. */ +#ifndef alloca + +/* Make alloca work the best possible way. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#if HAVE_ALLOCA_H +#include +#else /* not __GNUC__ or HAVE_ALLOCA_H */ +#ifndef _AIX /* Already did AIX, up at the top. */ +char *alloca (); +#endif /* not _AIX */ +#endif /* not HAVE_ALLOCA_H */ +#endif /* not __GNUC__ */ + +#endif /* not alloca */ + +#define REGEX_ALLOCATE alloca + +/* Assumes a `char *destination' variable. */ +#define REGEX_REALLOCATE(source, osize, nsize) \ + (destination = (char *) alloca (nsize), \ + bcopy (source, destination, osize), \ + destination) + +#endif /* not REGEX_MALLOC */ + + +/* True if `size1' is non-NULL and PTR is pointing anywhere inside + `string1' or just past its end. This works if PTR is NULL, which is + a good thing. */ +#define FIRST_STRING_P(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* (Re)Allocate N items of type T using malloc, or fail. */ +#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) +#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) +#define RETALLOC_IF(addr, n, t) \ + if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t) +#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) + +#define BYTEWIDTH 8 /* In bits. */ + +#define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) + +#undef MAX +#undef MIN +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +typedef char boolean; +#define false 0 +#define true 1 + +static int re_match_2_internal (); + +/* These are the command codes that appear in compiled regular + expressions. Some opcodes are followed by argument bytes. A + command code can specify any interpretation whatsoever for its + arguments. Zero bytes may appear in the compiled regular expression. */ + +typedef enum +{ + no_op = 0, + + /* Followed by one byte giving n, then by n literal bytes. */ + exactn, + + /* Matches any (more or less) character. */ + anychar, + + /* Matches any one char belonging to specified set. First + following byte is number of bitmap bytes. Then come bytes + for a bitmap saying which chars are in. Bits in each byte + are ordered low-bit-first. A character is in the set if its + bit is 1. A character too large to have a bit in the map is + automatically not in the set. */ + charset, + + /* Same parameters as charset, but match any character that is + not one of those specified. */ + charset_not, + + /* Start remembering the text that is matched, for storing in a + register. Followed by one byte with the register number, in + the range 0 to one less than the pattern buffer's re_nsub + field. Then followed by one byte with the number of groups + inner to this one. (This last has to be part of the + start_memory only because we need it in the on_failure_jump + of re_match_2.) */ + start_memory, + + /* Stop remembering the text that is matched and store it in a + memory register. Followed by one byte with the register + number, in the range 0 to one less than `re_nsub' in the + pattern buffer, and one byte with the number of inner groups, + just like `start_memory'. (We need the number of inner + groups here because we don't have any easy way of finding the + corresponding start_memory when we're at a stop_memory.) */ + stop_memory, + + /* Match a duplicate of something remembered. Followed by one + byte containing the register number. */ + duplicate, + + /* Fail unless at beginning of line. */ + begline, + + /* Fail unless at end of line. */ + endline, + + /* Succeeds if at beginning of buffer (if emacs) or at beginning + of string to be matched (if not). */ + begbuf, + + /* Analogously, for end of buffer/string. */ + endbuf, + + /* Followed by two byte relative address to which to jump. */ + jump, + + /* Same as jump, but marks the end of an alternative. */ + jump_past_alt, + + /* Followed by two-byte relative address of place to resume at + in case of failure. */ + on_failure_jump, + + /* Like on_failure_jump, but pushes a placeholder instead of the + current string position when executed. */ + on_failure_keep_string_jump, + + /* Throw away latest failure point and then jump to following + two-byte relative address. */ + pop_failure_jump, + + /* Change to pop_failure_jump if know won't have to backtrack to + match; otherwise change to jump. This is used to jump + back to the beginning of a repeat. If what follows this jump + clearly won't match what the repeat does, such that we can be + sure that there is no use backtracking out of repetitions + already matched, then we change it to a pop_failure_jump. + Followed by two-byte address. */ + maybe_pop_jump, + + /* Jump to following two-byte address, and push a dummy failure + point. This failure point will be thrown away if an attempt + is made to use it for a failure. A `+' construct makes this + before the first repeat. Also used as an intermediary kind + of jump when compiling an alternative. */ + dummy_failure_jump, + + /* Push a dummy failure point and continue. Used at the end of + alternatives. */ + push_dummy_failure, + + /* Followed by two-byte relative address and two-byte number n. + After matching N times, jump to the address upon failure. */ + succeed_n, + + /* Followed by two-byte relative address, and two-byte number n. + Jump to the address N times, then fail. */ + jump_n, + + /* Set the following two-byte relative address to the + subsequent two-byte number. The address *includes* the two + bytes of number. */ + set_number_at, + + wordchar, /* Matches any word-constituent character. */ + notwordchar, /* Matches any char that is not a word-constituent. */ + + wordbeg, /* Succeeds if at word beginning. */ + wordend, /* Succeeds if at word end. */ + + wordbound, /* Succeeds if at a word boundary. */ + notwordbound /* Succeeds if not at a word boundary. */ + +#ifdef emacs + ,before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ + + /* Matches any character whose syntax is specified. Followed by + a byte which contains a syntax code, e.g., Sword. */ + syntaxspec, + + /* Matches any character whose syntax is not that specified. */ + notsyntaxspec +#endif /* emacs */ +} re_opcode_t; + +/* Common operations on the compiled pattern. */ + +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ + +#define STORE_NUMBER(destination, number) \ + do { \ + (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; \ + } while (0) + +/* Same as STORE_NUMBER, except increment DESTINATION to + the byte after where the number is stored. Therefore, DESTINATION + must be an lvalue. */ + +#define STORE_NUMBER_AND_INCR(destination, number) \ + do { \ + STORE_NUMBER (destination, number); \ + (destination) += 2; \ + } while (0) + +/* Put into DESTINATION a number stored in two contiguous bytes starting + at SOURCE. */ + +#define EXTRACT_NUMBER(destination, source) \ + do { \ + (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \ + } while (0) + +#ifdef DEBUG +static void +extract_number (dest, source) + int *dest; + unsigned char *source; +{ + int temp = SIGN_EXTEND_CHAR (*(source + 1)); + *dest = *source & 0377; + *dest += temp << 8; +} + +#ifndef EXTRACT_MACROS /* To debug the macros. */ +#undef EXTRACT_NUMBER +#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src) +#endif /* not EXTRACT_MACROS */ + +#endif /* DEBUG */ + +/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number. + SOURCE must be an lvalue. */ + +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + do { \ + EXTRACT_NUMBER (destination, source); \ + (source) += 2; \ + } while (0) + +#ifdef DEBUG +static void +extract_number_and_incr (destination, source) + int *destination; + unsigned char **source; +{ + extract_number (destination, *source); + *source += 2; +} + +#ifndef EXTRACT_MACROS +#undef EXTRACT_NUMBER_AND_INCR +#define EXTRACT_NUMBER_AND_INCR(dest, src) \ + extract_number_and_incr (&dest, &src) +#endif /* not EXTRACT_MACROS */ + +#endif /* DEBUG */ + +/* If DEBUG is defined, Regex prints many voluminous messages about what + it is doing (if the variable `debug' is nonzero). If linked with the + main program in `iregex.c', you can enter patterns and strings + interactively. And if linked with the main program in `main.c' and + the other test files, you can run the already-written tests. */ + +#ifdef DEBUG + +/* We use standard I/O for debugging. */ +#include + +/* It is useful to test things that ``must'' be true when debugging. */ +#include + +static int debug = 0; + +#define DEBUG_STATEMENT(e) e +#define DEBUG_PRINT1(x) if (debug) printf (x) +#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2) +#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3) +#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4) +#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ + if (debug) print_partial_compiled_pattern (s, e) +#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \ + if (debug) print_double_string (w, s1, sz1, s2, sz2) + + +extern void printchar (); + +/* Print the fastmap in human-readable form. */ + +void +print_fastmap (fastmap) + char *fastmap; +{ + unsigned was_a_range = 0; + unsigned i = 0; + + while (i < (1 << BYTEWIDTH)) + { + if (fastmap[i++]) + { + was_a_range = 0; + printchar (i - 1); + while (i < (1 << BYTEWIDTH) && fastmap[i]) + { + was_a_range = 1; + i++; + } + if (was_a_range) + { + printf ("-"); + printchar (i - 1); + } + } + } + putchar ('\n'); +} + + +/* Print a compiled pattern string in human-readable form, starting at + the START pointer into it and ending just before the pointer END. */ + +void +print_partial_compiled_pattern (start, end) + unsigned char *start; + unsigned char *end; +{ + int mcnt, mcnt2; + unsigned char *p = start; + unsigned char *pend = end; + + if (start == NULL) + { + printf ("(null)\n"); + return; + } + + /* Loop over pattern commands. */ + while (p < pend) + { + printf ("%d:\t", p - start); + + switch ((re_opcode_t) *p++) + { + case no_op: + printf ("/no_op"); + break; + + case exactn: + mcnt = *p++; + printf ("/exactn/%d", mcnt); + do + { + putchar ('/'); + printchar (*p++); + } + while (--mcnt); + break; + + case start_memory: + mcnt = *p++; + printf ("/start_memory/%d/%d", mcnt, *p++); + break; + + case stop_memory: + mcnt = *p++; + printf ("/stop_memory/%d/%d", mcnt, *p++); + break; + + case duplicate: + printf ("/duplicate/%d", *p++); + break; + + case anychar: + printf ("/anychar"); + break; + + case charset: + case charset_not: + { + register int c, last = -100; + register int in_range = 0; + + printf ("/charset [%s", + (re_opcode_t) *(p - 1) == charset_not ? "^" : ""); + + assert (p + *p < pend); + + for (c = 0; c < 256; c++) + if (c / 8 < *p + && (p[1 + (c/8)] & (1 << (c % 8)))) + { + /* Are we starting a range? */ + if (last + 1 == c && ! in_range) + { + putchar ('-'); + in_range = 1; + } + /* Have we broken a range? */ + else if (last + 1 != c && in_range) + { + printchar (last); + in_range = 0; + } + + if (! in_range) + printchar (c); + + last = c; + } + + if (in_range) + printchar (last); + + putchar (']'); + + p += 1 + *p; + } + break; + + case begline: + printf ("/begline"); + break; + + case endline: + printf ("/endline"); + break; + + case on_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_jump to %d", p + mcnt - start); + break; + + case on_failure_keep_string_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_keep_string_jump to %d", p + mcnt - start); + break; + + case dummy_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/dummy_failure_jump to %d", p + mcnt - start); + break; + + case push_dummy_failure: + printf ("/push_dummy_failure"); + break; + + case maybe_pop_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/maybe_pop_jump to %d", p + mcnt - start); + break; + + case pop_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/pop_failure_jump to %d", p + mcnt - start); + break; + + case jump_past_alt: + extract_number_and_incr (&mcnt, &p); + printf ("/jump_past_alt to %d", p + mcnt - start); + break; + + case jump: + extract_number_and_incr (&mcnt, &p); + printf ("/jump to %d", p + mcnt - start); + break; + + case succeed_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2); + break; + + case jump_n: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2); + break; + + case set_number_at: + extract_number_and_incr (&mcnt, &p); + extract_number_and_incr (&mcnt2, &p); + printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2); + break; + + case wordbound: + printf ("/wordbound"); + break; + + case notwordbound: + printf ("/notwordbound"); + break; + + case wordbeg: + printf ("/wordbeg"); + break; + + case wordend: + printf ("/wordend"); + +#ifdef emacs + case before_dot: + printf ("/before_dot"); + break; + + case at_dot: + printf ("/at_dot"); + break; + + case after_dot: + printf ("/after_dot"); + break; + + case syntaxspec: + printf ("/syntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; + + case notsyntaxspec: + printf ("/notsyntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; +#endif /* emacs */ + + case wordchar: + printf ("/wordchar"); + break; + + case notwordchar: + printf ("/notwordchar"); + break; + + case begbuf: + printf ("/begbuf"); + break; + + case endbuf: + printf ("/endbuf"); + break; + + default: + printf ("?%d", *(p-1)); + } + + putchar ('\n'); + } + + printf ("%d:\tend of pattern.\n", p - start); +} + + +void +print_compiled_pattern (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *buffer = bufp->buffer; + + print_partial_compiled_pattern (buffer, buffer + bufp->used); + printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated); + + if (bufp->fastmap_accurate && bufp->fastmap) + { + printf ("fastmap: "); + print_fastmap (bufp->fastmap); + } + + printf ("re_nsub: %d\t", bufp->re_nsub); + printf ("regs_alloc: %d\t", bufp->regs_allocated); + printf ("can_be_null: %d\t", bufp->can_be_null); + printf ("newline_anchor: %d\n", bufp->newline_anchor); + printf ("no_sub: %d\t", bufp->no_sub); + printf ("not_bol: %d\t", bufp->not_bol); + printf ("not_eol: %d\t", bufp->not_eol); + printf ("syntax: %d\n", bufp->syntax); + /* Perhaps we should print the translate table? */ +} + + +void +print_double_string (where, string1, size1, string2, size2) + const char *where; + const char *string1; + const char *string2; + int size1; + int size2; +{ + unsigned this_char; + + if (where == NULL) + printf ("(null)"); + else + { + if (FIRST_STRING_P (where)) + { + for (this_char = where - string1; this_char < size1; this_char++) + printchar (string1[this_char]); + + where = string2; + } + + for (this_char = where - string2; this_char < size2; this_char++) + printchar (string2[this_char]); + } +} + +#else /* not DEBUG */ + +#undef assert +#define assert(e) + +#define DEBUG_STATEMENT(e) +#define DEBUG_PRINT1(x) +#define DEBUG_PRINT2(x1, x2) +#define DEBUG_PRINT3(x1, x2, x3) +#define DEBUG_PRINT4(x1, x2, x3, x4) +#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) +#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) + +#endif /* not DEBUG */ + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; +} + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. */ + +static const char *re_error_msg[] = + { NULL, /* REG_NOERROR */ + "No match", /* REG_NOMATCH */ + "Invalid regular expression", /* REG_BADPAT */ + "Invalid collation character", /* REG_ECOLLATE */ + "Invalid character class name", /* REG_ECTYPE */ + "Trailing backslash", /* REG_EESCAPE */ + "Invalid back reference", /* REG_ESUBREG */ + "Unmatched [ or [^", /* REG_EBRACK */ + "Unmatched ( or \\(", /* REG_EPAREN */ + "Unmatched \\{", /* REG_EBRACE */ + "Invalid content of \\{\\}", /* REG_BADBR */ + "Invalid range end", /* REG_ERANGE */ + "Memory exhausted", /* REG_ESPACE */ + "Invalid preceding regular expression", /* REG_BADRPT */ + "Premature end of regular expression", /* REG_EEND */ + "Regular expression too big", /* REG_ESIZE */ + "Unmatched ) or \\)", /* REG_ERPAREN */ + }; + +/* Avoiding alloca during matching, to placate r_alloc. */ + +/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the + searching and matching functions should not call alloca. On some + systems, alloca is implemented in terms of malloc, and if we're + using the relocating allocator routines, then malloc could cause a + relocation, which might (if the strings being searched are in the + ralloc heap) shift the data out from underneath the regexp + routines. + + Here's another reason to avoid allocation: Emacs + processes input from X in a signal handler; processing X input may + call malloc; if input arrives while a matching routine is calling + malloc, then we're scrod. But Emacs can't just block input while + calling matching routines; then we don't notice interrupts when + they come in. So, Emacs blocks input around all regexp calls + except the matching calls, which it leaves unprotected, in the + faith that they will not malloc. */ + +/* Normally, this is fine. */ +#define MATCH_MAY_ALLOCATE + +/* The match routines may not allocate if (1) they would do it with malloc + and (2) it's not safe for them to use malloc. */ +#if (defined (C_ALLOCA) || defined (REGEX_MALLOC)) && (defined (emacs) || defined (REL_ALLOC)) +#undef MATCH_MAY_ALLOCATE +#endif + + +/* Failure stack declarations and macros; both re_compile_fastmap and + re_match_2 use a failure stack. These have to be macros because of + REGEX_ALLOCATE. */ + + +/* Number of failure points for which to initially allocate space + when matching. If this number is exceeded, we allocate more + space, so it is not a hard limit. */ +#ifndef INIT_FAILURE_ALLOC +#define INIT_FAILURE_ALLOC 5 +#endif + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always used MAX_FAILURE_SPACE each time we failed. + This is a variable only so users of regex can assign to it; we never + change it ourselves. */ +int re_max_failures = 2000; + +typedef unsigned char *fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} fail_stack_type; + +#define FAIL_STACK_EMPTY() (fail_stack.avail == 0) +#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) +#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) +#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail]) + + +/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */ + +#ifdef MATCH_MAY_ALLOCATE +#define INIT_FAIL_STACK() \ + do { \ + fail_stack.stack = (fail_stack_elt_t *) \ + REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \ + \ + if (fail_stack.stack == NULL) \ + return -2; \ + \ + fail_stack.size = INIT_FAILURE_ALLOC; \ + fail_stack.avail = 0; \ + } while (0) +#else +#define INIT_FAIL_STACK() \ + do { \ + fail_stack.avail = 0; \ + } while (0) +#endif + + +/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items. + + Return 1 if succeeds, and 0 if either ran out of memory + allocating space for it or it was already too large. + + REGEX_REALLOCATE requires `destination' be declared. */ + +#define DOUBLE_FAIL_STACK(fail_stack) \ + ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \ + ? 0 \ + : ((fail_stack).stack = (fail_stack_elt_t *) \ + REGEX_REALLOCATE ((fail_stack).stack, \ + (fail_stack).size * sizeof (fail_stack_elt_t), \ + ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \ + \ + (fail_stack).stack == NULL \ + ? 0 \ + : ((fail_stack).size <<= 1, \ + 1))) + + +/* Push PATTERN_OP on FAIL_STACK. + + Return 1 if was able to do so and 0 if ran out of memory allocating + space to do so. */ +#define PUSH_PATTERN_OP(pattern_op, fail_stack) \ + ((FAIL_STACK_FULL () \ + && !DOUBLE_FAIL_STACK (fail_stack)) \ + ? 0 \ + : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \ + 1)) + +/* This pushes an item onto the failure stack. Must be a four-byte + value. Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_ITEM(item) \ + fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item + +/* The complement operation. Assumes `fail_stack' is nonempty. */ +#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail] + +/* Used to omit pushing failure point id's when we're not debugging. */ +#ifdef DEBUG +#define DEBUG_PUSH PUSH_FAILURE_ITEM +#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM () +#else +#define DEBUG_PUSH(item) +#define DEBUG_POP(item_addr) +#endif + + +/* Push the information about the state we will need + if we ever fail back to it. + + Requires variables fail_stack, regstart, regend, reg_info, and + num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be + declared. + + Does `return FAILURE_CODE' if runs out of memory. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ + do { \ + char *destination; \ + /* Must be int, so when we don't save any registers, the arithmetic \ + of 0 + -1 isn't done as unsigned. */ \ + int this_reg; \ + \ + DEBUG_STATEMENT (failure_id++); \ + DEBUG_STATEMENT (nfailure_points_pushed++); \ + DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ + DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ + DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ + \ + DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \ + DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ + \ + /* Ensure we have enough space allocated for what we will push. */ \ + while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ + { \ + if (!DOUBLE_FAIL_STACK (fail_stack)) \ + return failure_code; \ + \ + DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ + (fail_stack).size); \ + DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ + } \ + \ + /* Push the info, starting with the registers. */ \ + DEBUG_PRINT1 ("\n"); \ + \ + for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ + this_reg++) \ + { \ + DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \ + DEBUG_STATEMENT (num_regs_pushed++); \ + \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + PUSH_FAILURE_ITEM (regstart[this_reg]); \ + \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + PUSH_FAILURE_ITEM (regend[this_reg]); \ + \ + DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \ + DEBUG_PRINT2 (" match_null=%d", \ + REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ + DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ + DEBUG_PRINT2 (" matched_something=%d", \ + MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT2 (" ever_matched=%d", \ + EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT1 ("\n"); \ + PUSH_FAILURE_ITEM (reg_info[this_reg].word); \ + } \ + \ + DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\ + PUSH_FAILURE_ITEM (lowest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\ + PUSH_FAILURE_ITEM (highest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ + PUSH_FAILURE_ITEM (pattern_place); \ + \ + DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \ + DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ + size2); \ + DEBUG_PRINT1 ("'\n"); \ + PUSH_FAILURE_ITEM (string_place); \ + \ + DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ + DEBUG_PUSH (failure_id); \ + } while (0) + +/* This is the number of items that are pushed and popped on the stack + for each register. */ +#define NUM_REG_ITEMS 3 + +/* Individual items aside from the registers. */ +#ifdef DEBUG +#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ +#else +#define NUM_NONREG_ITEMS 4 +#endif + +/* We push at most this many items on the stack. */ +#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS) + +/* We actually push this many items. */ +#define NUM_FAILURE_ITEMS \ + ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \ + + NUM_NONREG_ITEMS) + +/* How many items can still be added to the stack without overflowing it. */ +#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) + + +/* Pops what PUSH_FAIL_STACK pushes. + + We restore into the parameters, all of which should be lvalues: + STR -- the saved data position. + PAT -- the saved pattern position. + LOW_REG, HIGH_REG -- the highest and lowest active registers. + REGSTART, REGEND -- arrays of string positions. + REG_INFO -- array of information about each subexpression. + + Also assumes the variables `fail_stack' and (if debugging), `bufp', + `pend', `string1', `size1', `string2', and `size2'. */ + +#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ +{ \ + DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \ + int this_reg; \ + const unsigned char *string_temp; \ + \ + assert (!FAIL_STACK_EMPTY ()); \ + \ + /* Remove failure points and point to how many regs pushed. */ \ + DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ + DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ + DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ + \ + assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ + \ + DEBUG_POP (&failure_id); \ + DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ + \ + /* If the saved string location is NULL, it came from an \ + on_failure_keep_string_jump opcode, and we want to throw away the \ + saved NULL, thus retaining our current position in the string. */ \ + string_temp = POP_FAILURE_ITEM (); \ + if (string_temp != NULL) \ + str = (const char *) string_temp; \ + \ + DEBUG_PRINT2 (" Popping string 0x%x: `", str); \ + DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ + DEBUG_PRINT1 ("'\n"); \ + \ + pat = (unsigned char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ + \ + /* Restore register info. */ \ + high_reg = (unsigned) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \ + \ + low_reg = (unsigned) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \ + \ + for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ + { \ + DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \ + \ + reg_info[this_reg].word = POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \ + \ + regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \ + \ + regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \ + DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \ + } \ + \ + DEBUG_STATEMENT (nfailure_points_popped++); \ +} /* POP_FAILURE_POINT */ + + + +/* Structure for per-register (a.k.a. per-group) information. + This must not be longer than one word, because we push this value + onto the failure stack. Other register information, such as the + starting and ending positions (which are addresses), and the list of + inner groups (which is a bits list) are maintained in separate + variables. + + We are making a (strictly speaking) nonportable assumption here: that + the compiler will pack our bit fields into something that fits into + the type of `word', i.e., is something that fits into one item on the + failure stack. */ +typedef union +{ + fail_stack_elt_t word; + struct + { + /* This field is one if this group can match the empty string, + zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ +#define MATCH_NULL_UNSET_VALUE 3 + unsigned match_null_string_p : 2; + unsigned is_active : 1; + unsigned matched_something : 1; + unsigned ever_matched_something : 1; + } bits; +} register_info_type; + +#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) +#define IS_ACTIVE(R) ((R).bits.is_active) +#define MATCHED_SOMETHING(R) ((R).bits.matched_something) +#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) + + +/* Call this when have matched a real character; it sets `matched' flags + for the subexpressions which we are currently inside. Also records + that those subexprs have matched. */ +#define SET_REGS_MATCHED() \ + do \ + { \ + unsigned r; \ + for (r = lowest_active_reg; r <= highest_active_reg; r++) \ + { \ + MATCHED_SOMETHING (reg_info[r]) \ + = EVER_MATCHED_SOMETHING (reg_info[r]) \ + = 1; \ + } \ + } \ + while (0) + + +/* Registers are set to a sentinel when they haven't yet matched. */ +#define REG_UNSET_VALUE ((char *) -1) +#define REG_UNSET(e) ((e) == REG_UNSET_VALUE) + + + +/* How do we implement a missing MATCH_MAY_ALLOCATE? + We make the fail stack a global thing, and then grow it to + re_max_failures when we compile. */ +#ifndef MATCH_MAY_ALLOCATE +static fail_stack_type fail_stack; + +static const char ** regstart, ** regend; +static const char ** old_regstart, ** old_regend; +static const char **best_regstart, **best_regend; +static register_info_type *reg_info; +static const char **reg_dummy; +static register_info_type *reg_info_dummy; +#endif + + +/* Subroutine declarations and macros for regex_compile. */ + +static void store_op1 (), store_op2 (); +static void insert_op1 (), insert_op2 (); +static boolean at_begline_loc_p (), at_endline_loc_p (); +static boolean group_in_compile_stack (); +static reg_errcode_t compile_range (); + +/* Fetch the next character in the uncompiled pattern---translating it + if necessary. Also cast from a signed character in the constant + string passed to us by the user to an unsigned char that we can use + as an array index (in, e.g., `translate'). */ +#define PATFETCH(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + if (translate) c = translate[c]; \ + } while (0) + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + } while (0) + +/* Go backwards one character in the pattern. */ +#define PATUNFETCH p-- + + +/* If `translate' is non-null, return translate[D], else just D. We + cast the subscript to translate because some data is declared as + `char *', to avoid warnings when a string constant is passed. But + when we use a character as a subscript we must make it unsigned. */ +#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d)) + + +/* Macros for outputting the compiled pattern into `buffer'. */ + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 32 + +/* Make sure we have at least N more bytes of space in buffer. */ +#define GET_BUFFER_SPACE(n) \ + while (b - bufp->buffer + (n) > bufp->allocated) \ + EXTEND_BUFFER () + +/* Make sure we have one more byte of buffer space and then add C to it. */ +#define BUF_PUSH(c) \ + do { \ + GET_BUFFER_SPACE (1); \ + *b++ = (unsigned char) (c); \ + } while (0) + + +/* Ensure we have two more bytes of buffer space and then append C1 and C2. */ +#define BUF_PUSH_2(c1, c2) \ + do { \ + GET_BUFFER_SPACE (2); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + } while (0) + + +/* As with BUF_PUSH_2, except for three bytes. */ +#define BUF_PUSH_3(c1, c2, c3) \ + do { \ + GET_BUFFER_SPACE (3); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + *b++ = (unsigned char) (c3); \ + } while (0) + + +/* Store a jump with opcode OP at LOC to location TO. We store a + relative address offset by the three bytes the jump itself occupies. */ +#define STORE_JUMP(op, loc, to) \ + store_op1 (op, loc, (to) - (loc) - 3) + +/* Likewise, for a two-argument jump. */ +#define STORE_JUMP2(op, loc, to, arg) \ + store_op2 (op, loc, (to) - (loc) - 3, arg) + +/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP(op, loc, to) \ + insert_op1 (op, loc, (to) - (loc) - 3, b) + +/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP2(op, loc, to, arg) \ + insert_op2 (op, loc, (to) - (loc) - 3, arg, b) + + +/* This is not an arbitrary limit: the arguments which represent offsets + into the pattern are two bytes long. So if 2^16 bytes turns out to + be too small, many things would have to change. */ +#define MAX_BUF_SIZE (1L << 16) + + +/* Extend the buffer by twice its current size via realloc and + reset the pointers that pointed into the old block to point to the + correct places in the new one. If extending the buffer results in it + being larger than MAX_BUF_SIZE, then flag memory exhausted. */ +#define EXTEND_BUFFER() \ + do { \ + unsigned char *old_buffer = bufp->buffer; \ + if (bufp->allocated == MAX_BUF_SIZE) \ + return REG_ESIZE; \ + bufp->allocated <<= 1; \ + if (bufp->allocated > MAX_BUF_SIZE) \ + bufp->allocated = MAX_BUF_SIZE; \ + bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\ + if (bufp->buffer == NULL) \ + return REG_ESPACE; \ + /* If the buffer moved, move all the pointers into it. */ \ + if (old_buffer != bufp->buffer) \ + { \ + b = (b - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (fixup_alt_jump) \ + fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } \ + } while (0) + + +/* Since we have one byte reserved for the register number argument to + {start,stop}_memory, the maximum number of groups we can report + things about is what fits in that byte. */ +#define MAX_REGNUM 255 + +/* But patterns can have more than `MAX_REGNUM' registers. We just + ignore the excess. */ +typedef unsigned regnum_t; + + +/* Macros for the compile stack. */ + +/* Since offsets can go either forwards or backwards, this type needs to + be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ +typedef int pattern_offset_t; + +typedef struct +{ + pattern_offset_t begalt_offset; + pattern_offset_t fixup_alt_jump; + pattern_offset_t inner_group_offset; + pattern_offset_t laststart_offset; + regnum_t regnum; +} compile_stack_elt_t; + + +typedef struct +{ + compile_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} compile_stack_type; + + +#define INIT_COMPILE_STACK_SIZE 32 + +#define COMPILE_STACK_EMPTY (compile_stack.avail == 0) +#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) + +/* The next available element. */ +#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) + + +/* Set the bit for character C in a list. */ +#define SET_LIST_BIT(c) \ + (b[((unsigned char) (c)) / BYTEWIDTH] \ + |= 1 << (((unsigned char) c) % BYTEWIDTH)) + + +/* Get the next unsigned number in the uncompiled pattern. */ +#define GET_UNSIGNED_NUMBER(num) \ + { if (p != pend) \ + { \ + PATFETCH (c); \ + while (ISDIGIT (c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH (c); \ + } \ + } \ + } + +#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ + +#define IS_CHAR_CLASS(string) \ + (STREQ (string, "alpha") || STREQ (string, "upper") \ + || STREQ (string, "lower") || STREQ (string, "digit") \ + || STREQ (string, "alnum") || STREQ (string, "xdigit") \ + || STREQ (string, "space") || STREQ (string, "print") \ + || STREQ (string, "punct") || STREQ (string, "graph") \ + || STREQ (string, "cntrl") || STREQ (string, "blank")) + +/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. + Returns one of error codes defined in `regex.h', or zero for success. + + Assumes the `allocated' (and perhaps `buffer') and `translate' + fields are set in BUFP on entry. + + If it succeeds, results are put in BUFP (if it returns an error, the + contents of BUFP are undefined): + `buffer' is the compiled pattern; + `syntax' is set to SYNTAX; + `used' is set to the length of the compiled pattern; + `fastmap_accurate' is zero; + `re_nsub' is the number of subexpressions in PATTERN; + `not_bol' and `not_eol' are zero; + + The `fastmap' and `newline_anchor' fields are neither + examined nor set. */ + +/* Return, freeing storage we allocated. */ +#define FREE_STACK_RETURN(value) \ + return (free (compile_stack.stack), value) + +static reg_errcode_t +regex_compile (pattern, size, syntax, bufp) + const char *pattern; + int size; + reg_syntax_t syntax; + struct re_pattern_buffer *bufp; +{ + /* We fetch characters from PATTERN here. Even though PATTERN is + `char *' (i.e., signed), we declare these variables as unsigned, so + they can be reliably used as array indices. */ + register unsigned char c, c1; + + /* A random temporary spot in PATTERN. */ + const char *p1; + + /* Points to the end of the buffer, where we should append. */ + register unsigned char *b; + + /* Keeps track of unclosed groups. */ + compile_stack_type compile_stack; + + /* Points to the current (ending) position in the pattern. */ + const char *p = pattern; + const char *pend = pattern + size; + + /* How to translate the characters in the pattern. */ + char *translate = bufp->translate; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell if a new exact-match + character can be added to that command or if the character requires + a new `exactn' command. */ + unsigned char *pending_exact = 0; + + /* Address of start of the most recently finished expression. + This tells, e.g., postfix * where to find the start of its + operand. Reset at the beginning of groups and alternatives. */ + unsigned char *laststart = 0; + + /* Address of beginning of regexp, or inside of last group. */ + unsigned char *begalt; + + /* Place in the uncompiled pattern (i.e., the {) to + which to go back if the interval is invalid. */ + const char *beg_interval; + + /* Address of the place where a forward jump should go to the end of + the containing expression. Each alternative of an `or' -- except the + last -- ends with a forward jump of this sort. */ + unsigned char *fixup_alt_jump = 0; + + /* Counts open-groups as they are encountered. Remembered for the + matching close-group on the compile stack, so the same register + number is put in the stop_memory as the start_memory. */ + regnum_t regnum = 0; + +#ifdef DEBUG + DEBUG_PRINT1 ("\nCompiling pattern: "); + if (debug) + { + unsigned debug_count; + + for (debug_count = 0; debug_count < size; debug_count++) + printchar (pattern[debug_count]); + putchar ('\n'); + } +#endif /* DEBUG */ + + /* Initialize the compile stack. */ + compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t); + if (compile_stack.stack == NULL) + return REG_ESPACE; + + compile_stack.size = INIT_COMPILE_STACK_SIZE; + compile_stack.avail = 0; + + /* Initialize the pattern buffer. */ + bufp->syntax = syntax; + bufp->fastmap_accurate = 0; + bufp->not_bol = bufp->not_eol = 0; + + /* Set `used' to zero, so that if we return an error, the pattern + printer (for debugging) will think there's no pattern. We reset it + at the end. */ + bufp->used = 0; + + /* Always count groups, whether or not bufp->no_sub is set. */ + bufp->re_nsub = 0; + +#if !defined (emacs) && !defined (SYNTAX_TABLE) + /* Initialize the syntax table. */ + init_syntax_once (); +#endif + + if (bufp->allocated == 0) + { + if (bufp->buffer) + { /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. */ + RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); + } + else + { /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); + } + if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE); + + bufp->allocated = INIT_BUF_SIZE; + } + + begalt = b = bufp->buffer; + + /* Loop through the uncompiled pattern until we're at the end. */ + while (p != pend) + { + PATFETCH (c); + + switch (c) + { + case '^': + { + if ( /* If at start of pattern, it's an operator. */ + p == pattern + 1 + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's come before. */ + || at_begline_loc_p (pattern, p, syntax)) + BUF_PUSH (begline); + else + goto normal_char; + } + break; + + + case '$': + { + if ( /* If at end of pattern, it's an operator. */ + p == pend + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's next. */ + || at_endline_loc_p (p, pend, syntax)) + BUF_PUSH (endline); + else + goto normal_char; + } + break; + + + case '+': + case '?': + if ((syntax & RE_BK_PLUS_QM) + || (syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern... */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (!(syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + + { + /* Are we optimizing this jump? */ + boolean keep_string_p = false; + + /* 1 means zero (many) matches is allowed. */ + char zero_times_ok = 0, many_times_ok = 0; + + /* If there is a sequence of repetition chars, collapse it + down to just one (the right one). We can't combine + interval operators with these because of, e.g., `a{2}*', + which should only match an even number of `a's. */ + + for (;;) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + + if (p == pend) + break; + + PATFETCH (c); + + if (c == '*' + || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) + ; + + else if (syntax & RE_BK_PLUS_QM && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + + c = c1; + } + else + { + PATUNFETCH; + break; + } + + /* If we get here, we found another repeat character. */ + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ + if (many_times_ok) + { /* More than one repetition is allowed, so put in at the + end a backward relative jump from `b' to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). + + But if we are at the `*' in the exact sequence `.*\n', + insert an unconditional jump backwards to the ., + instead of the beginning of the loop. This way we only + push a failure point once, instead of every time + through the loop. */ + assert (p - 1 > pattern); + + /* Allocate the space for the jump. */ + GET_BUFFER_SPACE (3); + + /* We know we are not at the first character of the pattern, + because laststart was nonzero. And we've already + incremented `p', by the way, to be the character after + the `*'. Do we have to do something analogous here + for null bytes, because of RE_DOT_NOT_NULL? */ + if (TRANSLATE (*(p - 2)) == TRANSLATE ('.') + && zero_times_ok + && p < pend && TRANSLATE (*p) == TRANSLATE ('\n') + && !(syntax & RE_DOT_NEWLINE)) + { /* We have .*\n. */ + STORE_JUMP (jump, b, laststart); + keep_string_p = true; + } + else + /* Anything else. */ + STORE_JUMP (maybe_pop_jump, b, laststart - 3); + + /* We've added more stuff to the buffer. */ + b += 3; + } + + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump + : on_failure_jump, + laststart, b + 3); + pending_exact = 0; + b += 3; + + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + `dummy_failure_jump' before the initial + `on_failure_jump' instruction of the loop. This + effects a skip over that instruction the first time + we hit that loop. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); + b += 3; + } + } + break; + + + case '.': + laststart = b; + BUF_PUSH (anychar); + break; + + + case '[': + { + boolean had_char_class = false; + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + /* Ensure that we have enough space to push a charset: the + opcode, the length count, and the bitset; 34 bytes in all. */ + GET_BUFFER_SPACE (34); + + laststart = b; + + /* We test `*p == '^' twice, instead of using an if + statement, so we only need one BUF_PUSH. */ + BUF_PUSH (*p == '^' ? charset_not : charset); + if (*p == '^') + p++; + + /* Remember the first position in the bracket expression. */ + p1 = p; + + /* Push the number of bytes in the bitmap. */ + BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + + /* Clear the whole map. */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + + /* charset_not matches newline according to a syntax bit. */ + if ((re_opcode_t) b[-2] == charset_not + && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) + SET_LIST_BIT ('\n'); + + /* Read in characters and ranges, setting map bits. */ + for (;;) + { + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + PATFETCH (c); + + /* \ might escape characters inside [...] and [^...]. */ + if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + PATFETCH (c1); + SET_LIST_BIT (c1); + continue; + } + + /* Could be the end of the bracket expression. If it's + not (i.e., when the bracket expression is `[]' so + far), the ']' character bit gets set way below. */ + if (c == ']' && p != p1 + 1) + break; + + /* Look ahead to see if it's a range when the last thing + was a character class. */ + if (had_char_class && c == '-' && *p != ']') + FREE_STACK_RETURN (REG_ERANGE); + + /* Look ahead to see if it's a range when the last thing + was a character: if this is a hyphen not at the + beginning or the end of a list, then it's the range + operator. */ + if (c == '-' + && !(p - 2 >= pattern && p[-2] == '[') + && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') + && *p != ']') + { + reg_errcode_t ret + = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) FREE_STACK_RETURN (ret); + } + + else if (p[0] == '-' && p[1] != ']') + { /* This handles ranges made up of characters only. */ + reg_errcode_t ret; + + /* Move past the `-'. */ + PATFETCH (c1); + + ret = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) FREE_STACK_RETURN (ret); + } + + /* See if we're at the beginning of a possible character + class. */ + + else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') + { /* Leave room for the null. */ + char str[CHAR_CLASS_MAX_LENGTH + 1]; + + PATFETCH (c); + c1 = 0; + + /* If pattern is `[[:'. */ + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (;;) + { + PATFETCH (c); + if (c == ':' || c == ']' || p == pend + || c1 == CHAR_CLASS_MAX_LENGTH) + break; + str[c1++] = c; + } + str[c1] = '\0'; + + /* If isn't a word bracketed by `[:' and:`]': + undo the ending character, the letters, and leave + the leading `:' and `[' (but set bits for them). */ + if (c == ':' && *p == ']') + { + int ch; + boolean is_alnum = STREQ (str, "alnum"); + boolean is_alpha = STREQ (str, "alpha"); + boolean is_blank = STREQ (str, "blank"); + boolean is_cntrl = STREQ (str, "cntrl"); + boolean is_digit = STREQ (str, "digit"); + boolean is_graph = STREQ (str, "graph"); + boolean is_lower = STREQ (str, "lower"); + boolean is_print = STREQ (str, "print"); + boolean is_punct = STREQ (str, "punct"); + boolean is_space = STREQ (str, "space"); + boolean is_upper = STREQ (str, "upper"); + boolean is_xdigit = STREQ (str, "xdigit"); + + if (!IS_CHAR_CLASS (str)) + FREE_STACK_RETURN (REG_ECTYPE); + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (ch = 0; ch < 1 << BYTEWIDTH; ch++) + { + /* This was split into 3 if's to + avoid an arbitrary limit in some compiler. */ + if ( (is_alnum && ISALNUM (ch)) + || (is_alpha && ISALPHA (ch)) + || (is_blank && ISBLANK (ch)) + || (is_cntrl && ISCNTRL (ch))) + SET_LIST_BIT (ch); + if ( (is_digit && ISDIGIT (ch)) + || (is_graph && ISGRAPH (ch)) + || (is_lower && ISLOWER (ch)) + || (is_print && ISPRINT (ch))) + SET_LIST_BIT (ch); + if ( (is_punct && ISPUNCT (ch)) + || (is_space && ISSPACE (ch)) + || (is_upper && ISUPPER (ch)) + || (is_xdigit && ISXDIGIT (ch))) + SET_LIST_BIT (ch); + } + had_char_class = true; + } + else + { + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + had_char_class = false; + } + } + else + { + had_char_class = false; + SET_LIST_BIT (c); + } + } + + /* Discard any (non)matching list bytes that are all 0 at the + end of the map. Decrease the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + } + break; + + + case '(': + if (syntax & RE_NO_BK_PARENS) + goto handle_open; + else + goto normal_char; + + + case ')': + if (syntax & RE_NO_BK_PARENS) + goto handle_close; + else + goto normal_char; + + + case '\n': + if (syntax & RE_NEWLINE_ALT) + goto handle_alt; + else + goto normal_char; + + + case '|': + if (syntax & RE_NO_BK_VBAR) + goto handle_alt; + else + goto normal_char; + + + case '{': + if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) + goto handle_interval; + else + goto normal_char; + + + case '\\': + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + /* Do not translate the character after the \, so that we can + distinguish, e.g., \B from \b, even if we normally would + translate, e.g., B to b. */ + PATFETCH_RAW (c); + + switch (c) + { + case '(': + if (syntax & RE_NO_BK_PARENS) + goto normal_backslash; + + handle_open: + bufp->re_nsub++; + regnum++; + + if (COMPILE_STACK_FULL) + { + RETALLOC (compile_stack.stack, compile_stack.size << 1, + compile_stack_elt_t); + if (compile_stack.stack == NULL) return REG_ESPACE; + + compile_stack.size <<= 1; + } + + /* These are the values to restore when we hit end of this + group. They are all relative offsets, so that if the + whole pattern moves because of realloc, they will still + be valid. */ + COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; + COMPILE_STACK_TOP.fixup_alt_jump + = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; + COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; + COMPILE_STACK_TOP.regnum = regnum; + + /* We will eventually replace the 0 with the number of + groups inner to this one. But do not push a + start_memory for groups beyond the last one we can + represent in the compiled pattern. */ + if (regnum <= MAX_REGNUM) + { + COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; + BUF_PUSH_3 (start_memory, regnum, 0); + } + + compile_stack.avail++; + + fixup_alt_jump = 0; + laststart = 0; + begalt = b; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + break; + + + case ')': + if (syntax & RE_NO_BK_PARENS) goto normal_backslash; + + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_backslash; + else + FREE_STACK_RETURN (REG_ERPAREN); + + handle_close: + if (fixup_alt_jump) + { /* Push a dummy failure point at the end of the + alternative for a possible future + `pop_failure_jump' to pop. See comments at + `push_dummy_failure' in `re_match_2'. */ + BUF_PUSH (push_dummy_failure); + + /* We allocated space for this jump when we assigned + to `fixup_alt_jump', in the `handle_alt' case below. */ + STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); + } + + /* See similar code for backslashed left paren above. */ + if (COMPILE_STACK_EMPTY) + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + else + FREE_STACK_RETURN (REG_ERPAREN); + + /* Since we just checked for an empty stack above, this + ``can't happen''. */ + assert (compile_stack.avail != 0); + { + /* We don't just want to restore into `regnum', because + later groups should continue to be numbered higher, + as in `(ab)c(de)' -- the second group is #2. */ + regnum_t this_group_regnum; + + compile_stack.avail--; + begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; + fixup_alt_jump + = COMPILE_STACK_TOP.fixup_alt_jump + ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 + : 0; + laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; + this_group_regnum = COMPILE_STACK_TOP.regnum; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + + /* We're at the end of the group, so now we know how many + groups were inside this one. */ + if (this_group_regnum <= MAX_REGNUM) + { + unsigned char *inner_group_loc + = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; + + *inner_group_loc = regnum - this_group_regnum; + BUF_PUSH_3 (stop_memory, this_group_regnum, + regnum - this_group_regnum); + } + } + break; + + + case '|': /* `\|'. */ + if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) + goto normal_backslash; + handle_alt: + if (syntax & RE_LIMITED_OPS) + goto normal_char; + + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (on_failure_jump, begalt, b + 6); + pending_exact = 0; + b += 3; + + /* The alternative before this one has a jump after it + which gets executed if it gets matched. Adjust that + jump so it will jump to this alternative's analogous + jump (put in below, which in turn will jump to the next + (if any) alternative's such jump, etc.). The last such + jump jumps to the correct final destination. A picture: + _____ _____ + | | | | + | v | v + a | b | c + + If we are at `b', then fixup_alt_jump right now points to a + three-byte space after `a'. We'll put in the jump, set + fixup_alt_jump to right after `b', and leave behind three + bytes which we'll fill in when we get to after `c'. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + /* Mark and leave space for a jump after this alternative, + to be filled in later either by next alternative or + when know we're at the end of a series of alternatives. */ + fixup_alt_jump = b; + GET_BUFFER_SPACE (3); + b += 3; + + laststart = 0; + begalt = b; + break; + + + case '{': + /* If \{ is a literal. */ + if (!(syntax & RE_INTERVALS) + /* If we're at `\{' and it's not the open-interval + operator. */ + || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + || (p - 2 == pattern && p == pend)) + goto normal_backslash; + + handle_interval: + { + /* If got here, then the syntax allows intervals. */ + + /* At least (most) this many matches must be made. */ + int lower_bound = -1, upper_bound = -1; + + beg_interval = p - 1; + + if (p == pend) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_EBRACE); + } + + GET_UNSIGNED_NUMBER (lower_bound); + + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) upper_bound = RE_DUP_MAX; + } + else + /* Interval such as `{1}' => match exactly once. */ + upper_bound = lower_bound; + + if (lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (c != '\\') FREE_STACK_RETURN (REG_EBRACE); + + PATFETCH (c); + } + + if (c != '}') + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + /* We just parsed a valid interval. */ + + /* If it's invalid to have no preceding re. */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (syntax & RE_CONTEXT_INDEP_OPS) + laststart = b; + else + goto unfetch_interval; + } + + /* If the upper bound is zero, don't want to succeed at + all; jump from `laststart' to `b + 3', which will be + the end of the buffer after we insert the jump. */ + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + INSERT_JUMP (jump, laststart, b + 3); + b += 3; + } + + /* Otherwise, we have a nontrivial interval. When + we're all done, the pattern will look like: + set_number_at + set_number_at + succeed_n + + jump_n + (The upper bound and `jump_n' are omitted if + `upper_bound' is 1, though.) */ + else + { /* If the upper bound is > 1, we need to insert + more at the end of the loop. */ + unsigned nbytes = 10 + (upper_bound > 1) * 10; + + GET_BUFFER_SPACE (nbytes); + + /* Initialize lower bound of the `succeed_n', even + though it will be set during matching by its + attendant `set_number_at' (inserted next), + because `re_compile_fastmap' needs to know. + Jump to the `jump_n' we might insert below. */ + INSERT_JUMP2 (succeed_n, laststart, + b + 5 + (upper_bound > 1) * 5, + lower_bound); + b += 5; + + /* Code to initialize the lower bound. Insert + before the `succeed_n'. The `5' is the last two + bytes of this `set_number_at', plus 3 bytes of + the following `succeed_n'. */ + insert_op2 (set_number_at, laststart, 5, lower_bound, b); + b += 5; + + if (upper_bound > 1) + { /* More than one repetition is allowed, so + append a backward jump to the `succeed_n' + that starts this interval. + + When we've reached this during matching, + we'll have matched the interval once, so + jump back only `upper_bound - 1' times. */ + STORE_JUMP2 (jump_n, b, laststart + 5, + upper_bound - 1); + b += 5; + + /* The location we want to set is the second + parameter of the `jump_n'; that is `b-2' as + an absolute address. `laststart' will be + the `set_number_at' we're about to insert; + `laststart+3' the number to set, the source + for the relative address. But we are + inserting into the middle of the pattern -- + so everything is getting moved up by 5. + Conclusion: (b - 2) - (laststart + 3) + 5, + i.e., b - laststart. + + We insert this at the beginning of the loop + so that if we fail during matching, we'll + reinitialize the bounds. */ + insert_op2 (set_number_at, laststart, b - laststart, + upper_bound - 1, b); + b += 5; + } + } + pending_exact = 0; + beg_interval = NULL; + } + break; + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + assert (beg_interval); + p = beg_interval; + beg_interval = NULL; + + /* normal_char and normal_backslash need `c'. */ + PATFETCH (c); + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (p > pattern && p[-1] == '\\') + goto normal_backslash; + } + goto normal_char; + +#ifdef emacs + /* There is no way to specify the before_dot and after_dot + operators. rms says this is ok. --karl */ + case '=': + BUF_PUSH (at_dot); + break; + + case 's': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); + break; +#endif /* emacs */ + + + case 'w': + laststart = b; + BUF_PUSH (wordchar); + break; + + + case 'W': + laststart = b; + BUF_PUSH (notwordchar); + break; + + + case '<': + BUF_PUSH (wordbeg); + break; + + case '>': + BUF_PUSH (wordend); + break; + + case 'b': + BUF_PUSH (wordbound); + break; + + case 'B': + BUF_PUSH (notwordbound); + break; + + case '`': + BUF_PUSH (begbuf); + break; + + case '\'': + BUF_PUSH (endbuf); + break; + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (syntax & RE_NO_BK_REFS) + goto normal_char; + + c1 = c - '0'; + + if (c1 > regnum) + FREE_STACK_RETURN (REG_ESUBREG); + + /* Can't back reference to a subexpression if inside of it. */ + if (group_in_compile_stack (compile_stack, c1)) + goto normal_char; + + laststart = b; + BUF_PUSH_2 (duplicate, c1); + break; + + + case '+': + case '?': + if (syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backslash; + + default: + normal_backslash: + /* You might think it would be useful for \ to mean + not to translate; but if we don't translate it + it will never match anything. */ + c = TRANSLATE (c); + goto normal_char; + } + break; + + + default: + /* Expects the character in `c'. */ + normal_char: + /* If no exactn currently being built. */ + if (!pending_exact + + /* If last exactn not at current position. */ + || pending_exact + *pending_exact + 1 != b + + /* We have only one byte following the exactn for the count. */ + || *pending_exact == (1 << BYTEWIDTH) - 1 + + /* If followed by a repetition operator. */ + || *p == '*' || *p == '^' + || ((syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((syntax & RE_INTERVALS) + && ((syntax & RE_NO_BK_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + /* Start building a new exactn. */ + + laststart = b; + + BUF_PUSH_2 (exactn, 0); + pending_exact = b - 1; + } + + BUF_PUSH (c); + (*pending_exact)++; + break; + } /* switch (c) */ + } /* while p != pend */ + + + /* Through the pattern now. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + if (!COMPILE_STACK_EMPTY) + FREE_STACK_RETURN (REG_EPAREN); + + free (compile_stack.stack); + + /* We have succeeded; set the length of the buffer. */ + bufp->used = b - bufp->buffer; + +#ifdef DEBUG + if (debug) + { + DEBUG_PRINT1 ("\nCompiled pattern: \n"); + print_compiled_pattern (bufp); + } +#endif /* DEBUG */ + +#ifndef MATCH_MAY_ALLOCATE + /* Initialize the failure stack to the largest possible stack. This + isn't necessary unless we're trying to avoid calling alloca in + the search and match routines. */ + { + int num_regs = bufp->re_nsub + 1; + + /* Since DOUBLE_FAIL_STACK refuses to double only if the current size + is strictly greater than re_max_failures, the largest possible stack + is 2 * re_max_failures failure points. */ + if (fail_stack.size < (2 * re_max_failures * MAX_FAILURE_ITEMS)) + { + fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS); + +#ifdef emacs + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) xmalloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) xrealloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +#else /* not emacs */ + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) malloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) realloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +#endif /* not emacs */ + } + + /* Initialize some other variables the matcher uses. */ + RETALLOC_IF (regstart, num_regs, const char *); + RETALLOC_IF (regend, num_regs, const char *); + RETALLOC_IF (old_regstart, num_regs, const char *); + RETALLOC_IF (old_regend, num_regs, const char *); + RETALLOC_IF (best_regstart, num_regs, const char *); + RETALLOC_IF (best_regend, num_regs, const char *); + RETALLOC_IF (reg_info, num_regs, register_info_type); + RETALLOC_IF (reg_dummy, num_regs, const char *); + RETALLOC_IF (reg_info_dummy, num_regs, register_info_type); + } +#endif + + return REG_NOERROR; +} /* regex_compile */ + +/* Subroutines for `regex_compile'. */ + +/* Store OP at LOC followed by two-byte integer parameter ARG. */ + +static void +store_op1 (op, loc, arg) + re_opcode_t op; + unsigned char *loc; + int arg; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg); +} + + +/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +store_op2 (op, loc, arg1, arg2) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg1); + STORE_NUMBER (loc + 3, arg2); +} + + +/* Copy the bytes from LOC to END to open up three bytes of space at LOC + for OP followed by two-byte integer parameter ARG. */ + +static void +insert_op1 (op, loc, arg, end) + re_opcode_t op; + unsigned char *loc; + int arg; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 3; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op1 (op, loc, arg); +} + + +/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +insert_op2 (op, loc, arg1, arg2, end) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 5; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op2 (op, loc, arg1, arg2); +} + + +/* P points to just after a ^ in PATTERN. Return true if that ^ comes + after an alternative or a begin-subexpression. We assume there is at + least one character before the ^. */ + +static boolean +at_begline_loc_p (pattern, p, syntax) + const char *pattern, *p; + reg_syntax_t syntax; +{ + const char *prev = p - 2; + boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; + + return + /* After a subexpression? */ + (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) + /* After an alternative? */ + || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); +} + + +/* The dual of at_begline_loc_p. This one is for $. We assume there is + at least one character after the $, i.e., `P < PEND'. */ + +static boolean +at_endline_loc_p (p, pend, syntax) + const char *p, *pend; + int syntax; +{ + const char *next = p; + boolean next_backslash = *next == '\\'; + const char *next_next = p + 1 < pend ? p + 1 : NULL; + + return + /* Before a subexpression? */ + (syntax & RE_NO_BK_PARENS ? *next == ')' + : next_backslash && next_next && *next_next == ')') + /* Before an alternative? */ + || (syntax & RE_NO_BK_VBAR ? *next == '|' + : next_backslash && next_next && *next_next == '|'); +} + + +/* Returns true if REGNUM is in one of COMPILE_STACK's elements and + false if it's not. */ + +static boolean +group_in_compile_stack (compile_stack, regnum) + compile_stack_type compile_stack; + regnum_t regnum; +{ + int this_element; + + for (this_element = compile_stack.avail - 1; + this_element >= 0; + this_element--) + if (compile_stack.stack[this_element].regnum == regnum) + return true; + + return false; +} + + +/* Read the ending character of a range (in a bracket expression) from the + uncompiled pattern *P_PTR (which ends at PEND). We assume the + starting character is in `P[-2]'. (`P[-1]' is the character `-'.) + Then we set the translation of all bits between the starting and + ending characters (inclusive) in the compiled pattern B. + + Return an error code. + + We use these short variable names so we can use the same macros as + `regex_compile' itself. */ + +static reg_errcode_t +compile_range (p_ptr, pend, translate, syntax, b) + const char **p_ptr, *pend; + char *translate; + reg_syntax_t syntax; + unsigned char *b; +{ + unsigned this_char; + + const char *p = *p_ptr; + int range_start, range_end; + + if (p == pend) + return REG_ERANGE; + + /* Even though the pattern is a signed `char *', we need to fetch + with unsigned char *'s; if the high bit of the pattern character + is set, the range endpoints will be negative if we fetch using a + signed char *. + + We also want to fetch the endpoints without translating them; the + appropriate translation is done in the bit-setting loop below. */ + /* The SVR4 compiler on the 3B2 had trouble with unsigned const char *. */ + range_start = ((const unsigned char *) p)[-2]; + range_end = ((const unsigned char *) p)[0]; + + /* Have to increment the pointer into the pattern string, so the + caller isn't still at the ending character. */ + (*p_ptr)++; + + /* If the start is after the end, the range is empty. */ + if (range_start > range_end) + return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR; + + /* Here we see why `this_char' has to be larger than an `unsigned + char' -- the range is inclusive, so if `range_end' == 0xff + (assuming 8-bit characters), we would otherwise go into an infinite + loop, since all characters <= 0xff. */ + for (this_char = range_start; this_char <= range_end; this_char++) + { + SET_LIST_BIT (TRANSLATE (this_char)); + } + + return REG_NOERROR; +} + +/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in + BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible + characters can start a string that matches the pattern. This fastmap + is used by re_search to skip quickly over impossible starting points. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as BUFP->fastmap. + + We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in + the pattern buffer. + + Returns 0 if we succeed, -2 if an internal error. */ + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + int j, k; +#ifdef MATCH_MAY_ALLOCATE + fail_stack_type fail_stack; +#endif +#ifndef REGEX_MALLOC + char *destination; +#endif + /* We don't push any register information onto the failure stack. */ + unsigned num_regs = 0; + + register char *fastmap = bufp->fastmap; + unsigned char *pattern = bufp->buffer; + unsigned long size = bufp->used; + unsigned char *p = pattern; + register unsigned char *pend = pattern + size; + + /* Assume that each path through the pattern can be null until + proven otherwise. We set this false at the bottom of switch + statement, to which we get only if a particular path doesn't + match the empty string. */ + boolean path_can_be_null = true; + + /* We aren't doing a `succeed_n' to begin with. */ + boolean succeed_n_p = false; + + assert (fastmap != NULL && p != NULL); + + INIT_FAIL_STACK (); + bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */ + bufp->fastmap_accurate = 1; /* It will be when we're done. */ + bufp->can_be_null = 0; + + while (p != pend || !FAIL_STACK_EMPTY ()) + { + if (p == pend) + { + bufp->can_be_null |= path_can_be_null; + + /* Reset for next path. */ + path_can_be_null = true; + + p = fail_stack.stack[--fail_stack.avail]; + } + + /* We should never be about to go beyond the end of the pattern. */ + assert (p < pend); + +#ifdef SWITCH_ENUM_BUG + switch ((int) ((re_opcode_t) *p++)) +#else + switch ((re_opcode_t) *p++) +#endif + { + + /* I guess the idea here is to simply not bother with a fastmap + if a backreference is used, since it's too hard to figure out + the fastmap for the corresponding group. Setting + `can_be_null' stops `re_search_2' from using the fastmap, so + that is all we do. */ + case duplicate: + bufp->can_be_null = 1; + return 0; + + + /* Following are the cases which match a character. These end + with `break'. */ + + case exactn: + fastmap[p[1]] = 1; + break; + + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + fastmap[j] = 1; + break; + + + case charset_not: + /* Chars beyond end of map must be allowed. */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + fastmap[j] = 1; + break; + + + case wordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == Sword) + fastmap[j] = 1; + break; + + + case notwordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != Sword) + fastmap[j] = 1; + break; + + + case anychar: + { + int fastmap_newline = fastmap['\n']; + + /* `.' matches anything ... */ + for (j = 0; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + /* ... except perhaps newline. */ + if (!(bufp->syntax & RE_DOT_NEWLINE)) + fastmap['\n'] = fastmap_newline; + + /* Return if we have already set `can_be_null'; if we have, + then the fastmap is irrelevant. Something's wrong here. */ + else if (bufp->can_be_null) + return 0; + + /* Otherwise, have to check alternative paths. */ + break; + } + +#ifdef emacs + case syntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + case notsyntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + /* All cases after this match the empty string. These end with + `continue'. */ + + + case before_dot: + case at_dot: + case after_dot: + continue; +#endif /* not emacs */ + + + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + case push_dummy_failure: + continue; + + + case jump_n: + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case jump_past_alt: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (j > 0) + continue; + + /* Jump backward implies we just went through the body of a + loop and matched nothing. Opcode jumped to should be + `on_failure_jump' or `succeed_n'. Just treat it like an + ordinary jump. For a * loop, it has pushed its failure + point already; if so, discard that as redundant. */ + if ((re_opcode_t) *p != on_failure_jump + && (re_opcode_t) *p != succeed_n) + continue; + + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + + /* If what's on the stack is where we are now, pop it. */ + if (!FAIL_STACK_EMPTY () + && fail_stack.stack[fail_stack.avail - 1] == p) + fail_stack.avail--; + + continue; + + + case on_failure_jump: + case on_failure_keep_string_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + + /* For some patterns, e.g., `(a?)?', `p+j' here points to the + end of the pattern. We don't want to push such a point, + since when we restore it above, entering the switch will + increment `p' past the end of the pattern. We don't need + to push such a point since we obviously won't find any more + fastmap entries beyond `pend'. Such a pattern can match + the null string, though. */ + if (p + j < pend) + { + if (!PUSH_PATTERN_OP (p + j, fail_stack)) + return -2; + } + else + bufp->can_be_null = 1; + + if (succeed_n_p) + { + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + succeed_n_p = false; + } + + continue; + + + case succeed_n: + /* Get to the number of times to succeed. */ + p += 2; + + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR (k, p); + if (k == 0) + { + p -= 4; + succeed_n_p = true; /* Spaghetti code alert. */ + goto handle_on_failure_jump; + } + continue; + + + case set_number_at: + p += 4; + continue; + + + case start_memory: + case stop_memory: + p += 2; + continue; + + + default: + abort (); /* We have listed all the cases. */ + } /* switch *p++ */ + + /* Getting here means we have found the possible starting + characters for one path of the pattern -- and that the empty + string does not match. We need not follow this path further. + Instead, look at the next alternative (remembered on the + stack), or quit if no more. The test at the top of the loop + does these things. */ + path_can_be_null = false; + p = pend; + } /* while p */ + + /* Set `can_be_null' for the last path (also the first path, if the + pattern is empty). */ + bufp->can_be_null |= path_can_be_null; + return 0; +} /* re_compile_fastmap */ + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t *) 0; + } +} + +/* Searching routines. */ + +/* Like re_search_2, below, but only one string is specified, and + doesn't let you say where to stop matching. */ + +int +re_search (bufp, string, size, startpos, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, startpos, range; + struct re_registers *regs; +{ + return re_search_2 (bufp, NULL, 0, string, size, startpos, range, + regs, size); +} + + +/* Using the compiled pattern in BUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. + + STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. + + RANGE is how far to scan while trying to match. RANGE = 0 means try + only at STARTPOS; in general, the last start tried is STARTPOS + + RANGE. + + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire BUFP->buffer and its contained + subexpressions. + + Do not consider matching one past the index STOP in the virtual + concatenation of STRING1 and STRING2. + + We return either the position in the strings at which the match was + found, -1 if no match, or -2 if error (such as failure + stack overflow). */ + +int +re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int startpos; + int range; + struct re_registers *regs; + int stop; +{ + int val; + register char *fastmap = bufp->fastmap; + register char *translate = bufp->translate; + int total_size = size1 + size2; + int endpos = startpos + range; + + /* Check for out-of-range STARTPOS. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up RANGE if it might eventually take us outside + the virtual concatenation of STRING1 and STRING2. */ + if (endpos < -1) + range = -1 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* If the search isn't to be a backwards one, don't waste time in a + search for a pattern that must be anchored. */ + if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0) + { + if (startpos > 0) + return -1; + else + range = 1; + } + + /* Update the fastmap now if not correct already. */ + if (fastmap && !bufp->fastmap_accurate) + if (re_compile_fastmap (bufp) == -2) + return -2; + + /* Loop through the string, looking for a place to start matching. */ + for (;;) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot be the start of a match. If the pattern can match the + null string, however, we don't need to skip characters; we want + the first null string. */ + if (fastmap && startpos < total_size && !bufp->can_be_null) + { + if (range > 0) /* Searching forwards. */ + { + register const char *d; + register int lim = 0; + int irange = range; + + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + + d = (startpos >= size1 ? string2 - size1 : string1) + startpos; + + /* Written out as an if-else to avoid testing `translate' + inside the loop. */ + if (translate) + while (range > lim + && !fastmap[(unsigned char) + translate[(unsigned char) *d++]]) + range--; + else + while (range > lim && !fastmap[(unsigned char) *d++]) + range--; + + startpos += irange - range; + } + else /* Searching backwards. */ + { + register char c = (size1 == 0 || startpos >= size1 + ? string2[startpos - size1] + : string1[startpos]); + + if (!fastmap[(unsigned char) TRANSLATE (c)]) + goto advance; + } + } + + /* If can't match the null string, and that's all we have left, fail. */ + if (range >= 0 && startpos == total_size && fastmap + && !bufp->can_be_null) + return -1; + + val = re_match_2_internal (bufp, string1, size1, string2, size2, + startpos, regs, stop); +#ifndef REGEX_MALLOC +#ifdef C_ALLOCA + alloca (0); +#endif +#endif + + if (val >= 0) + return startpos; + + if (val == -2) + return -2; + + advance: + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } + } + return -1; +} /* re_search_2 */ + +/* Declarations and macros for re_match_2. */ + +static int bcmp_translate (); +static boolean alt_match_null_string_p (), + common_op_match_null_string_p (), + group_match_null_string_p (); + +/* This converts PTR, a pointer into one of the search strings `string1' + and `string2' into an offset from the beginning of that string. */ +#define POINTER_TO_OFFSET(ptr) \ + (FIRST_STRING_P (ptr) \ + ? ((regoff_t) ((ptr) - string1)) \ + : ((regoff_t) ((ptr) - string2 + size1))) + +/* Macros for dealing with the split strings in re_match_2. */ + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Call before fetching a character with *d. This switches over to + string2 if necessary. */ +#define PREFETCH() \ + while (d == dend) \ + { \ + /* End of string2 => fail. */ \ + if (dend == end_match_2) \ + goto fail; \ + /* End of string1 => advance to string2. */ \ + d = string2; \ + dend = end_match_2; \ + } + + +/* Test if at very beginning or at very end of the virtual concatenation + of `string1' and `string2'. If only one string, it's `string2'. */ +#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END(d) ((d) == end2) + + +/* Test if D points to a character which is word-constituent. We have + two special cases to check for: if past the end of string1, look at + the first character in string2; and if before the beginning of + string2, look at the last character in string1. */ +#define WORDCHAR_P(d) \ + (SYNTAX ((d) == end1 ? *string2 \ + : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ + == Sword) + +/* Test if the character before D and the one at D differ with respect + to being word-constituent. */ +#define AT_WORD_BOUNDARY(d) \ + (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ + || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) + + +/* Free everything we malloc. */ +#ifdef MATCH_MAY_ALLOCATE +#ifdef REGEX_MALLOC +#define FREE_VAR(var) if (var) free (var); var = NULL +#define FREE_VARIABLES() \ + do { \ + FREE_VAR (fail_stack.stack); \ + FREE_VAR (regstart); \ + FREE_VAR (regend); \ + FREE_VAR (old_regstart); \ + FREE_VAR (old_regend); \ + FREE_VAR (best_regstart); \ + FREE_VAR (best_regend); \ + FREE_VAR (reg_info); \ + FREE_VAR (reg_dummy); \ + FREE_VAR (reg_info_dummy); \ + } while (0) +#else /* not REGEX_MALLOC */ +/* This used to do alloca (0), but now we do that in the caller. */ +#define FREE_VARIABLES() /* Nothing */ +#endif /* not REGEX_MALLOC */ +#else +#define FREE_VARIABLES() /* Do nothing! */ +#endif /* not MATCH_MAY_ALLOCATE */ + +/* These values must meet several constraints. They must not be valid + register values; since we have a limit of 255 registers (because + we use only one byte in the pattern for the register number), we can + use numbers larger than 255. They must differ by 1, because of + NUM_FAILURE_ITEMS above. And the value for the lowest register must + be larger than the value for the highest register, so we do not try + to actually save any registers when none are active. */ +#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) +#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) + +/* Matching routines. */ + +#ifndef emacs /* Emacs never uses this. */ +/* re_match is like re_match_2 except it takes only a single string. */ + +int +re_match (bufp, string, size, pos, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, pos; + struct re_registers *regs; +{ + int result = re_match_2_internal (bufp, NULL, 0, string, size, + pos, regs, size); + alloca (0); + return result; +} +#endif /* not emacs */ + + +/* re_match_2 matches the compiled pattern in BUFP against the + the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 + and SIZE2, respectively). We start matching at POS, and stop + matching at STOP. + + If REGS is non-null and the `no_sub' field of BUFP is nonzero, we + store offsets for the substring each group matched in REGS. See the + documentation for exactly how many groups we fill. + + We return -1 if no match, -2 if an internal error (such as the + failure stack overflowing). Otherwise, we return the length of the + matched substring. */ + +int +re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ + int result = re_match_2_internal (bufp, string1, size1, string2, size2, + pos, regs, stop); + alloca (0); + return result; +} + +/* This is a separate function so that we can force an alloca cleanup + afterwards. */ +static int +re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ + /* General temporaries. */ + int mcnt; + unsigned char *p1; + + /* Just past the end of the corresponding string. */ + const char *end1, *end2; + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ + const char *end_match_1, *end_match_2; + + /* Where we are in the data, and the end of the current string. */ + const char *d, *dend; + + /* Where we are in the pattern, and the end of the pattern. */ + unsigned char *p = bufp->buffer; + register unsigned char *pend = p + bufp->used; + + /* Mark the opcode just after a start_memory, so we can test for an + empty subpattern when we get to the stop_memory. */ + unsigned char *just_past_start_mem = 0; + + /* We use this to map every character in the string. */ + char *translate = bufp->translate; + + /* Failure point stack. Each place that can handle a failure further + down the line pushes a failure point on this stack. It consists of + restart, regend, and reg_info for all registers corresponding to + the subexpressions we're currently inside, plus the number of such + registers, and, finally, two char *'s. The first char * is where + to resume scanning the pattern; the second one is where to resume + scanning the strings. If the latter is zero, the failure point is + a ``dummy''; if a failure happens and the failure point is a dummy, + it gets discarded and the next next one is tried. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ + fail_stack_type fail_stack; +#endif +#ifdef DEBUG + static unsigned failure_id = 0; + unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0; +#endif + + /* We fill all the registers internally, independent of what we + return, for use in backreferences. The number here includes + an element for register zero. */ + unsigned num_regs = bufp->re_nsub + 1; + + /* The currently active registers. */ + unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG; + unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG; + + /* Information on the contents of registers. These are pointers into + the input strings; they record just what was matched (on this + attempt) by a subexpression part of the pattern, that is, the + regnum-th regstart pointer points to where in the pattern we began + matching and the regnum-th regend points to right after where we + stopped matching the regnum-th subexpression. (The zeroth register + keeps track of what the whole pattern matches.) */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **regstart, **regend; +#endif + + /* If a group that's operated upon by a repetition operator fails to + match anything, then the register for its start will need to be + restored because it will have been set to wherever in the string we + are when we last see its open-group operator. Similarly for a + register's end. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **old_regstart, **old_regend; +#endif + + /* The is_active field of reg_info helps us keep track of which (possibly + nested) subexpressions we are currently in. The matched_something + field of reg_info[reg_num] helps us tell whether or not we have + matched any of the pattern so far this time through the reg_num-th + subexpression. These two fields get reset each time through any + loop their register is in. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ + register_info_type *reg_info; +#endif + + /* The following record the register info as found in the above + variables when we find a match better than any we've seen before. + This happens as we backtrack through the failure points, which in + turn happens only if we have not yet matched the entire string. */ + unsigned best_regs_set = false; +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **best_regstart, **best_regend; +#endif + + /* Logically, this is `best_regend[0]'. But we don't want to have to + allocate space for that if we're not allocating space for anything + else (see below). Also, we never need info about register 0 for + any of the other register vectors, and it seems rather a kludge to + treat `best_regend' differently than the rest. So we keep track of + the end of the best match so far in a separate variable. We + initialize this to NULL so that when we backtrack the first time + and need to test it, it's not garbage. */ + const char *match_end = NULL; + + /* Used when we pop values we don't care about. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **reg_dummy; + register_info_type *reg_info_dummy; +#endif + +#ifdef DEBUG + /* Counts the total number of registers pushed. */ + unsigned num_regs_pushed = 0; +#endif + + DEBUG_PRINT1 ("\n\nEntering re_match_2.\n"); + + INIT_FAIL_STACK (); + +#ifdef MATCH_MAY_ALLOCATE + /* Do not bother to initialize all the register variables if there are + no groups in the pattern, as it takes a fair amount of time. If + there are groups, we include space for register 0 (the whole + pattern), even though we never use it, since it simplifies the + array indexing. We should fix this. */ + if (bufp->re_nsub) + { + regstart = REGEX_TALLOC (num_regs, const char *); + regend = REGEX_TALLOC (num_regs, const char *); + old_regstart = REGEX_TALLOC (num_regs, const char *); + old_regend = REGEX_TALLOC (num_regs, const char *); + best_regstart = REGEX_TALLOC (num_regs, const char *); + best_regend = REGEX_TALLOC (num_regs, const char *); + reg_info = REGEX_TALLOC (num_regs, register_info_type); + reg_dummy = REGEX_TALLOC (num_regs, const char *); + reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); + + if (!(regstart && regend && old_regstart && old_regend && reg_info + && best_regstart && best_regend && reg_dummy && reg_info_dummy)) + { + FREE_VARIABLES (); + return -2; + } + } +#if defined (REGEX_MALLOC) + else + { + /* We must initialize all our variables to NULL, so that + `FREE_VARIABLES' doesn't try to free them. */ + regstart = regend = old_regstart = old_regend = best_regstart + = best_regend = reg_dummy = NULL; + reg_info = reg_info_dummy = (register_info_type *) NULL; + } +#endif /* REGEX_MALLOC */ +#endif /* MATCH_MAY_ALLOCATE */ + + /* The starting position is bogus. */ + if (pos < 0 || pos > size1 + size2) + { + FREE_VARIABLES (); + return -1; + } + + /* Initialize subexpression text positions to -1 to mark ones that no + start_memory/stop_memory has been seen for. Also initialize the + register information struct. */ + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + regstart[mcnt] = regend[mcnt] + = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; + + REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + /* We move `string1' into `string2' if the latter's empty -- but not if + `string1' is null. */ + if (size2 == 0 && string1 != NULL) + { + string2 = string1; + size2 = size1; + string1 = 0; + size1 = 0; + } + end1 = string1 + size1; + end2 = string2 + size2; + + /* Compute where to stop matching, within the two strings. */ + if (stop <= size1) + { + end_match_1 = string1 + stop; + end_match_2 = string2; + } + else + { + end_match_1 = end1; + end_match_2 = string2 + stop - size1; + } + + /* `p' scans through the pattern as `d' scans through the data. + `dend' is the end of the input string that `d' points within. `d' + is advanced into the following input string whenever necessary, but + this happens before fetching; therefore, at the beginning of the + loop, `d' can be pointing at the end of a string, but it cannot + equal `string2'. */ + if (size1 > 0 && pos <= size1) + { + d = string1 + pos; + dend = end_match_1; + } + else + { + d = string2 + pos - size1; + dend = end_match_2; + } + + DEBUG_PRINT1 ("The compiled pattern is: "); + DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend); + DEBUG_PRINT1 ("The string to match is: `"); + DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); + DEBUG_PRINT1 ("'\n"); + + /* This loops over pattern commands. It exits by returning from the + function if the match is complete, or it drops through if the match + fails at this starting point in the input data. */ + for (;;) + { + DEBUG_PRINT2 ("\n0x%x: ", p); + + if (p == pend) + { /* End of pattern means we might have succeeded. */ + DEBUG_PRINT1 ("end of pattern ... "); + + /* If we haven't matched the entire string, and we want the + longest match, try backtracking. */ + if (d != end_match_2) + { + /* 1 if this match ends in the same string (string1 or string2) + as the best previous match. */ + boolean same_str_p = (FIRST_STRING_P (match_end) + == MATCHING_IN_FIRST_STRING); + /* 1 if this match is the best seen so far. */ + boolean best_match_p; + + /* AIX compiler got confused when this was combined + with the previous declaration. */ + if (same_str_p) + best_match_p = d > match_end; + else + best_match_p = !MATCHING_IN_FIRST_STRING; + + DEBUG_PRINT1 ("backtracking.\n"); + + if (!FAIL_STACK_EMPTY ()) + { /* More failure points to try. */ + + /* If exceeds best match so far, save it. */ + if (!best_regs_set || best_match_p) + { + best_regs_set = true; + match_end = d; + + DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); + + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + + /* If no failure points, don't restore garbage. And if + last match is real best match, don't restore second + best one. */ + else if (best_regs_set && !best_match_p) + { + restore_best_regs: + /* Restore best match. It may happen that `dend == + end_match_1' while the restored d is in string2. + For example, the pattern `x.*y.*z' against the + strings `x-' and `y-z-', if the two strings are + not consecutive in memory. */ + DEBUG_PRINT1 ("Restoring best registers.\n"); + + d = match_end; + dend = ((d >= string1 && d <= end1) + ? end_match_1 : end_match_2); + + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } /* d != end_match_2 */ + + DEBUG_PRINT1 ("Accepting match.\n"); + + /* If caller wants register contents data back, do it. */ + if (regs && !bufp->no_sub) + { + /* Have the register data arrays been allocated? */ + if (bufp->regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. We need one + extra element beyond `num_regs' for the `-1' marker + GNU code uses. */ + regs->num_regs = MAX (RE_NREGS, num_regs + 1); + regs->start = TALLOC (regs->num_regs, regoff_t); + regs->end = TALLOC (regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + return -2; + bufp->regs_allocated = REGS_REALLOCATE; + } + else if (bufp->regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (regs->num_regs < num_regs + 1) + { + regs->num_regs = num_regs + 1; + RETALLOC (regs->start, regs->num_regs, regoff_t); + RETALLOC (regs->end, regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + return -2; + } + } + else + { + /* These braces fend off a "empty body in an else-statement" + warning under GCC when assert expands to nothing. */ + assert (bufp->regs_allocated == REGS_FIXED); + } + + /* Convert the pointer data in `regstart' and `regend' to + indices. Register zero has to be set differently, + since we haven't kept track of any info for it. */ + if (regs->num_regs > 0) + { + regs->start[0] = pos; + regs->end[0] = (MATCHING_IN_FIRST_STRING + ? ((regoff_t) (d - string1)) + : ((regoff_t) (d - string2 + size1))); + } + + /* Go through the first `min (num_regs, regs->num_regs)' + registers, since that is all we initialized. */ + for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++) + { + if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) + regs->start[mcnt] = regs->end[mcnt] = -1; + else + { + regs->start[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]); + regs->end[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]); + } + } + + /* If the regs structure we return has more elements than + were in the pattern, set the extra elements to -1. If + we (re)allocated the registers, this is the case, + because we always allocate enough to have at least one + -1 at the end. */ + for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; + } /* regs && !bufp->no_sub */ + + FREE_VARIABLES (); + DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", + nfailure_points_pushed, nfailure_points_popped, + nfailure_points_pushed - nfailure_points_popped); + DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); + + mcnt = d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + + DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); + + return mcnt; + } + + /* Otherwise match next pattern command. */ +#ifdef SWITCH_ENUM_BUG + switch ((int) ((re_opcode_t) *p++)) +#else + switch ((re_opcode_t) *p++) +#endif + { + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case no_op: + DEBUG_PRINT1 ("EXECUTING no_op.\n"); + break; + + + /* Match the next n pattern characters exactly. The following + byte in the pattern defines n, and the n bytes after that + are the characters to match. */ + case exactn: + mcnt = *p++; + DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); + + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (translate) + { + do + { + PREFETCH (); + if (translate[(unsigned char) *d++] != (char) *p++) + goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH (); + if (*d++ != (char) *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED (); + break; + + + /* Match any character except possibly a newline or a null. */ + case anychar: + DEBUG_PRINT1 ("EXECUTING anychar.\n"); + + PREFETCH (); + + if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n') + || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000')) + goto fail; + + SET_REGS_MATCHED (); + DEBUG_PRINT2 (" Matched `%d'.\n", *d); + d++; + break; + + + case charset: + case charset_not: + { + register unsigned char c; + boolean not = (re_opcode_t) *(p - 1) == charset_not; + + DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); + + PREFETCH (); + c = TRANSLATE (*d); /* The character to match. */ + + /* Cast to `unsigned' instead of `unsigned char' in case the + bit list is a full 32 bytes long. */ + if (c < (unsigned) (*p * BYTEWIDTH) + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + + SET_REGS_MATCHED (); + d++; + break; + } + + + /* The beginning of a group is represented by start_memory. + The arguments are the register number in the next byte, and the + number of groups inner to this one in the next. The text + matched within the group is recorded (in the internal + registers data structure) under the register number. */ + case start_memory: + DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]); + + /* Find out if this group can match the empty string. */ + p1 = p; /* To send to group_match_null_string_p. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[*p]) + = group_match_null_string_p (&p1, pend, reg_info); + + /* Save the position in the string where we were the last time + we were at this open-group operator in case the group is + operated upon by a repetition operator, e.g., with `(a*)*b' + against `ab'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regstart[*p]) ? d : regstart[*p] + : regstart[*p]; + DEBUG_PRINT2 (" old_regstart: %d\n", + POINTER_TO_OFFSET (old_regstart[*p])); + + regstart[*p] = d; + DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p])); + + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* This is the new highest active register. */ + highest_active_reg = *p; + + /* If nothing was active before, this is the new lowest active + register. */ + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *p; + + /* Move past the register number and inner group count. */ + p += 2; + just_past_start_mem = p; + break; + + + /* The stop_memory opcode represents the end of a group. Its + arguments are the same as start_memory's: the register + number, and the number of inner groups. */ + case stop_memory: + DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); + + /* We need to save the string position the last time we were at + this close-group operator in case the group is operated + upon by a repetition operator, e.g., with `((a*)*(b*)*)*' + against `aba'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regend[*p]) ? d : regend[*p] + : regend[*p]; + DEBUG_PRINT2 (" old_regend: %d\n", + POINTER_TO_OFFSET (old_regend[*p])); + + regend[*p] = d; + DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p])); + + /* This register isn't active anymore. */ + IS_ACTIVE (reg_info[*p]) = 0; + + /* If this was the only register active, nothing is active + anymore. */ + if (lowest_active_reg == highest_active_reg) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + { /* We must scan for the new highest active register, since + it isn't necessarily one less than now: consider + (a(b)c(d(e)f)g). When group 3 ends, after the f), the + new highest active register is 1. */ + unsigned char r = *p - 1; + while (r > 0 && !IS_ACTIVE (reg_info[r])) + r--; + + /* If we end up at register zero, that means that we saved + the registers as the result of an `on_failure_jump', not + a `start_memory', and we jumped to past the innermost + `stop_memory'. For example, in ((.)*) we save + registers 1 and 2 as a result of the *, but when we pop + back to the second ), we are at the stop_memory 1. + Thus, nothing is active. */ + if (r == 0) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + highest_active_reg = r; + } + + /* If just failed to match something this time around with a + group that's operated on by a repetition operator, try to + force exit from the ``loop'', and restore the register + information for this group that we had before trying this + last match. */ + if ((!MATCHED_SOMETHING (reg_info[*p]) + || just_past_start_mem == p - 1) + && (p + 2) < pend) + { + boolean is_a_jump_n = false; + + p1 = p + 2; + mcnt = 0; + switch ((re_opcode_t) *p1++) + { + case jump_n: + is_a_jump_n = true; + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (is_a_jump_n) + p1 += 2; + break; + + default: + /* do nothing */ ; + } + p1 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump right before the start_memory + corresponding to this stop_memory, exit from the loop + by forcing a failure after pushing on the stack the + on_failure_jump's jump in the pattern, and d. */ + if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump + && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) + { + /* If this group ever matched anything, then restore + what its registers were before trying this last + failed match, e.g., with `(a*)*b' against `ab' for + regstart[1], and, e.g., with `((a*)*(b*)*)*' + against `aba' for regend[3]. + + Also restore the registers for inner groups for, + e.g., `((a*)(b*))*' against `aba' (register 3 would + otherwise get trashed). */ + + if (EVER_MATCHED_SOMETHING (reg_info[*p])) + { + unsigned r; + + EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* Restore this and inner groups' (if any) registers. */ + for (r = *p; r < *p + *(p + 1); r++) + { + regstart[r] = old_regstart[r]; + + /* xx why this test? */ + if ((int) old_regend[r] >= (int) regstart[r]) + regend[r] = old_regend[r]; + } + } + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + PUSH_FAILURE_POINT (p1 + mcnt, d, -2); + + goto fail; + } + } + + /* Move past the register number and the inner group count. */ + p += 2; + break; + + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of as the register number. */ + case duplicate: + { + register const char *d2, *dend2; + int regno = *p++; /* Get which register to match against. */ + DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno); + + /* Can't back reference a group which we've never matched. */ + if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) + goto fail; + + /* Where in input to try to start matching. */ + d2 = regstart[regno]; + + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ + + dend2 = ((FIRST_STRING_P (regstart[regno]) + == FIRST_STRING_P (regend[regno])) + ? regend[regno] : end_match_1); + for (;;) + { + /* If necessary, advance to next segment in register + contents. */ + while (d2 == dend2) + { + if (dend2 == end_match_2) break; + if (dend2 == regend[regno]) break; + + /* End of string1 => advance to string2. */ + d2 = string2; + dend2 = regend[regno]; + } + /* At end of register contents => success */ + if (d2 == dend2) break; + + /* If necessary, advance to next segment in data. */ + PREFETCH (); + + /* How many characters left in this segment to match. */ + mcnt = dend - d; + + /* Want how many consecutive characters we can match in + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) + mcnt = dend2 - d2; + + /* Compare that many; failure if mismatch, else move + past them. */ + if (translate + ? bcmp_translate (d, d2, mcnt, translate) + : bcmp (d, d2, mcnt)) + goto fail; + d += mcnt, d2 += mcnt; + } + } + break; + + + /* begline matches the empty string at the beginning of the string + (unless `not_bol' is set in `bufp'), and, if + `newline_anchor' is set, after newlines. */ + case begline: + DEBUG_PRINT1 ("EXECUTING begline.\n"); + + if (AT_STRINGS_BEG (d)) + { + if (!bufp->not_bol) break; + } + else if (d[-1] == '\n' && bufp->newline_anchor) + { + break; + } + /* In all other cases, we fail. */ + goto fail; + + + /* endline is the dual of begline. */ + case endline: + DEBUG_PRINT1 ("EXECUTING endline.\n"); + + if (AT_STRINGS_END (d)) + { + if (!bufp->not_eol) break; + } + + /* We have to ``prefetch'' the next character. */ + else if ((d == end1 ? *string2 : *d) == '\n' + && bufp->newline_anchor) + { + break; + } + goto fail; + + + /* Match at the very beginning of the data. */ + case begbuf: + DEBUG_PRINT1 ("EXECUTING begbuf.\n"); + if (AT_STRINGS_BEG (d)) + break; + goto fail; + + + /* Match at the very end of the data. */ + case endbuf: + DEBUG_PRINT1 ("EXECUTING endbuf.\n"); + if (AT_STRINGS_END (d)) + break; + goto fail; + + + /* on_failure_keep_string_jump is used to optimize `.*\n'. It + pushes NULL as the value for the string on the stack. Then + `pop_failure_point' will keep the current value for the + string, instead of restoring it. To see why, consider + matching `foo\nbar' against `.*\n'. The .* matches the foo; + then the . fails against the \n. But the next thing we want + to do is match the \n against the \n; if we restored the + string value, we would be back at the foo. + + Because this is used only in specific cases, we don't need to + check all the things that `on_failure_jump' does, to make + sure the right things get saved on the stack. Hence we don't + share its code. The only reason to push anything on the + stack at all is that otherwise we would have to change + `anychar's code to do something besides goto fail in this + case; that seems worse than this. */ + case on_failure_keep_string_jump: + DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); + + PUSH_FAILURE_POINT (p + mcnt, NULL, -2); + break; + + + /* Uses of on_failure_jump: + + Each alternative starts with an on_failure_jump that points + to the beginning of the next alternative. Each alternative + except the last ends with a jump that in effect jumps past + the rest of the alternatives. (They really jump to the + ending jump of the following alternative, because tensioning + these jumps is a hassle.) + + Repeats start with an on_failure_jump that points past both + the repetition text and either the following jump or + pop_failure_jump back to this on_failure_jump. */ + case on_failure_jump: + on_failure: + DEBUG_PRINT1 ("EXECUTING on_failure_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); + + /* If this on_failure_jump comes right before a group (i.e., + the original * applied to a group), save the information + for that group and all inner ones, so that if we fail back + to this point, the group's information will be correct. + For example, in \(a*\)*\1, we need the preceding group, + and in \(\(a*\)b*\)\2, we need the inner group. */ + + /* We can't use `p' to check ahead because we push + a failure point to `p + mcnt' after we do this. */ + p1 = p; + + /* We need to skip no_op's before we look for the + start_memory in case this on_failure_jump is happening as + the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 + against aba. */ + while (p1 < pend && (re_opcode_t) *p1 == no_op) + p1++; + + if (p1 < pend && (re_opcode_t) *p1 == start_memory) + { + /* We have a new highest active register now. This will + get reset at the start_memory we are about to get to, + but we will have saved all the registers relevant to + this repetition op, as described above. */ + highest_active_reg = *(p1 + 1) + *(p1 + 2); + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *(p1 + 1); + } + + DEBUG_PRINT1 (":\n"); + PUSH_FAILURE_POINT (p + mcnt, d, -2); + break; + + + /* A smart repeat ends with `maybe_pop_jump'. + We change it to either `pop_failure_jump' or `jump'. */ + case maybe_pop_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); + { + register unsigned char *p2 = p; + + /* Compare the beginning of the repeat with what in the + pattern follows its end. If we can establish that there + is nothing that they would both match, i.e., that we + would have to backtrack because of (as in, e.g., `a*a') + then we can change to pop_failure_jump, because we'll + never have to backtrack. + + This is not true in the case of alternatives: in + `(a|ab)*' we do need to backtrack to the `ab' alternative + (e.g., if the string was `ab'). But instead of trying to + detect that here, the alternative has put on a dummy + failure point which is what we will end up popping. */ + + /* Skip over open/close-group commands. + If what follows this loop is a ...+ construct, + look at what begins its body, since we will have to + match at least one of that. */ + while (1) + { + if (p2 + 2 < pend + && ((re_opcode_t) *p2 == stop_memory + || (re_opcode_t) *p2 == start_memory)) + p2 += 3; + else if (p2 + 6 < pend + && (re_opcode_t) *p2 == dummy_failure_jump) + p2 += 6; + else + break; + } + + p1 = p + mcnt; + /* p1[0] ... p1[2] are the `on_failure_jump' corresponding + to the `maybe_finalize_jump' of this case. Examine what + follows. */ + + /* If we're at the end of the pattern, we can change. */ + if (p2 == pend) + { + /* Consider what happens when matching ":\(.*\)" + against ":/". I don't really understand this code + yet. */ + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 + (" End of pattern: change to `pop_failure_jump'.\n"); + } + + else if ((re_opcode_t) *p2 == exactn + || (bufp->newline_anchor && (re_opcode_t) *p2 == endline)) + { + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; + + if ((re_opcode_t) p1[3] == exactn && p1[5] != c) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset + || (re_opcode_t) p1[3] == charset_not) + { + int not = (re_opcode_t) p1[3] == charset_not; + + if (c < (unsigned char) (p1[4] * BYTEWIDTH) + && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + /* `not' is equal to 1 if c would match, which means + that we can't change to pop_failure_jump. */ + if (!not) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + else if ((re_opcode_t) *p2 == charset) + { +#ifdef DEBUG + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; +#endif + + if ((re_opcode_t) p1[3] == exactn + && ! ((int) p2[1] * BYTEWIDTH > (int) p1[4] + && (p2[1 + p1[4] / BYTEWIDTH] + & (1 << (p1[4] % BYTEWIDTH))))) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset_not) + { + int idx; + /* We win if the charset_not inside the loop + lists every character listed in the charset after. */ + for (idx = 0; idx < (int) p2[1]; idx++) + if (! (p2[2 + idx] == 0 + || (idx < (int) p1[4] + && ((p2[2 + idx] & ~ p1[5 + idx]) == 0)))) + break; + + if (idx == p2[1]) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + else if ((re_opcode_t) p1[3] == charset) + { + int idx; + /* We win if the charset inside the loop + has no overlap with the one after the loop. */ + for (idx = 0; + idx < (int) p2[1] && idx < (int) p1[4]; + idx++) + if ((p2[2 + idx] & p1[5 + idx]) != 0) + break; + + if (idx == p2[1] || idx == p1[4]) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + } + p -= 2; /* Point at relative address again. */ + if ((re_opcode_t) p[-1] != pop_failure_jump) + { + p[-1] = (unsigned char) jump; + DEBUG_PRINT1 (" Match => jump.\n"); + goto unconditional_jump; + } + /* Note fall through. */ + + + /* The end of a simple repeat has a pop_failure_jump back to + its matching on_failure_jump, where the latter will push a + failure point. The pop_failure_jump takes off failure + points put on by this pop_failure_jump's matching + on_failure_jump; we got through the pattern to here from the + matching on_failure_jump, so didn't fail. */ + case pop_failure_jump: + { + /* We need to pass separate storage for the lowest and + highest registers, even though we don't care about the + actual values. Otherwise, we will restore only one + register from the stack, since lowest will == highest in + `pop_failure_point'. */ + unsigned dummy_low_reg, dummy_high_reg; + unsigned char *pdummy; + const char *sdummy; + + DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); + POP_FAILURE_POINT (sdummy, pdummy, + dummy_low_reg, dummy_high_reg, + reg_dummy, reg_dummy, reg_info_dummy); + } + /* Note fall through. */ + + + /* Unconditionally jump (without popping any failure points). */ + case jump: + unconditional_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */ + DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); + p += mcnt; /* Do the jump. */ + DEBUG_PRINT2 ("(to 0x%x).\n", p); + break; + + + /* We need this opcode so we can detect where alternatives end + in `group_match_null_string_p' et al. */ + case jump_past_alt: + DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); + goto unconditional_jump; + + + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at pop_failure_jump. We will end up at + pop_failure_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for pop_failure_jump to pop. */ + case dummy_failure_jump: + DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); + /* It doesn't matter what we push for the string here. What + the code at `fail' tests is the value for the pattern. */ + PUSH_FAILURE_POINT (0, 0, -2); + goto unconditional_jump; + + + /* At the end of an alternative, we need to push a dummy failure + point in case we are followed by a `pop_failure_jump', because + we don't want the failure point for the alternative to be + popped. For example, matching `(a|ab)*' against `aab' + requires that we match the `ab' alternative. */ + case push_dummy_failure: + DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); + /* See comments just above at `dummy_failure_jump' about the + two zeroes. */ + PUSH_FAILURE_POINT (0, 0, -2); + break; + + /* Have to succeed matching what follows at least n times. + After that, handle like `on_failure_jump'. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); + + assert (mcnt >= 0); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt > 0) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt); + } + else if (mcnt == 0) + { + DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); + p[2] = (unsigned char) no_op; + p[3] = (unsigned char) no_op; + goto on_failure; + } + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); + + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER (p + 2, mcnt); + goto unconditional_jump; + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); + STORE_NUMBER (p1, mcnt); + break; + } + + case wordbound: + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + break; + goto fail; + + case notwordbound: + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + goto fail; + break; + + case wordbeg: + DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); + if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1))) + break; + goto fail; + + case wordend: + DEBUG_PRINT1 ("EXECUTING wordend.\n"); + if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1) + && (!WORDCHAR_P (d) || AT_STRINGS_END (d))) + break; + goto fail; + +#ifdef emacs + case before_dot: + DEBUG_PRINT1 ("EXECUTING before_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) >= point) + goto fail; + break; + + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) != point) + goto fail; + break; + + case after_dot: + DEBUG_PRINT1 ("EXECUTING after_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) <= point) + goto fail; + break; +#if 0 /* not emacs19 */ + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point) + goto fail; + break; +#endif /* not emacs19 */ + + case syntaxspec: + DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchsyntax; + + case wordchar: + DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); + mcnt = (int) Sword; + matchsyntax: + PREFETCH (); + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + + case notsyntaxspec: + DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchnotsyntax; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); + mcnt = (int) Sword; + matchnotsyntax: + PREFETCH (); + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + +#else /* not emacs */ + case wordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n"); + PREFETCH (); + if (!WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n"); + PREFETCH (); + if (WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; +#endif /* not emacs */ + + default: + abort (); + } + continue; /* Successfully executed one pattern command; keep going. */ + + + /* We goto here if a matching operation fails. */ + fail: + if (!FAIL_STACK_EMPTY ()) + { /* A restart point is known. Restore to that state. */ + DEBUG_PRINT1 ("\nFAIL:\n"); + POP_FAILURE_POINT (d, p, + lowest_active_reg, highest_active_reg, + regstart, regend, reg_info); + + /* If this failure point is a dummy, try the next one. */ + if (!p) + goto fail; + + /* If we failed to the end of the pattern, don't examine *p. */ + assert (p <= pend); + if (p < pend) + { + boolean is_a_jump_n = false; + + /* If failed to a backwards jump that's part of a repetition + loop, need to pop this failure point and use the next one. */ + switch ((re_opcode_t) *p) + { + case jump_n: + is_a_jump_n = true; + case maybe_pop_jump: + case pop_failure_jump: + case jump: + p1 = p + 1; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + + if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n) + || (!is_a_jump_n + && (re_opcode_t) *p1 == on_failure_jump)) + goto fail; + break; + default: + /* do nothing */ ; + } + } + + if (d >= string1 && d <= end1) + dend = end_match_1; + } + else + break; /* Matching at this starting point really fails. */ + } /* for (;;) */ + + if (best_regs_set) + goto restore_best_regs; + + FREE_VARIABLES (); + + return -1; /* Failure to match. */ +} /* re_match_2 */ + +/* Subroutine definitions for re_match_2. */ + + +/* We are passed P pointing to a register number after a start_memory. + + Return true if the pattern up to the corresponding stop_memory can + match the empty string, and false otherwise. + + If we find the matching stop_memory, sets P to point to one past its number. + Otherwise, sets P to an undefined byte less than or equal to END. + + We don't handle duplicates properly (yet). */ + +static boolean +group_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + /* Point to after the args to the start_memory. */ + unsigned char *p1 = *p + 2; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and return true or + false, as appropriate, when we get to one that can't, or to the + matching stop_memory. */ + + switch ((re_opcode_t) *p1) + { + /* Could be either a loop or a series of alternatives. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + /* If the next operation is not a jump backwards in the + pattern. */ + + if (mcnt >= 0) + { + /* Go through the on_failure_jumps of the alternatives, + seeing if any of the alternatives cannot match nothing. + The last alternative starts with only a jump, + whereas the rest start with on_failure_jump and end + with a jump, e.g., here is the pattern for `a|b|c': + + /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 + /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 + /exactn/1/c + + So, we have to first go through the first (n-1) + alternatives and then deal with the last one separately. */ + + + /* Deal with the first (n-1) alternatives, which start + with an on_failure_jump (see above) that jumps to right + past a jump_past_alt. */ + + while ((re_opcode_t) p1[mcnt-3] == jump_past_alt) + { + /* `mcnt' holds how many bytes long the alternative + is, including the ending `jump_past_alt' and + its number. */ + + if (!alt_match_null_string_p (p1, p1 + mcnt - 3, + reg_info)) + return false; + + /* Move to right after this alternative, including the + jump_past_alt. */ + p1 += mcnt; + + /* Break if it's the beginning of an n-th alternative + that doesn't begin with an on_failure_jump. */ + if ((re_opcode_t) *p1 != on_failure_jump) + break; + + /* Still have to check that it's not an n-th + alternative that starts with an on_failure_jump. */ + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if ((re_opcode_t) p1[mcnt-3] != jump_past_alt) + { + /* Get to the beginning of the n-th alternative. */ + p1 -= 3; + break; + } + } + + /* Deal with the last alternative: go back and get number + of the `jump_past_alt' just before it. `mcnt' contains + the length of the alternative. */ + EXTRACT_NUMBER (mcnt, p1 - 2); + + if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info)) + return false; + + p1 += mcnt; /* Get past the n-th alternative. */ + } /* if mcnt > 0 */ + break; + + + case stop_memory: + assert (p1[1] == **p); + *p = p1 + 2; + return true; + + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return false; +} /* group_match_null_string_p */ + + +/* Similar to group_match_null_string_p, but doesn't deal with alternatives: + It expects P to be the first byte of a single alternative and END one + byte past the last. The alternative can contain groups. */ + +static boolean +alt_match_null_string_p (p, end, reg_info) + unsigned char *p, *end; + register_info_type *reg_info; +{ + int mcnt; + unsigned char *p1 = p; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and break when we get + to one that can't. */ + + switch ((re_opcode_t) *p1) + { + /* It's a loop. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + break; + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return true; +} /* alt_match_null_string_p */ + + +/* Deals with the ops common to group_match_null_string_p and + alt_match_null_string_p. + + Sets P to one after the op and its arguments, if any. */ + +static boolean +common_op_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + boolean ret; + int reg_no; + unsigned char *p1 = *p; + + switch ((re_opcode_t) *p1++) + { + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbeg: + case wordend: + case wordbound: + case notwordbound: +#ifdef emacs + case before_dot: + case at_dot: + case after_dot: +#endif + break; + + case start_memory: + reg_no = *p1; + assert (reg_no > 0 && reg_no <= MAX_REGNUM); + ret = group_match_null_string_p (&p1, end, reg_info); + + /* Have to set this here in case we're checking a group which + contains a group and a back reference to it. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret; + + if (!ret) + return false; + break; + + /* If this is an optimized succeed_n for zero times, make the jump. */ + case jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (mcnt >= 0) + p1 += mcnt; + else + return false; + break; + + case succeed_n: + /* Get to the number of times to succeed. */ + p1 += 2; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + if (mcnt == 0) + { + p1 -= 4; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + } + else + return false; + break; + + case duplicate: + if (!REG_MATCH_NULL_STRING_P (reg_info[*p1])) + return false; + break; + + case set_number_at: + p1 += 4; + + default: + /* All other opcodes mean we cannot match the empty string. */ + return false; + } + + *p = p1; + return true; +} /* common_op_match_null_string_p */ + + +/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN + bytes; nonzero otherwise. */ + +static int +bcmp_translate (s1, s2, len, translate) + unsigned char *s1, *s2; + register int len; + char *translate; +{ + register unsigned char *p1 = s1, *p2 = s2; + while (len) + { + if (translate[*p1++] != translate[*p2++]) return 1; + len--; + } + return 0; +} + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length SIZE) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. + + We call regex_compile to do the actual compilation. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + int length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* GNU code is written to assume at least RE_NREGS registers will be set + (and at least one extra will be -1). */ + bufp->regs_allocated = REGS_UNALLOCATED; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub. */ + bufp->no_sub = 0; + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = regex_compile (pattern, length, re_syntax_options, bufp); + + return re_error_msg[(int) ret]; +} + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#ifdef _REGEX_RE_COMP + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + + if (!s) + { + if (!re_comp_buf.buffer) + return "No previous regular expression"; + return 0; + } + + if (!re_comp_buf.buffer) + { + re_comp_buf.buffer = (unsigned char *) malloc (200); + if (re_comp_buf.buffer == NULL) + return "Memory exhausted"; + re_comp_buf.allocated = 200; + + re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH); + if (re_comp_buf.fastmap == NULL) + return "Memory exhausted"; + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf); + + /* Yes, we're discarding `const' here. */ + return (char *) re_error_msg[(int) ret]; +} + + +int +re_exec (s) + const char *s; +{ + const int len = strlen (s); + return + 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0); +} +#endif /* _REGEX_RE_COMP */ + +/* POSIX.2 functions. Don't define these for Emacs. */ + +#ifndef emacs + +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' and `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *preg; + const char *pattern; + int cflags; +{ + reg_errcode_t ret; + unsigned syntax + = (cflags & REG_EXTENDED) ? + RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC; + + /* regex_compile will allocate the space for the compiled pattern. */ + preg->buffer = 0; + preg->allocated = 0; + preg->used = 0; + + /* Don't bother to use a fastmap when searching. This simplifies the + REG_NEWLINE case: if we used a fastmap, we'd have to put all the + characters after newlines into the fastmap. This way, we just try + every character. */ + preg->fastmap = 0; + + if (cflags & REG_ICASE) + { + unsigned i; + + preg->translate = (char *) malloc (CHAR_SET_SIZE); + if (preg->translate == NULL) + return (int) REG_ESPACE; + + /* Map uppercase characters to corresponding lowercase ones. */ + for (i = 0; i < CHAR_SET_SIZE; i++) + preg->translate[i] = ISUPPER (i) ? tolower (i) : i; + } + else + preg->translate = NULL; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + + preg->no_sub = !!(cflags & REG_NOSUB); + + /* POSIX says a null character in the pattern terminates it, so we + can use strlen here in compiling the pattern. */ + ret = regex_compile (pattern, strlen (pattern), syntax, preg); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) ret = REG_EPAREN; + + return (int) ret; +} + + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *preg; + const char *string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + int ret; + struct re_registers regs; + regex_t private_preg; + int len = strlen (string); + boolean want_reg_info = !preg->no_sub && nmatch > 0; + + private_preg = *preg; + + private_preg.not_bol = !!(eflags & REG_NOTBOL); + private_preg.not_eol = !!(eflags & REG_NOTEOL); + + /* The user has told us exactly how many registers to return + information about, via `nmatch'. We have to pass that on to the + matching routines. */ + private_preg.regs_allocated = REGS_FIXED; + + if (want_reg_info) + { + regs.num_regs = nmatch; + regs.start = TALLOC (nmatch, regoff_t); + regs.end = TALLOC (nmatch, regoff_t); + if (regs.start == NULL || regs.end == NULL) + return (int) REG_NOMATCH; + } + + /* Perform the searching operation. */ + ret = re_search (&private_preg, string, len, + /* start: */ 0, /* range: */ len, + want_reg_info ? ®s : (struct re_registers *) 0); + + /* Copy the register information to the POSIX structure. */ + if (want_reg_info) + { + if (ret >= 0) + { + unsigned r; + + for (r = 0; r < nmatch; r++) + { + pmatch[r].rm_so = regs.start[r]; + pmatch[r].rm_eo = regs.end[r]; + } + } + + /* If we needed the temporary register info, free the space now. */ + free (regs.start); + free (regs.end); + } + + /* We want zero return to mean success, unlike `re_search'. */ + return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH; +} + + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *preg; + char *errbuf; + size_t errbuf_size; +{ + const char *msg; + size_t msg_size; + + if (errcode < 0 + || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0]))) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = re_error_msg[errcode]; + + /* POSIX doesn't require that we do anything in this case, but why + not be nice. */ + if (! msg) + msg = "Success"; + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (errbuf_size != 0) + { + if (msg_size > errbuf_size) + { + strncpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; + } + else + strcpy (errbuf, msg); + } + + return msg_size; +} + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + if (preg->buffer != NULL) + free (preg->buffer); + preg->buffer = NULL; + + preg->allocated = 0; + preg->used = 0; + + if (preg->fastmap != NULL) + free (preg->fastmap); + preg->fastmap = NULL; + preg->fastmap_accurate = 0; + + if (preg->translate != NULL) + free (preg->translate); + preg->translate = NULL; +} + +#endif /* not emacs */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/lib/regex.h b/lib/regex.h new file mode 100644 index 0000000..55927f6 --- /dev/null +++ b/lib/regex.h @@ -0,0 +1,487 @@ +/* Definitions for data structures and routines for the regular + expression library, version 0.12. + + Copyright (C) 1985, 89, 90, 91, 92, 1993 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef __REGEXP_LIBRARY_H__ +#define __REGEXP_LIBRARY_H__ + +/* POSIX says that must be included (by the caller) before + . */ + +#ifdef VMS +/* VMS doesn't have `size_t' in , even though POSIX says it + should be there. */ +#include +#endif + + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS (1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS + replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +#undef RE_DUP_MAX +#endif +#define RE_DUP_MAX ((1 << 15) - 1) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Not implemented. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +struct re_pattern_buffer +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + char *translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +#define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* To avoid duplicating every routine declaration -- once with a + prototype (if we are ANSI), and once without (if we aren't) -- we + use the following macro to declare argument types. This + unfortunately clutters up the declarations a bit, but I think it's + worth it. */ + +#if __STDC__ + +#define _RE_ARGS(args) args + +#else /* not __STDC__ */ + +#define _RE_ARGS(args) () + +#endif /* not __STDC__ */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern + _RE_ARGS ((const char *pattern, int length, + struct re_pattern_buffer *buffer)); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +#ifdef _REGEX_RE_COMP +/* 4.2 bsd compatibility. */ +extern char *re_comp _RE_ARGS ((const char *)); +extern int re_exec _RE_ARGS ((const char *)); +#endif + +/* POSIX compatibility. */ +extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags)); +extern int regexec + _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch, + regmatch_t pmatch[], int eflags)); +extern size_t regerror + _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf, + size_t errbuf_size)); +extern void regfree _RE_ARGS ((regex_t *preg)); + +#endif /* not __REGEXP_LIBRARY_H__ */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/lib/savedir.c b/lib/savedir.c new file mode 100644 index 0000000..1992cf5 --- /dev/null +++ b/lib/savedir.c @@ -0,0 +1,131 @@ +/* savedir.c -- save the list of files in a directory in a string + Copyright (C) 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie . */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#if HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#ifdef CLOSEDIR_VOID +/* Fake a return value. */ +#define CLOSEDIR(d) (closedir (d), 0) +#else +#define CLOSEDIR(d) closedir (d) +#endif + +#ifdef STDC_HEADERS +#include +#include +#else +char *malloc (); +char *realloc (); +#endif +#ifndef NULL +#define NULL 0 +#endif + +char *stpcpy (); + +/* Return a freshly allocated string containing the filenames + in directory DIR, separated by '\0' characters; + the end is marked by two '\0' characters in a row. + NAME_SIZE is the number of bytes to initially allocate + for the string; it will be enlarged as needed. + Return NULL if DIR cannot be opened or if out of memory. */ + +char * +savedir (dir, name_size) + char *dir; + unsigned name_size; +{ + DIR *dirp; + struct dirent *dp; + char *name_space; + char *namep; + + dirp = opendir (dir); + if (dirp == NULL) + return NULL; + + name_space = (char *) malloc (name_size); + if (name_space == NULL) + { + closedir (dirp); + return NULL; + } + namep = name_space; + + while ((dp = readdir (dirp)) != NULL) + { + /* Skip "." and ".." (some NFS filesystems' directories lack them). */ + if (dp->d_name[0] != '.' + || (dp->d_name[1] != '\0' + && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) + { + unsigned size_needed = (namep - name_space) + NAMLEN (dp) + 2; + + if (size_needed > name_size) + { + char *new_name_space; + + while (size_needed > name_size) + name_size += 1024; + + new_name_space = realloc (name_space, name_size); + if (new_name_space == NULL) + { + closedir (dirp); + return NULL; + } + namep += new_name_space - name_space; + name_space = new_name_space; + } + namep = stpcpy (namep, dp->d_name) + 1; + } + } + *namep = '\0'; + if (CLOSEDIR (dirp)) + { + free (name_space); + return NULL; + } + return name_space; +} diff --git a/lib/stpcpy.c b/lib/stpcpy.c new file mode 100644 index 0000000..5ca0a2e --- /dev/null +++ b/lib/stpcpy.c @@ -0,0 +1,32 @@ +/* stpcpy.c -- copy a string and return pointer to end of new string + Copyright (C) 1989, 1990 Free Software Foundation. + + 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */ + +char * +stpcpy (dest, src) + char *dest; + const char *src; +{ + while ((*dest++ = *src++) != '\0') + /* Do nothing. */ ; + return dest - 1; +} diff --git a/lib/strdup.c b/lib/strdup.c new file mode 100644 index 0000000..1d60f13 --- /dev/null +++ b/lib/strdup.c @@ -0,0 +1,43 @@ +/* strdup.c -- return a newly allocated copy of a string + Copyright (C) 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef STDC_HEADERS +#include +#include +#else +char *malloc (); +char *strcpy (); +#endif + +/* Return a newly allocated copy of STR, + or 0 if out of memory. */ + +char * +strdup (str) + const char *str; +{ + char *newstr; + + newstr = (char *) malloc (strlen (str) + 1); + if (newstr) + strcpy (newstr, str); + return newstr; +} diff --git a/lib/strftime.c b/lib/strftime.c new file mode 100644 index 0000000..484852a --- /dev/null +++ b/lib/strftime.c @@ -0,0 +1,469 @@ +/* strftime - custom formatting of date and/or time + Copyright (C) 1989, 1991, 1992 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Note: this version of strftime lacks locale support, + but it is standalone. + + Performs `%' substitutions similar to those in printf. Except + where noted, substituted fields have a fixed size; numeric fields are + padded if necessary. Padding is with zeros by default; for fields + that display a single number, padding can be changed or inhibited by + following the `%' with one of the modifiers described below. Unknown + field specifiers are copied as normal characters. All other + characters are copied to the output without change. + + Supports a superset of the ANSI C field specifiers. + + Literal character fields: + % % + n newline + t tab + + Numeric modifiers (a nonstandard extension): + - do not pad the field + _ pad the field with spaces + + Time fields: + %H hour (00..23) + %I hour (01..12) + %k hour ( 0..23) + %l hour ( 1..12) + %M minute (00..59) + %p locale's AM or PM + %r time, 12-hour (hh:mm:ss [AP]M) + %R time, 24-hour (hh:mm) + %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension) + %S second (00..61) + %T time, 24-hour (hh:mm:ss) + %X locale's time representation (%H:%M:%S) + %Z time zone (EDT), or nothing if no time zone is determinable + + Date fields: + %a locale's abbreviated weekday name (Sun..Sat) + %A locale's full weekday name, variable length (Sunday..Saturday) + %b locale's abbreviated month name (Jan..Dec) + %B locale's full month name, variable length (January..December) + %c locale's date and time (Sat Nov 04 12:02:33 EST 1989) + %C century (00..99) + %d day of month (01..31) + %e day of month ( 1..31) + %D date (mm/dd/yy) + %h same as %b + %j day of year (001..366) + %m month (01..12) + %U week number of year with Sunday as first day of week (00..53) + %w day of week (0..6) + %W week number of year with Monday as first day of week (00..53) + %x locale's date representation (mm/dd/yy) + %y last two digits of year (00..99) + %Y year (1970...) + + David MacKenzie */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)) +#include +#else +#include +#endif + +#ifndef STDC_HEADERS +time_t mktime (); +#endif + +#if defined(HAVE_TZNAME) +extern char *tzname[2]; +#endif + +/* Types of padding for numbers in date and time. */ +enum padding +{ + none, blank, zero +}; + +static char const* const days[] = +{ + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +static char const * const months[] = +{ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + +/* Add character C to STRING and increment LENGTH, + unless LENGTH would exceed MAX. */ + +#define add_char(c) \ + do \ + { \ + if (length + 1 <= max) \ + string[length++] = (c); \ + } \ + while (0) + +/* Add a 2 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num2 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Add a 3 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num3 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 100; + int mid = (num - top * 100) / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + if (mid == 0 && top == 0 && pad == blank) + add_char (' '); + else if (mid != 0 || top != 0 || pad == zero) + add_char (mid + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Like strncpy except return the number of characters copied. */ + +static int +add_str (to, from, max) + char *to; + const char *from; + int max; +{ + int i; + + for (i = 0; from[i] && i <= max; ++i) + to[i] = from[i]; + return i; +} + +static int +add_num_time_t (string, max, num) + char *string; + int max; + time_t num; +{ + /* This buffer is large enough to hold the character representation + (including the trailing NUL) of any unsigned decimal quantity + whose binary representation fits in 128 bits. */ + char buf[40]; + int length; + + if (sizeof (num) > 16) + abort (); + sprintf (buf, "%lu", (unsigned long) num); + length = add_str (string, buf, max); + return length; +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Sundays. */ + +static int +sun_week (tm) + struct tm *tm; +{ + int dl; + + /* Set `dl' to the day in the year of the last day of the week previous + to the one containing the day specified in TM. If the day specified + in TM is in the first week of the year, `dl' will be negative or 0. + Otherwise, calculate the number of complete weeks before our week + (dl / 7) and add any partial week at the start of the year (dl % 7). */ + dl = tm->tm_yday - tm->tm_wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Mondays. */ + +static int +mon_week (tm) + struct tm *tm; +{ + int dl, wday; + + if (tm->tm_wday == 0) + wday = 6; + else + wday = tm->tm_wday - 1; + dl = tm->tm_yday - wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME) +char * +zone_name (tp) + struct tm *tp; +{ + char *timezone (); + struct timeval tv; + struct timezone tz; + + gettimeofday (&tv, &tz); + return timezone (tz.tz_minuteswest, tp->tm_isdst); +} +#endif + +/* Format the time given in TM according to FORMAT, and put the + results in STRING. + Return the number of characters (not including terminating null) + that were put into STRING, or 0 if the length would have + exceeded MAX. */ + +size_t +strftime (string, max, format, tm) + char *string; + size_t max; + const char *format; + const struct tm *tm; +{ + enum padding pad; /* Type of padding to apply. */ + size_t length = 0; /* Characters put in STRING so far. */ + + for (; *format && length < max; ++format) + { + if (*format != '%') + add_char (*format); + else + { + ++format; + /* Modifiers: */ + if (*format == '-') + { + pad = none; + ++format; + } + else if (*format == '_') + { + pad = blank; + ++format; + } + else + pad = zero; + + switch (*format) + { + /* Literal character fields: */ + case 0: + case '%': + add_char ('%'); + break; + case 'n': + add_char ('\n'); + break; + case 't': + add_char ('\t'); + break; + default: + add_char (*format); + break; + + /* Time fields: */ + case 'H': + case 'k': + length += + add_num2 (&string[length], tm->tm_hour, max - length, + *format == 'H' ? pad : blank); + break; + case 'I': + case 'l': + { + int hour12; + + if (tm->tm_hour == 0) + hour12 = 12; + else if (tm->tm_hour > 12) + hour12 = tm->tm_hour - 12; + else + hour12 = tm->tm_hour; + length += + add_num2 (&string[length], hour12, max - length, + *format == 'I' ? pad : blank); + } + break; + case 'M': + length += + add_num2 (&string[length], tm->tm_min, max - length, pad); + break; + case 'p': + if (tm->tm_hour < 12) + add_char ('A'); + else + add_char ('P'); + add_char ('M'); + break; + case 'r': + length += + strftime (&string[length], max - length, "%I:%M:%S %p", tm); + break; + case 'R': + length += + strftime (&string[length], max - length, "%H:%M", tm); + break; + + case 's': + { + struct tm writable_tm; + writable_tm = *tm; + length += add_num_time_t (&string[length], max - length, + mktime (&writable_tm)); + } + break; + + case 'S': + length += + add_num2 (&string[length], tm->tm_sec, max - length, pad); + break; + case 'T': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'X': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'Z': +#ifdef HAVE_TM_ZONE + length += add_str (&string[length], tm->tm_zone, max - length); +#else +#ifdef HAVE_TZNAME + if (tm->tm_isdst && tzname[1] && *tzname[1]) + length += add_str (&string[length], tzname[1], max - length); + else + length += add_str (&string[length], tzname[0], max - length); +#else + length += add_str (&string[length], zone_name (tm), max - length); +#endif +#endif + break; + + /* Date fields: */ + case 'a': + add_char (days[tm->tm_wday][0]); + add_char (days[tm->tm_wday][1]); + add_char (days[tm->tm_wday][2]); + break; + case 'A': + length += + add_str (&string[length], days[tm->tm_wday], max - length); + break; + case 'b': + case 'h': + add_char (months[tm->tm_mon][0]); + add_char (months[tm->tm_mon][1]); + add_char (months[tm->tm_mon][2]); + break; + case 'B': + length += + add_str (&string[length], months[tm->tm_mon], max - length); + break; + case 'c': + length += + strftime (&string[length], max - length, + "%a %b %d %H:%M:%S %Z %Y", tm); + break; + case 'C': + length += + add_num2 (&string[length], (tm->tm_year + 1900) / 100, + max - length, pad); + break; + case 'd': + length += + add_num2 (&string[length], tm->tm_mday, max - length, pad); + break; + case 'e': + length += + add_num2 (&string[length], tm->tm_mday, max - length, blank); + break; + case 'D': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'j': + length += + add_num3 (&string[length], tm->tm_yday + 1, max - length, pad); + break; + case 'm': + length += + add_num2 (&string[length], tm->tm_mon + 1, max - length, pad); + break; + case 'U': + length += + add_num2 (&string[length], sun_week (tm), max - length, pad); + break; + case 'w': + add_char (tm->tm_wday + '0'); + break; + case 'W': + length += + add_num2 (&string[length], mon_week (tm), max - length, pad); + break; + case 'x': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'y': + length += + add_num2 (&string[length], tm->tm_year % 100, + max - length, pad); + break; + case 'Y': + add_char ((tm->tm_year + 1900) / 1000 + '0'); + length += + add_num3 (&string[length], + (1900 + tm->tm_year) % 1000, max - length, zero); + break; + } + } + } + add_char (0); + return length - 1; +} diff --git a/lib/strspn.c b/lib/strspn.c new file mode 100644 index 0000000..01d8d0f --- /dev/null +++ b/lib/strspn.c @@ -0,0 +1,40 @@ +/* strspn.c -- return numbers of chars at start of string in a class + Copyright (C) 1987, 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if defined(HAVE_STRING_H) +#include +#else +#include +#ifndef strchr +#define strchr index +#endif +#endif + +int +strspn (str, class) + char *str, *class; +{ + register char *st = str; + + while (*st && strchr (class, *st)) + ++st; + return st - str; +} diff --git a/lib/strstr.c b/lib/strstr.c new file mode 100644 index 0000000..16b748b --- /dev/null +++ b/lib/strstr.c @@ -0,0 +1,44 @@ +/* strstr.c -- return the offset of one string within another + Copyright (C) 1989, 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Mike Rendell . */ + +/* Return the starting address of string S2 in S1; + return 0 if it is not found. */ + +char * +strstr (s1, s2) + char *s1; + char *s2; +{ + int i; + char *p1; + char *p2; + char *s = s1; + + for (p2 = s2, i = 0; *s; p2 = s2, i++, s++) + { + for (p1 = s; *p1 && *p2 && *p1 == *p2; p1++, p2++) + ; + if (!*p2) + break; + } + if (!*p2) + return s1 + i; + + return 0; +} diff --git a/lib/strtol.c b/lib/strtol.c new file mode 100644 index 0000000..08ef0a4 --- /dev/null +++ b/lib/strtol.c @@ -0,0 +1,186 @@ +/* Copyright (C) 1991, 1992 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#if HAVE_LIMITS_H +#include +#endif + +#ifndef ULONG_MAX +#define ULONG_MAX ((unsigned long) ~(unsigned long) 0) +#endif + +#ifndef LONG_MAX +#define LONG_MAX (~(1 << (sizeof (long) * 8 - 1))) +#endif + +#ifndef LONG_MIN +#define LONG_MIN (-LONG_MAX - 1) +#endif + +#if STDC_HEADERS +#include +#include +#else +#define NULL 0 +extern int errno; +#endif + +#ifndef UNSIGNED +#define UNSIGNED 0 +#endif + +/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. + If BASE is 0 the base is determined by the presence of a leading + zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. + If BASE is < 2 or > 36, it is reset to 10. + If ENDPTR is not NULL, a pointer to the character after the last + one converted is stored in *ENDPTR. */ +#if UNSIGNED +unsigned long int +#define strtol strtoul +#else +long int +#endif +strtol (nptr, endptr, base) + const char *nptr; + char **endptr; + int base; +{ + int negative; + register unsigned long int cutoff; + register unsigned int cutlim; + register unsigned long int i; + register const char *s; + register unsigned char c; + const char *save; + int overflow; + + if (base < 0 || base == 1 || base > 36) + base = 10; + + s = nptr; + + /* Skip white space. */ + while (isspace (*s)) + ++s; + if (*s == '\0') + goto noconv; + + /* Check for a sign. */ + if (*s == '-') + { + negative = 1; + ++s; + } + else if (*s == '+') + { + negative = 0; + ++s; + } + else + negative = 0; + + if (base == 16 && s[0] == '0' && toupper (s[1]) == 'X') + s += 2; + + /* If BASE is zero, figure it out ourselves. */ + if (base == 0) + { + if (*s == '0') + { + if (toupper (s[1]) == 'X') + { + s += 2; + base = 16; + } + else + base = 8; + } + else + base = 10; + } + + /* Save the pointer so we can check later if anything happened. */ + save = s; + + cutoff = ULONG_MAX / (unsigned long int) base; + cutlim = ULONG_MAX % (unsigned long int) base; + + overflow = 0; + i = 0; + for (c = *s; c != '\0'; c = *++s) + { + if (isdigit (c)) + c -= '0'; + else if (isalpha (c)) + c = toupper (c) - 'A' + 10; + else + break; + if (c >= base) + break; + /* Check for overflow. */ + if (i > cutoff || (i == cutoff && c > cutlim)) + overflow = 1; + else + { + i *= (unsigned long int) base; + i += c; + } + } + + /* Check if anything actually happened. */ + if (s == save) + goto noconv; + + /* Store in ENDPTR the address of one character + past the last character we converted. */ + if (endptr != NULL) + *endptr = (char *) s; + +#if !UNSIGNED + /* Check for a value that is within the range of + `unsigned long int', but outside the range of `long int'. */ + if (i > (negative ? + -(unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX)) + overflow = 1; +#endif + + if (overflow) + { + errno = ERANGE; +#if UNSIGNED + return ULONG_MAX; +#else + return negative ? LONG_MIN : LONG_MAX; +#endif + } + + /* Return the result of the appropriate sign. */ + return (negative ? -i : i); + +noconv:; + /* There was no number to convert. */ + if (endptr != NULL) + *endptr = (char *) nptr; + return 0L; +} diff --git a/lib/wait.h b/lib/wait.h new file mode 100644 index 0000000..b665288 --- /dev/null +++ b/lib/wait.h @@ -0,0 +1,41 @@ +/* wait.h -- POSIX macros for evaluating exit statuses + Copyright (C) 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include /* For pid_t. */ +#if HAVE_SYS_WAIT_H +#include +#endif + +#ifndef WIFSTOPPED +#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f) +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0) +#endif +#ifndef WIFEXITED +#define WIFEXITED(w) (((w) & 0xff) == 0) +#endif + +#ifndef WSTOPSIG +#define WSTOPSIG(w) (((w) >> 8) & 0xff) +#endif +#ifndef WTERMSIG +#define WTERMSIG(w) ((w) & 0x7f) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(w) (((w) >> 8) & 0xff) +#endif diff --git a/lib/xgetcwd.c b/lib/xgetcwd.c new file mode 100644 index 0000000..1c1a7bd --- /dev/null +++ b/lib/xgetcwd.c @@ -0,0 +1,78 @@ +/* xgetcwd.c -- return current directory with unlimited length + Copyright (C) 1992 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by David MacKenzie . */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#ifndef errno +extern int errno; +#endif +#include +#include "pathmax.h" + +#ifndef HAVE_GETCWD +char *getwd (); +#define getcwd(buf, max) getwd (buf) +#else +char *getcwd (); +#endif + +/* Amount to increase buffer size by in each try. */ +#define PATH_INCR 32 + +char *xmalloc (); +char *xrealloc (); +void free (); + +/* Return the current directory, newly allocated, arbitrarily long. + Return NULL and set errno on error. */ + +char * +xgetcwd () +{ + char *cwd; + char *ret; + unsigned path_max; + + errno = 0; + path_max = (unsigned) PATH_MAX; + path_max += 2; /* The getcwd docs say to do this. */ + + cwd = xmalloc (path_max); + + errno = 0; + while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) + { + path_max += PATH_INCR; + cwd = xrealloc (cwd, path_max); + errno = 0; + } + + if (ret == NULL) + { + int save_errno = errno; + free (cwd); + errno = save_errno; + return NULL; + } + return cwd; +} diff --git a/lib/xmalloc.c b/lib/xmalloc.c new file mode 100644 index 0000000..9f70111 --- /dev/null +++ b/lib/xmalloc.c @@ -0,0 +1,95 @@ +/* xmalloc.c -- malloc with out of memory checking + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if __STDC__ +#define VOID void +#else +#define VOID char +#endif + +#include + +#if STDC_HEADERS +#include +#else +VOID *malloc (); +VOID *realloc (); +void free (); +#endif + +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif + +/* Exit value when the requested amount of memory is not available. + The caller may set it to some other value. */ +int xmalloc_exit_failure = EXIT_FAILURE; + +#if __STDC__ && (HAVE_VPRINTF || HAVE_DOPRNT) +void error (int, int, const char *, ...); +#else +void error (); +#endif + +static VOID * +fixup_null_alloc (n) + size_t n; +{ + VOID *p; + + p = 0; + if (n == 0) + p = malloc ((size_t) 1); + if (p == 0) + error (xmalloc_exit_failure, 0, "memory exhausted"); + return p; +} + +/* Allocate N bytes of memory dynamically, with error checking. */ + +VOID * +xmalloc (n) + size_t n; +{ + VOID *p; + + p = malloc (n); + if (p == 0) + p = fixup_null_alloc (n); + return p; +} + +/* Change the size of an allocated block of memory P to N bytes, + with error checking. + If P is NULL, run xmalloc. */ + +VOID * +xrealloc (p, n) + VOID *p; + size_t n; +{ + if (p == 0) + return xmalloc (n); + p = realloc (p, n); + if (p == 0) + p = fixup_null_alloc (n); + return p; +} diff --git a/lib/xstrdup.c b/lib/xstrdup.c new file mode 100644 index 0000000..27cd0c6 --- /dev/null +++ b/lib/xstrdup.c @@ -0,0 +1,36 @@ +/* xstrdup.c -- copy a string with out of memory checking + Copyright (C) 1990 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include +#else +#include +#endif +char *xmalloc (); + +/* Return a newly allocated copy of STRING. */ + +char * +xstrdup (string) + char *string; +{ + return strcpy (xmalloc (strlen (string) + 1), string); +} diff --git a/locate/Makefile.am b/locate/Makefile.am new file mode 100644 index 0000000..93bd908 --- /dev/null +++ b/locate/Makefile.am @@ -0,0 +1,42 @@ +# The default database to build and search. +LOCATE_DB = $(localstatedir)/locatedb + +PROGRAMS = locate +LIBPROGRAMS = frcode code bigram +SCRIPTS = updatedb +MANS = locate.1 updatedb.1 locatedb.5 +CONFIG_HEADER = ../config.h + +DIST_OTHER = locatedb.h updatedb.sh +CLEANFILES = updatedb + +INCLUDES = -I.. -I$(top_srcdir)/lib -DLOCATE_DB=\"$(LOCATE_DB)\" + +LDADD = ../find/version.o ../lib/libfind.a + +$(PROGRAMS) $(LIBPROGRAMS): ../find/version.o ../lib/libfind.a + +updatedb: updatedb.sh + rm -f $@ + version=`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q $(top_srcdir)/find/version.c`; \ + find=`echo find|sed '$(transform)'`; \ + frcode=`echo frcode|sed '$(transform)'`; \ + bigram=`echo bigram|sed '$(transform)'`; \ + code=`echo code|sed '$(transform)'`; \ + sed \ + -e "s,@bindir@,$(bindir)," \ + -e "s,@libexecdir@,$(libexecdir)," \ + -e "s,@LOCATE_DB@,$(LOCATE_DB)," \ + -e "s,@version@,$$version," \ + -e "s,@find@,$$find," \ + -e "s,@frcode@,$$frcode," \ + -e "s,@bigram@,$$bigram," \ + -e "s,@code@,$$code," \ + $(srcdir)/updatedb.sh > $@ + chmod +x $@ + +install:: + $(top_srcdir)/mkinstalldirs $(localstatedir) + +frcode.o code.o locate.o: locatedb.h +locate.o: ../lib/fnmatch.h ../lib/getopt.h diff --git a/locate/Makefile.in b/locate/Makefile.in new file mode 100644 index 0000000..5e1d6d4 --- /dev/null +++ b/locate/Makefile.in @@ -0,0 +1,222 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +CC = @CC@ +LEX = @LEX@ +YACC = @YACC@ +ANSI2KNR = ./ansi2knr + +DEFS = @DEFS@ +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +locate_SOURCES = locate.c +locate_OBJECTS = locate.o +frcode_SOURCES = frcode.c +frcode_OBJECTS = frcode.o +code_SOURCES = code.c +code_OBJECTS = code.o +bigram_SOURCES = bigram.c +bigram_OBJECTS = bigram.o +NROFF = nroff + +SOURCES = locate.c frcode.c code.c bigram.c +DIST_CONF = Makefile.am Makefile.in +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +# The default database to build and search. +LOCATE_DB = $(localstatedir)/locatedb + +PROGRAMS = locate +LIBPROGRAMS = frcode code bigram +SCRIPTS = updatedb +MANS = locate.1 updatedb.1 locatedb.5 +CONFIG_HEADER = ../config.h + +DIST_OTHER = locatedb.h updatedb.sh +CLEANFILES = updatedb + +INCLUDES = -I.. -I$(top_srcdir)/lib -DLOCATE_DB=\"$(LOCATE_DB)\" + +LDADD = ../find/version.o ../lib/libfind.a + +all:: ${ALL} + +.c.o: + $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $< + +$(locate_OBJECTS): ../config.h +$(frcode_OBJECTS): ../config.h +$(code_OBJECTS): ../config.h +$(bigram_OBJECTS): ../config.h +install:: install-programs + +install-programs: $(PROGRAMS) $(SCRIPTS) + $(top_srcdir)/mkinstalldirs $(bindir) + for p in $(PROGRAMS) $(SCRIPTS); do \ + $(INSTALL_PROGRAM) $$p $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +uninstall:: uninstall-programs + +uninstall-programs: + for p in $(PROGRAMS) $(SCRIPTS); do \ + rm -f $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +install:: install-libprograms + +install-libprograms: $(LIBPROGRAMS) $(LIBSCRIPTS) + $(top_srcdir)/mkinstalldirs $(libexecdir) + for p in $(LIBPROGRAMS) $(LIBSCRIPTS); do \ + $(INSTALL_PROGRAM) $$p $(libexecdir)/`echo $$p|sed '$(transform)'`; \ + done + +uninstall:: uninstall-libprograms + +uninstall-libprograms: + for p in $(LIBPROGRAMS) $(LIBSCRIPTS); do \ + rm -f $(libexecdir)/`echo $$p|sed '$(transform)'`; \ + done + +locate: $(locate_OBJECTS) + $(CC) -o $@ $(locate_OBJECTS) $(LDADD) $(LDFLAGS) $(LIBS) + +frcode: $(frcode_OBJECTS) + $(CC) -o $@ $(frcode_OBJECTS) $(LDADD) $(LDFLAGS) $(LIBS) + +code: $(code_OBJECTS) + $(CC) -o $@ $(code_OBJECTS) $(LDADD) $(LDFLAGS) $(LIBS) + +bigram: $(bigram_OBJECTS) + $(CC) -o $@ $(bigram_OBJECTS) $(LDADD) $(LDFLAGS) $(LIBS) + +install:: install-man + +install-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\.\([0-9][a-z]*\)$$%\1%'`; \ + inst=`basename $$man $$sect|sed '$(transform)'`$$sect; \ + mdir=$(mandir)/man$$sect; \ + $(top_srcdir)/mkinstalldirs $$mdir; \ + echo installing $$man as $$mdir/$$inst; \ + $(INSTALL_DATA) $(srcdir)/$$man $$mdir/$$inst; \ + cdir=$(mandir)/cat$$sect; \ + if test -d $$cdir; then \ + echo formatting $$man as $$cdir/$$inst; \ + $(NROFF) -man $(srcdir)/$$man > $$cdir/$$inst; \ + fi; \ + done + +uninstall:: uninstall-man + +uninstall-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\(\.[0-9][a-z]*\)$$%\1%'; \ + inst=`basename $$man $sect|sed '$(transform)'`.$$sect; \ + mdir=$(mandir)/man$$sect; \ + cdir=$(mandir)/cat$$sect; \ + rm -f $$mdir/$$inst $$cdir/$$inst; \ + done + +mostlyclean: + rm -f *.o core + +clean: mostlyclean + rm -f $(PROGRAMS) $(LIBPROGRAMS) $(LIBFILES) $(TEXFILES) $(CLEANFILES) + +distclean: clean + rm -f Makefile *.tab.c $(DISTCLEANFILES) + rm -f config.cache config.log config.status ${CONFIG_HEADER} stamp-h + +realclean: distclean + rm -f TAGS $(INFOS) + +dist: $(DIST_FILES) $(DIST_DIRS) + -mkdir ../`cat ../distname`/$(subdir) + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file;}; \ + done + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +$(PROGRAMS) $(LIBPROGRAMS): ../find/version.o ../lib/libfind.a + +updatedb: updatedb.sh + rm -f $@ + version=`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q $(top_srcdir)/find/version.c`; \ + find=`echo find|sed '$(transform)'`; \ + frcode=`echo frcode|sed '$(transform)'`; \ + bigram=`echo bigram|sed '$(transform)'`; \ + code=`echo code|sed '$(transform)'`; \ + sed \ + -e "s,@bindir@,$(bindir)," \ + -e "s,@libexecdir@,$(libexecdir)," \ + -e "s,@LOCATE_DB@,$(LOCATE_DB)," \ + -e "s,@version@,$$version," \ + -e "s,@find@,$$find," \ + -e "s,@frcode@,$$frcode," \ + -e "s,@bigram@,$$bigram," \ + -e "s,@code@,$$code," \ + $(srcdir)/updatedb.sh > $@ + chmod +x $@ + +install:: + $(top_srcdir)/mkinstalldirs $(localstatedir) + +frcode.o code.o locate.o: locatedb.h +locate.o: ../lib/fnmatch.h ../lib/getopt.h diff --git a/locate/bigram.c b/locate/bigram.c new file mode 100644 index 0000000..ed04a81 --- /dev/null +++ b/locate/bigram.c @@ -0,0 +1,115 @@ +/* bigram -- list bigrams for locate + Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Usage: bigram < text > bigrams + Use `code' to encode a file using this output. + + Read a file from stdin and write out the bigrams (pairs of + adjacent characters), one bigram per line, to stdout. To reduce + needless duplication in the output, it starts finding the + bigrams on each input line at the character where that line + first differs from the previous line (i.e., in the ASCII + remainder). Therefore, the input should be sorted in order to + get the least redundant output. + + Written by James A. Woods . + Modified by David MacKenzie . */ + +#include +#include + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#endif + +#ifdef STDC_HEADERS +#include +#endif +#include + +char *xmalloc (); + +/* The name this program was run with. */ +char *program_name; + +/* Return the length of the longest common prefix of strings S1 and S2. */ + +static int +prefix_length (s1, s2) + char *s1, *s2; +{ + register char *start; + + for (start = s1; *s1 == *s2 && *s1 != '\0'; s1++, s2++) + ; + return s1 - start; +} + +void +main (argc, argv) + int argc; + char **argv; +{ + char *path; /* The current input entry. */ + char *oldpath; /* The previous input entry. */ + size_t pathsize, oldpathsize; /* Amounts allocated for them. */ + int line_len; /* Length of input line. */ + + program_name = argv[0]; + + pathsize = oldpathsize = 1026; /* Increased as necessary by getstr. */ + path = xmalloc (pathsize); + oldpath = xmalloc (oldpathsize); + + /* Set to anything not starting with a slash, to force the first + prefix count to 0. */ + strcpy (oldpath, " "); + + while ((line_len = getstr (&path, &pathsize, stdin, '\n', 0)) > 0) + { + register int count; /* The prefix length. */ + register int j; /* Index into input line. */ + + path[line_len - 1] = '\0'; /* Remove the newline. */ + + /* Output bigrams in the remainder only. */ + count = prefix_length (oldpath, path); + for (j = count; path[j] != '\0' && path[j + 1] != '\0'; j += 2) + { + putchar (path[j]); + putchar (path[j + 1]); + putchar ('\n'); + } + + { + /* Swap path and oldpath and their sizes. */ + char *tmppath = oldpath; + size_t tmppathsize = oldpathsize; + oldpath = path; + oldpathsize = pathsize; + path = tmppath; + pathsize = tmppathsize; + } + } + + free (path); + free (oldpath); + + exit (0); +} diff --git a/locate/code.c b/locate/code.c new file mode 100644 index 0000000..d79e72c --- /dev/null +++ b/locate/code.c @@ -0,0 +1,214 @@ +/* code -- bigram- and front-encode filenames for locate + Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Compress a sorted list. + Works with `find' to encode a filename database to save space + and search time. + + Usage: + + bigram < file_list > bigrams + process-bigrams > most_common_bigrams + code most_common_bigrams < file_list > squeezed_list + + Uses `front compression' (see ";login:", March 1983, p. 8). + The output begins with the 128 most common bigrams. + After that, the output format is, for each line, + an offset (from the previous line) differential count byte + followed by a (partially bigram-encoded) ASCII remainder. + The output lines have no terminating byte; the start of the next line + is indicated by its first byte having a value <= 30. + + The encoding of the output bytes is: + + 0-28 likeliest differential counts + offset (14) to make nonnegative + 30 escape code for out-of-range count to follow in next halfword + 128-255 bigram codes (the 128 most common, as determined by `updatedb') + 32-127 single character (printable) ASCII remainder + + Written by James A. Woods . + Modified by David MacKenzie . */ + +#include +#include +#include + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#endif + +#ifdef STDC_HEADERS +#include +#endif + +#include "locatedb.h" + +char *xmalloc (); + +/* The name this program was run with. */ +char *program_name; + +/* The 128 most common bigrams in the file list, padded with NULs + if there are fewer. */ +static char bigrams[257] = {0}; + +/* Return the offset of PATTERN in STRING, or -1 if not found. */ + +static int +strindex (string, pattern) + char *string, *pattern; +{ + register char *s; + + for (s = string; *s != '\0'; s++) + /* Fast first char check. */ + if (*s == *pattern) + { + register char *p2 = pattern + 1, *s2 = s + 1; + while (*p2 != '\0' && *p2 == *s2) + p2++, s2++; + if (*p2 == '\0') + return s2 - strlen (pattern) - string; + } + return -1; +} + +/* Return the length of the longest common prefix of strings S1 and S2. */ + +static int +prefix_length (s1, s2) + char *s1, *s2; +{ + register char *start; + + for (start = s1; *s1 == *s2 && *s1 != '\0'; s1++, s2++) + ; + return s1 - start; +} + +void +main (argc, argv) + int argc; + char **argv; +{ + char *path; /* The current input entry. */ + char *oldpath; /* The previous input entry. */ + size_t pathsize, oldpathsize; /* Amounts allocated for them. */ + int count, oldcount, diffcount; /* Their prefix lengths & the difference. */ + char bigram[3]; /* Bigram to search for in table. */ + int code; /* Index of `bigram' in bigrams table. */ + FILE *fp; /* Most common bigrams file. */ + int line_len; /* Length of input line. */ + + program_name = argv[0]; + + bigram[2] = '\0'; + + if (argc != 2) + { + fprintf (stderr, "Usage: %s most_common_bigrams < list > coded_list\n", + argv[0]); + exit (2); + } + + fp = fopen (argv[1], "r"); + if (fp == NULL) + { + fprintf (stderr, "%s: ", argv[0]); + perror (argv[1]); + exit (1); + } + + pathsize = oldpathsize = 1026; /* Increased as necessary by getstr. */ + path = xmalloc (pathsize); + oldpath = xmalloc (oldpathsize); + + /* Set to anything not starting with a slash, to force the first + prefix count to 0. */ + strcpy (oldpath, " "); + oldcount = 0; + + /* Copy the list of most common bigrams to the output, + padding with NULs if there are <128 of them. */ + fgets (bigrams, 257, fp); + fwrite (bigrams, 1, 256, stdout); + fclose (fp); + + while ((line_len = getstr (&path, &pathsize, stdin, '\n', 0)) > 0) + { + char *pp; + + path[line_len - 1] = '\0'; /* Remove newline. */ + + /* Squelch unprintable chars in path so as not to botch decoding. */ + for (pp = path; *pp != '\0'; pp++) + { + *pp &= 0177; + if (*pp < 040 || *pp == 0177) + *pp = '?'; + } + + count = prefix_length (oldpath, path); + diffcount = count - oldcount; + oldcount = count; + /* If the difference is small, it fits in one byte; + otherwise, two bytes plus a marker noting that fact. */ + if (diffcount < -LOCATEDB_OLD_OFFSET || diffcount > LOCATEDB_OLD_OFFSET) + { + putc (LOCATEDB_OLD_ESCAPE, stdout); + putw (diffcount + LOCATEDB_OLD_OFFSET, stdout); + } + else + putc (diffcount + LOCATEDB_OLD_OFFSET, stdout); + + /* Look for bigrams in the remainder of the path. */ + for (pp = path + count; *pp != '\0'; pp += 2) + { + if (pp[1] == '\0') + { + /* No bigram is possible; only one char is left. */ + putchar (*pp); + break; + } + bigram[0] = *pp; + bigram[1] = pp[1]; + /* Linear search for specific bigram in string table. */ + code = strindex (bigrams, bigram); + if (code % 2 == 0) + putchar ((code / 2) | 0200); /* It's a common bigram. */ + else + fputs (bigram, stdout); /* Write the text as printable ASCII. */ + } + + { + /* Swap path and oldpath and their sizes. */ + char *tmppath = oldpath; + size_t tmppathsize = oldpathsize; + oldpath = path; + oldpathsize = pathsize; + path = tmppath; + pathsize = tmppathsize; + } + } + + free (path); + free (oldpath); + + exit (0); +} diff --git a/locate/frcode.c b/locate/frcode.c new file mode 100644 index 0000000..ac250c3 --- /dev/null +++ b/locate/frcode.c @@ -0,0 +1,170 @@ +/* frcode -- front-compress a sorted list + Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Usage: frcode < sorted-list > compressed-list + + Uses front compression (also known as incremental encoding); + see ";login:", March 1983, p. 8. + + The input is a sorted list of NUL-terminated strings. + (FIXME newline-terminated, until we figure out how to sort + NUL-terminated strings.) + + The output entries are in the same order as the input; + each entry consists of an offset-differential count byte + (the additional number of characters of prefix of the preceding entry to + use beyond the number that the preceding entry is using of its predecessor), + followed by a null-terminated ASCII remainder. + + If the offset-differential count is larger than can be stored + in a byte (+/-127), the byte has the value LOCATEDB_ESCAPE + and the count follows in a 2-byte word, with the high byte first + (network byte order). + + Example: + + Input, with NULs changed to newlines: + /usr/src + /usr/src/cmd/aardvark.c + /usr/src/cmd/armadillo.c + /usr/tmp/zoo + + Length of the longest prefix of the preceding entry to share: + 0 /usr/src + 8 /cmd/aardvark.c + 14 rmadillo.c + 5 tmp/zoo + + Output, with NULs changed to newlines and count bytes made printable: + 0 LOCATE02 + 0 /usr/src + 8 /cmd/aardvark.c + 6 rmadillo.c + -9 tmp/zoo + + (6 = 14 - 8, and -9 = 5 - 14) + + Written by James A. Woods . + Modified by David MacKenzie . */ + +#include +#include +#include + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#endif + +#ifdef STDC_HEADERS +#include +#endif + +#include "locatedb.h" + +char *xmalloc (); + +/* The name this program was run with. */ +char *program_name; + +/* Write out a 16-bit int, high byte first (network byte order). */ + +static void +put_short (c, fp) + int c; + FILE *fp; +{ + putc (c >> 8, fp); + putc (c, fp); +} + +/* Return the length of the longest common prefix of strings S1 and S2. */ + +static int +prefix_length (s1, s2) + char *s1, *s2; +{ + register char *start; + + for (start = s1; *s1 == *s2 && *s1 != '\0'; s1++, s2++) + ; + return s1 - start; +} + +void +main (argc, argv) + int argc; + char **argv; +{ + char *path; /* The current input entry. */ + char *oldpath; /* The previous input entry. */ + size_t pathsize, oldpathsize; /* Amounts allocated for them. */ + int count, oldcount, diffcount; /* Their prefix lengths & the difference. */ + int line_len; /* Length of input line. */ + + program_name = argv[0]; + + pathsize = oldpathsize = 1026; /* Increased as necessary by getstr. */ + path = xmalloc (pathsize); + oldpath = xmalloc (oldpathsize); + + /* Set to anything not starting with a slash, to force the first + prefix count to 0. */ + strcpy (oldpath, " "); + oldcount = 0; + + fwrite (LOCATEDB_MAGIC, sizeof (LOCATEDB_MAGIC), 1, stdout); + + /* FIXME temporary: change the \n to \0 when we figure out how to sort + null-terminated strings. */ + while ((line_len = getstr (&path, &pathsize, stdin, '\n', 0)) > 0) + { + path[line_len - 1] = '\0'; /* FIXME temporary: nuke the newline. */ + + count = prefix_length (oldpath, path); + diffcount = count - oldcount; + oldcount = count; + /* If the difference is small, it fits in one byte; + otherwise, two bytes plus a marker noting that fact. */ + if (diffcount < -127 || diffcount > 127) + { + putc (LOCATEDB_ESCAPE, stdout); + put_short (diffcount, stdout); + } + else + putc (diffcount, stdout); + + fputs (path + count, stdout); + putc ('\0', stdout); + + { + /* Swap path and oldpath and their sizes. */ + char *tmppath = oldpath; + size_t tmppathsize = oldpathsize; + oldpath = path; + oldpathsize = pathsize; + path = tmppath; + pathsize = tmppathsize; + } + } + + free (path); + free (oldpath); + + exit (0); +} diff --git a/locate/locate.1 b/locate/locate.1 new file mode 100644 index 0000000..dde9cc6 --- /dev/null +++ b/locate/locate.1 @@ -0,0 +1,78 @@ +.TH LOCATE 1L \" -*- nroff -*- +.SH NAME +locate \- list files in databases that match a pattern +.SH SYNOPSIS +.B locate +[\-d path] [\-\-database=path] [\-\-version] [\-\-help] pattern... +.SH DESCRIPTION +This manual page +documents the GNU version of +.BR locate . +For each given pattern, +.B locate +searches one or more databases of file names and displays the +file names that contain the pattern. Patterns can contain shell-style +metacharacters: `*', `?', and `[]'. The metacharacters do not treat +`/' or `.' specially. Therefore, a pattern `foo*bar' can match a +file name that contains `foo3/bar', and a pattern `*duck*' can match a +file name that contains `lake/.ducky'. Patterns that contain +metacharacters should be quoted to protect them from expansion by the +shell. +.P +If a pattern is a plain string \(em it contains no metacharacters \(em +.B locate +displays all file names in the database that contain that string +anywhere. If a pattern does contain metacharacters, +.B locate +only displays file names that match the pattern exactly. As a result, +patterns that contain metacharacters should usually begin with a `*', +and will most often end with one as well. The exceptions are patterns +that are intended to explicitly match the beginning or end of a file +name. +.P +The file name databases contain lists of files that were on the system +when the databases were last updated. The system administrator can +choose the file name of the default database, the frequency with which +the databases are updated, and the directories for which they contain +entries; see \fBupdatedb\fP(1L). +.SH OPTIONS +.TP +.I "\-d \fIpath\fP, \-\-database=\fIpath\fP" +Instead of searching the default file name database, search the file +name databases in \fIpath\fP, which is a colon-separated list of +database file names. You can also use the environment variable +.B LOCATE_PATH +to set the list of database files to search. +The option overrides the environment variable if both are used. +.P +The file name database format changed starting with GNU +.B find +and +.B locate +version 4.0 to allow machines with diffent byte orderings to share +the databases. This version of +.B locate +can automatically recognize and read databases produced for older +versions of GNU +.B locate +or Unix versions of +.B locate +or +.BR find . +.TP +.I "\-\-help" +Print a summary of the options to +.B locate +and exit. +.TP +.I "\-\-version" +Print the version number of +.B locate +and exit. +.SH ENVIRONMENT +.TP +.B LOCATE_PATH +Colon-separated list of databases to search. +.SH "SEE ALSO" +\fBfind\fP(1L), \fBlocatedb\fP(5L), \fBupdatedb\fP(1L), \fBxargs\fP(1L) +\fBFinding Files\fP (on-line in Info, or printed) diff --git a/locate/locate.c b/locate/locate.c new file mode 100644 index 0000000..29be022 --- /dev/null +++ b/locate/locate.c @@ -0,0 +1,402 @@ +/* locate -- search databases for filenames that match patterns + Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Usage: locate [options] pattern... + + Scan a pathname list for the full pathname of a file, given only + a piece of the name (possibly containing shell globbing metacharacters). + The list has been processed with front-compression, which reduces + the list size by a factor of 4-5. + Recognizes two database formats, old and new. The old format is + bigram coded, which reduces space by a further 20-25% and uses the + following encoding of the database bytes: + + 0-28 likeliest differential counts + offset (14) to make nonnegative + 30 escape code for out-of-range count to follow in next halfword + 128-255 bigram codes (the 128 most common, as determined by `updatedb') + 32-127 single character (printable) ASCII remainder + + Uses a novel two-tiered string search technique: + + First, match a metacharacter-free subpattern and a partial pathname + BACKWARDS to avoid full expansion of the pathname list. + The time savings is 40-50% over forward matching, which cannot efficiently + handle overlapped search patterns and compressed path remainders. + + Then, match the actual shell glob-style regular expression (if in this form) + against the candidate pathnames using the slower shell filename + matching routines. + + Described more fully in Usenix ;login:, Vol 8, No 1, + February/March, 1983, p. 8. + + Written by James A. Woods . + Modified by David MacKenzie . */ + +#include +#include +#include +#include +#include +#include +#include + +#define NDEBUG +#include + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#else +#include +#define strchr index +#endif + +#ifdef STDC_HEADERS +#include +#else +char *getenv (); +#endif + +#ifdef STDC_HEADERS +#include +#include +#else +extern int errno; +#endif + +#include "locatedb.h" + +typedef enum {false, true} boolean; + +/* Warn if a database is older than this. 8 days allows for a weekly + update that takes up to a day to perform. */ +#define WARN_SECONDS (60 * 60 * 24 * 8) + +/* Printable version of WARN_SECONDS. */ +#define WARN_MESSAGE "8 days" + +char *next_element (); +char *xmalloc (); +char *xrealloc (); +void error (); + +/* Read in a 16-bit int, high byte first (network byte order). */ + +static int +get_short (fp) + FILE *fp; +{ + register short x; + + x = fgetc (fp); + return (x << 8) | (fgetc (fp) & 0xff); +} + +/* Return a pointer to the last character in a static copy of the last + glob-free subpattern in NAME, + with '\0' prepended for a fast backwards pre-match. */ + +static char * +last_literal_end (name) + char *name; +{ + static char *globfree = NULL; /* A copy of the subpattern in NAME. */ + static size_t gfalloc = 0; /* Bytes allocated for `globfree'. */ + register char *subp; /* Return value. */ + register char *p; /* Search location in NAME. */ + + /* Find the end of the subpattern. + Skip trailing metacharacters and [] ranges. */ + for (p = name + strlen (name) - 1; p >= name && strchr ("*?]", *p) != NULL; + p--) + { + if (*p == ']') + while (p >= name && *p != '[') + p--; + } + if (p < name) + p = name; + + if (p - name + 3 > gfalloc) + { + gfalloc = p - name + 3 + 64; /* Room to grow. */ + globfree = xrealloc (globfree, gfalloc); + } + subp = globfree; + *subp++ = '\0'; + + /* If the pattern has only metacharacters, make every path match the + subpattern, so it gets checked the slow way. */ + if (p == name && strchr ("?*[]", *p) != NULL) + *subp++ = '/'; + else + { + char *endmark; + /* Find the start of the metacharacter-free subpattern. */ + for (endmark = p; p >= name && strchr ("]*?", *p) == NULL; p--) + ; + /* Copy the subpattern into globfree. */ + for (++p; p <= endmark; ) + *subp++ = *p++; + } + *subp-- = '\0'; /* Null terminate, though it's not needed. */ + + return subp; +} + +/* Print the entries in DBFILE that match shell globbing pattern PATHPART. + Return the number of entries printed. */ + +static int +locate (pathpart, dbfile) + char *pathpart, *dbfile; +{ + /* The pathname database. */ + FILE *fp; + /* An input byte. */ + int c; + /* Number of bytes read from an entry. */ + int nread; + + /* true if PATHPART contains globbing metacharacters. */ + boolean globflag; + /* The end of the last glob-free subpattern in PATHPART. */ + char *patend; + + /* The current input database entry. */ + char *path; + /* Amount allocated for it. */ + size_t pathsize; + + /* The length of the prefix shared with the previous database entry. */ + int count = 0; + /* Where in `path' to stop the backward search for the last character + in the subpattern. Set according to `count'. */ + char *cutoff; + + /* true if we found a fast match (of patend) on the previous path. */ + boolean prev_fast_match = false; + /* The return value. */ + int printed = 0; + + /* true if reading a bigram-encoded database. */ + boolean old_format = false; + /* For the old database format, + the first and second characters of the most common bigrams. */ + char bigram1[128], bigram2[128]; + + /* To check the age of the database. */ + struct stat st; + time_t now; + + if (stat (dbfile, &st) || (fp = fopen (dbfile, "r")) == NULL) + { + error (0, errno, "%s", dbfile); + return 0; + } + time(&now); + if (now - st.st_mtime > WARN_SECONDS) + { + error (0, 0, "warning: database `%s' is more than %s old", + dbfile, WARN_MESSAGE); + } + + pathsize = 1026; /* Increased as necessary by getstr. */ + path = xmalloc (pathsize); + + nread = fread (path, 1, sizeof (LOCATEDB_MAGIC), fp); + if (nread != sizeof (LOCATEDB_MAGIC) + || memcmp (path, LOCATEDB_MAGIC, sizeof (LOCATEDB_MAGIC))) + { + int i; + /* Read the list of the most common bigrams in the database. */ + fseek (fp, 0, 0); + for (i = 0; i < 128; i++) + { + bigram1[i] = getc (fp); + bigram2[i] = getc (fp); + } + old_format = true; + } + + globflag = strchr (pathpart, '*') || strchr (pathpart, '?') + || strchr (pathpart, '['); + + patend = last_literal_end (pathpart); + + c = getc (fp); + while (c != EOF) + { + register char *s; /* Scan the path we read in. */ + + if (old_format) + { + /* Get the offset in the path where this path info starts. */ + if (c == LOCATEDB_OLD_ESCAPE) + count += getw (fp) - LOCATEDB_OLD_OFFSET; + else + count += c - LOCATEDB_OLD_OFFSET; + + /* Overlay the old path with the remainder of the new. */ + for (s = path + count; (c = getc (fp)) > LOCATEDB_OLD_ESCAPE;) + if (c < 0200) + *s++ = c; /* An ordinary character. */ + else + { + /* Bigram markers have the high bit set. */ + c &= 0177; + *s++ = bigram1[c]; + *s++ = bigram2[c]; + } + *s-- = '\0'; + } + else + { + if (c == LOCATEDB_ESCAPE) + count += get_short (fp); + else if (c > 127) + count += c - 256; + else + count += c; + + /* Overlay the old path with the remainder of the new. */ + nread = getstr (&path, &pathsize, fp, '\0', count); + if (nread < 0) + break; + c = getc (fp); + s = path + count + nread - 2; /* Move to the last char in path. */ + assert (s[0] != '\0'); + assert (s[1] == '\0'); /* Our terminator. */ + assert (s[2] == '\0'); /* Added by getstr. */ + } + + /* If the previous path matched, scan the whole path for the last + char in the subpattern. If not, the shared prefix doesn't match + the pattern, so don't scan it for the last char. */ + cutoff = prev_fast_match ? path : path + count; + + /* Search backward starting at the end of the path we just read in, + for the character at the end of the last glob-free subpattern + in PATHPART. */ + for (prev_fast_match = false; s >= cutoff; s--) + /* Fast first char check. */ + if (*s == *patend) + { + char *s2; /* Scan the path we read in. */ + register char *p2; /* Scan `patend'. */ + + for (s2 = s - 1, p2 = patend - 1; *p2 != '\0' && *s2 == *p2; + s2--, p2--) + ; + if (*p2 == '\0') + { + /* Success on the fast match. Compare the whole pattern + if it contains globbing characters. */ + prev_fast_match = true; + if (globflag == false || fnmatch (pathpart, path, 0) == 0) + { + puts (path); + ++printed; + } + break; + } + } + } + + if (ferror (fp)) + { + error (0, errno, "%s", dbfile); + return 0; + } + if (fclose (fp) == EOF) + { + error (0, errno, "%s", dbfile); + return 0; + } + + return printed; +} + +extern char *version_string; + +/* The name this program was run with. */ +char *program_name; + +static void +usage (stream, status) + FILE *stream; + int status; +{ + fprintf (stream, "\ +Usage: %s [-d path] [--database=path] [--version] [--help] pattern...\n", + program_name); + exit (status); +} + +static struct option const longopts[] = +{ + {"database", required_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, no_argument, NULL, 0} +}; + +void +main (argc, argv) + int argc; + char **argv; +{ + char *dbpath; + int found = 0, optc; + + program_name = argv[0]; + + dbpath = getenv ("LOCATE_PATH"); + if (dbpath == NULL) + dbpath = LOCATE_DB; + + while ((optc = getopt_long (argc, argv, "d:", longopts, (int *) 0)) != -1) + switch (optc) + { + case 'd': + dbpath = optarg; + break; + + case 'h': + usage (stdout, 0); + + case 'v': + printf ("GNU locate version %s\n", version_string); + exit (0); + + default: + usage (stderr, 1); + } + + if (optind == argc) + usage (stderr, 1); + + for (; optind < argc; optind++) + { + char *e; + next_element (dbpath); /* Initialize. */ + while ((e = next_element ((char *) NULL)) != NULL) + found |= locate (argv[optind], e); + } + + exit (!found); +} diff --git a/locate/locatedb.5 b/locate/locatedb.5 new file mode 100644 index 0000000..7145fee --- /dev/null +++ b/locate/locatedb.5 @@ -0,0 +1,106 @@ +.TH LOCATEDB 5L \" -*- nroff -*- +.SH NAME +locatedb \- front-compressed file name database +.SH DESCRIPTION +This manual page documents the format of file name databases for the +GNU version of +.BR locate . +The file name databases contain lists of files that were in +particular directory trees when the databases were last updated. +.P +There can be multiple databases. Users can select which databases +\fBlocate\fP searches using an environment variable or command line +option; see \fBlocate\fP(1L). The system administrator can choose the +file name of the default database, the frequency with which the +databases are updated, and the directories for which they contain +entries. Normally, file name databases are updated by running the +\fBupdatedb\fP program periodically, typically nightly; see +\fBupdatedb\fP(1L). +.P +\fBupdatedb\fP runs a program called \fBfrcode\fP to compress the list +of file names using front-compression, which reduces +the database size by a factor of 4 to 5. Front-compression (also +known as incremental encoding) works as follows. +.P +The database entries are a sorted list (case-insensitively, for users' +convenience). Since the list is sorted, each entry is likely to share +a prefix (initial string) with the previous entry. Each database +entry begins with an offset-differential count byte, which is the +additional number of characters of prefix of the preceding entry to +use beyond the number that the preceding entry is using of its +predecessor. (The counts can be negative.) Following the count is a +null-terminated ASCII remainder \(em the part of the name that follows +the shared prefix. +.P +If the offset-differential count is larger than can be stored in a +byte (+/\-127), the byte has the value 0x80 and the count follows in a +2-byte word, with the high byte first (network byte order). +.P +Every database begins with a dummy entry for a file called `LOCATE02', +which \fBlocate\fP checks for to ensure that the database file has the +correct format; it ignores the entry in doing the search. +.P +Databases can not be concatenated together, even if the first +(dummy) entry is trimmed from all but the first database. This +is because the offset-differential count in the first entry of the +second and following databases will be wrong. +.P +There is also an old database format, used by Unix +.B locate +and +.B find +programs and earlier releases of the GNU ones. \fBupdatedb\fP runs +programs called \fBbigram\fP and \fBcode\fP to produce old-format +databases. The old format differs from the above description in the +following ways. Instead of each entry starting with an +offset-differential count byte and ending with a null, byte values +from 0 through 28 indicate offset-differential counts from -14 through +14. The byte value indicating that a long offset-differential count +follows is 0x1e (30), not 0x80. The long counts are stored in host +byte order, which is not necessarily network byte order, and host +integer word size, which is usually 4 bytes. They also represent a +count 14 less than their value. The database lines have no +termination byte; the start of the next line is indicated by its first +byte having a value <= 30. +.P +In addition, instead of starting with a dummy entry, the old database +format starts with a 256 byte table containing the 128 most common +bigrams in the file list. A bigram is a pair of adjacent bytes. +Bytes in the database that have the high bit set are indexes (with the +high bit cleared) into the bigram table. The bigram and +offset-differential count coding makes these databases 20-25% smaller +than the new format, but makes them not 8-bit clean. Any byte in a +file name that is in the ranges used for the special codes is replaced +in the database by a question mark, which not coincidentally is the +shell wildcard to match a single character. +.SH EXAMPLE +.nf + +Input to \fBfrcode\fP: +.\" with nulls changed to newlines: +/usr/src +/usr/src/cmd/aardvark.c +/usr/src/cmd/armadillo.c +/usr/tmp/zoo + +Length of the longest prefix of the preceding entry to share: +0 /usr/src +8 /cmd/aardvark.c +14 rmadillo.c +5 tmp/zoo + +.fi +Output from \fBfrcode\fP, with trailing nulls changed to newlines +and count bytes made printable: +.nf +0 LOCATE02 +0 /usr/src +8 /cmd/aardvark.c +6 rmadillo.c +\-9 tmp/zoo + +(6 = 14 \- 8, and \-9 = 5 \- 14) +.fi +.SH "SEE ALSO" +\fBfind\fP(1L), \fBlocate\fP(1L), \fBlocatedb\fP(5L), \fBxargs\fP(1L) +\fBFinding Files\fP (on-line in Info, or printed) diff --git a/locate/locatedb.h b/locate/locatedb.h new file mode 100644 index 0000000..e401765 --- /dev/null +++ b/locate/locatedb.h @@ -0,0 +1,42 @@ +/* locatedb.h -- declarations for the locate database + Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _LOCATEDB_H +#define _LOCATEDB_H 1 + +/* The magic string at the start of a locate database, to make sure + it's in the right format. The 02 is the database format version number. + This string has the same format as a database entry, but you can't + concatenate databases even if you remove it, since the differential count + in the first entry of the second database will be wrong. */ +#define LOCATEDB_MAGIC "\0LOCATE02" + +/* Common-prefix length differences in the ranges + 0..127, -127..-1 (0x00..0x7f, 0x81..0xff) fit into one byte. + This value (which is -128) indicates that the difference is + too large to fit into one byte, and a two-byte integer follows. */ +#define LOCATEDB_ESCAPE 0x80 + +/* These are used for old, bigram-encoded databases: */ + +/* Means the differential count follows in a 2-byte int instead. */ +#define LOCATEDB_OLD_ESCAPE 30 + +/* Offset added to differential counts to encode them as positive numbers. */ +#define LOCATEDB_OLD_OFFSET 14 + +#endif /* !_LOCATEDB_H */ diff --git a/locate/updatedb.1 b/locate/updatedb.1 new file mode 100644 index 0000000..7672626 --- /dev/null +++ b/locate/updatedb.1 @@ -0,0 +1,78 @@ +.TH UPDATEDB 1L \" -*- nroff -*- +.SH NAME +updatedb \- update a file name database +.SH SYNOPSIS +.B updatedb [\fIoptions\fP] +.SH DESCRIPTION +This manual page +documents the GNU version of +.BR updatedb , +which updates file name databases used by GNU +.BR locate . +The file name databases contain lists of files that were in +particular directory trees when the databases were last updated. +The file name of the default database is determined when \fBlocate\fP +and \fBupdatedb\fP are configured and installed. The frequency with +which the databases are updated and the directories for which they +contain entries depend on how often \fBupdatedb\fP is run, and with +which arguments. +.P +In networked environments, it often makes sense to build a database at +the root of each filesystem, containing the entries for that filesystem. +.B updatedb +is then run for each filesystem on the fileserver where that +filesystem is on a local disk, to prevent thrashing the network. +Users can select which databases \fBlocate\fP searches using an +environment variable or command line option; see \fBlocate\fP(1L). +Databases can not be concatenated together. +.P +The file name database format changed starting with GNU +.B find +and +.B locate +version 4.0 to allow machines with diffent byte orderings to share +the databases. The new GNU +.B locate +can read both the old and new database formats. +However, old versions of +.B locate +and +.B find +produce incorrect results if given a new-format database. +.SH OPTIONS +.TP +.B \-\-localpaths='\fIpath1 path2...\fP' +Non-network directories to put in the database. +Default is /. +.TP +.B \-\-netpaths='\fIpath1 path2...\fP' +Network (NFS, AFS, RFS, etc.) directories to put in the database. +Default is none. +.TP +.B \-\-prunepaths='\fIpath1 path2...\fP' +Directories to not put in the database, which would otherwise be. +Default is /tmp /usr/tmp /var/tmp /afs. +.TP +.B \-\-output=\fIdbfile\fP +The database file to build. +Default is system-dependent, but typically /usr/local/var/locatedb. +.TP +.B \-\-netuser=\fIuser\fP +The user to search network directories as, using \fBsu\fP(1). +Default is \fBdaemon\fP. +.TP +.B \-\-old\-format +Create the database in the old format instead of the new one. +.TP +.B \-\-version +Print the version number of +.B updatedb +and exit. +.TP +.I "\-\-help" +Print a summary of the options to +.B updatedb +and exit. +.SH "SEE ALSO" +\fBfind\fP(1L), \fBlocate\fP(1L), \fBlocatedb\fP(5L), \fBxargs\fP(1L) +\fBFinding Files\fP (on-line in Info, or printed) diff --git a/locate/updatedb.sh b/locate/updatedb.sh new file mode 100644 index 0000000..944791a --- /dev/null +++ b/locate/updatedb.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# updatedb -- build a locate pathname database +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# csh original by James Woods; sh conversion by David MacKenzie. + +usage="\ +Usage: updatedb [--localpaths='dir1 dir2...'] [--netpaths='dir1 dir2...'] + [--prunepaths='dir1 dir2...'] [--output=dbfile] [--netuser=user] + [--old-format] [--version] [--help]" + +old=no +for arg +do + opt=`echo $arg|sed 's/^\([^=]*\).*/\1/'` + val=`echo $arg|sed 's/^[^=]*=\(.*\)/\1/'` + case "$opt" in + --localpaths) SEARCHPATHS="$val" ;; + --netpaths) NETPATHS="$val" ;; + --prunepaths) PRUNEPATHS="$val" ;; + --output) LOCATE_DB="$val" ;; + --netuser) NETUSER="$val" ;; + --old-format) old=yes ;; + --version) echo "GNU updatedb version @version@"; exit 0 ;; + --help) echo "$usage"; exit 0 ;; + *) echo "updatedb: invalid option $opt +$usage" >&2 + exit 1 ;; + esac +done + +# You can set these in the environment, or use command-line options, +# to override their defaults: + +# Non-network directories to put in the database. +: ${SEARCHPATHS="/"} + +# Network (NFS, AFS, RFS, etc.) directories to put in the database. +: ${NETPATHS=} + +# Directories to not put in the database, which would otherwise be. +: ${PRUNEPATHS="/tmp /usr/tmp /var/tmp /afs"} + +# The same, in the form of a regex that find can use. +test -z "$PRUNEREGEX" && + PRUNEREGEX=`echo $PRUNEPATHS|sed -e 's,^,\\\(^,' -e 's, ,$\\\)\\\|\\\(^,g' -e 's,$,$\\\),'` + +# The database file to build. +: ${LOCATE_DB=@LOCATE_DB@} + +# Directory to hold intermediate files. +if test -d /var/tmp; then + : ${TMPDIR=/var/tmp} +else + : ${TMPDIR=/usr/tmp} +fi + +# The user to search network directories as. +: ${NETUSER=daemon} + +# The directory containing the subprograms. +: ${LIBEXECDIR=@libexecdir@} + +# The directory containing find. +: ${BINDIR=@bindir@} + +# The names of the utilities to run to build the database. +: ${find=@find@} +: ${frcode=@frcode@} +: ${bigram=@bigram@} +: ${code=@code@} + +PATH=$LIBEXECDIR:$BINDIR:/usr/ucb:/bin:/usr/bin:$PATH export PATH + +# Make and code the file list. +# Sort case insensitively for users' convenience. + +if test $old = no; then + +# FIXME figure out how to sort null-terminated strings, and use -print0. +{ +if test -n "$SEARCHPATHS"; then + $find $SEARCHPATHS \ + \( -fstype nfs -o -fstype NFS -o -type d -regex "$PRUNEREGEX" \) -prune -o -print +fi + +if test -n "$NETPATHS"; then + su $NETUSER -c \ + "$find $NETPATHS \\( -type d -regex \"$PRUNEREGEX\" -prune \\) -o -print" +fi +} | sort -f | $frcode > $LOCATE_DB.n + +# To avoid breaking locate while this script is running, put the +# results in a temp file, then rename it atomically. +if test -s $LOCATE_DB.n; then + rm -f $LOCATE_DB + mv $LOCATE_DB.n $LOCATE_DB + chmod 644 $LOCATE_DB +else + echo "updatedb: new database would be empty" >&2 + rm -f $LOCATE_DB.n +fi + +else # old + +bigrams=$TMPDIR/f.bigrams$$ +filelist=$TMPDIR/f.list$$ +trap 'rm -f $bigrams $filelist; exit' 1 15 + +# Alphabetize subdirectories before file entries using tr. James says: +# "to get everything in monotonic collating sequence, to avoid some +# breakage i'll have to think about." +{ +if test -n "$SEARCHPATHS"; then + $find $SEARCHPATHS \ + \( -fstype nfs -o -fstype NFS -o -type d -regex "$PRUNEREGEX" \) -prune -o -print +fi +if test -n "$NETPATHS"; then + su $NETUSER -c \ + "$find $NETPATHS \\( -type d -regex \"$PRUNEREGEX\" -prune \\) -o -print" +fi +} | tr / '\001' | sort -f | tr '\001' / > $filelist + +# Compute the (at most 128) most common bigrams in the file list. +$bigram < $filelist | sort | uniq -c | sort -nr | + awk '{ if (NR <= 128) print $2 }' | tr -d '\012' > $bigrams + +# Code the file list. +$code $bigrams < $filelist > $LOCATE_DB +chmod 644 $LOCATE_DB + +rm -f $bigrams $filelist $errs + +fi diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 0000000..91f6d04 --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,32 @@ +#!/bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Last modified: 1994-03-25 +# Public domain + +errstatus=0 + +for file in ${1+"$@"} ; do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d in ${1+"$@"} ; do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" 1>&2 + mkdir "$pathcomp" || errstatus=$? + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..81bc61c --- /dev/null +++ b/stamp-h.in @@ -0,0 +1 @@ +Tue Oct 4 11:24:03 EDT 1994 diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am new file mode 100644 index 0000000..c360600 --- /dev/null +++ b/testsuite/Makefile.am @@ -0,0 +1,36 @@ +XARGS = ../xargs/xargs +XARGSFLAGS = + +RUNTEST= runtest +RUNTESTFLAGS= + +DIST_OTHER = config/*.exp inputs/*.xi xargs.*/*.exp xargs.*/*.xo +DIST_DIRS = config inputs xargs.gnu xargs.posix xargs.sysv + +CLEANFILES = *.log *.sum site.exp site.bak + +check:: site.exp + @echo This only works if you have the DejaGNU runtest program installed... + $(RUNTEST) $(RUNTESTFLAGS) --tool xargs XARGS=${XARGS} \ + XARGSFLAGS="${XARGSFLAGS}" --srcdir $(srcdir) + +site.exp: + @echo "Making a new site.exp file..." + -@rm -f site.bak + -@mv site.exp site.bak + @echo "## these variables are automatically generated by make ##" > site.exp + @echo "# Do not edit here. If you wish to override these values" >> site.exp + @echo "# add them to the last section" >> site.exp + @echo "set tool xargs" >> site.exp + @echo "set srcdir ${srcdir}" >> site.exp + @echo "set objdir `pwd`" >> site.exp + @echo "## All variables above are generated by configure. Do Not Edit ##" >> site.exp + -@sed '1,/^## All variables above are.*##/ d' site.bak >> site.exp + +$(DIST_DIRS): FORCE + -mkdir ../`cat ../distname`/$(subdir) + -for d in $(DIST_DIRS); do \ + echo mkdir ../`cat ../distname`/$(subdir)/$$d; \ + mkdir ../`cat ../distname`/$(subdir)/$$d; done + +FORCE: diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in new file mode 100644 index 0000000..902beef --- /dev/null +++ b/testsuite/Makefile.in @@ -0,0 +1,118 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +SOURCES = +DIST_CONF = Makefile.am Makefile.in +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +XARGS = ../xargs/xargs +XARGSFLAGS = + +RUNTEST= runtest +RUNTESTFLAGS= + +DIST_OTHER = config/*.exp inputs/*.xi xargs.*/*.exp xargs.*/*.xo +DIST_DIRS = config inputs xargs.gnu xargs.posix xargs.sysv + +CLEANFILES = *.log *.sum site.exp site.bak + +all:: ${ALL} + +mostlyclean: + rm -f *.o core + +clean: mostlyclean + rm -f $(PROGRAMS) $(LIBPROGRAMS) $(LIBFILES) $(TEXFILES) $(CLEANFILES) + +distclean: clean + rm -f Makefile *.tab.c $(DISTCLEANFILES) + rm -f config.cache config.log config.status ${CONFIG_HEADER} stamp-h + +realclean: distclean + rm -f TAGS $(INFOS) + +dist: $(DIST_FILES) $(DIST_DIRS) + -mkdir ../`cat ../distname`/$(subdir) + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file;}; \ + done + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +check:: site.exp + @echo This only works if you have the DejaGNU runtest program installed... + $(RUNTEST) $(RUNTESTFLAGS) --tool xargs XARGS=${XARGS} \ + XARGSFLAGS="${XARGSFLAGS}" --srcdir $(srcdir) + +site.exp: + @echo "Making a new site.exp file..." + -@rm -f site.bak + -@mv site.exp site.bak + @echo "## these variables are automatically generated by make ##" > site.exp + @echo "# Do not edit here. If you wish to override these values" >> site.exp + @echo "# add them to the last section" >> site.exp + @echo "set tool xargs" >> site.exp + @echo "set srcdir ${srcdir}" >> site.exp + @echo "set objdir `pwd`" >> site.exp + @echo "## All variables above are generated by configure. Do Not Edit ##" >> site.exp + -@sed '1,/^## All variables above are.*##/ d' site.bak >> site.exp + +$(DIST_DIRS): FORCE + -mkdir ../`cat ../distname`/$(subdir) + -for d in $(DIST_DIRS); do \ + echo mkdir ../`cat ../distname`/$(subdir)/$$d; \ + mkdir ../`cat ../distname`/$(subdir)/$$d; done + +FORCE: diff --git a/testsuite/config/unix.exp b/testsuite/config/unix.exp new file mode 100644 index 0000000..a85d7cd --- /dev/null +++ b/testsuite/config/unix.exp @@ -0,0 +1,109 @@ +# -*- TCL -*- +# Test-specific TCL procedures required by DejaGNU. +# Copyright (C) 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 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. + +# Modified by David MacKenzie from the gcc files +# written by Rob Savoye . + + +# Called by runtest. +# Extract and print the version number of xargs. +proc xargs_version {} { + global XARGS + global XARGSFLAGS + + if {[which $XARGS] != 0} then { + set tmp [ eval exec $XARGS $XARGSFLAGS --version xargs.out" + send_log "$cmd\n" + if $verbose>1 then { + send_user "Spawning \"$cmd\"\n" + } + + catch "exec $cmd" comp_output + if {$comp_output != ""} then { + send_log "$comp_output\n" + if $verbose>1 then { + send_user "$comp_output\n" + } + if $fail_good then { + pass "$testname" + } else { + fail "$testname, $comp_output" + } + return + } + + if [file exists $outfile] then { + set cmp_cmd "cmp xargs.out $outfile" + send_log "$cmp_cmd\n" + catch "exec $cmp_cmd" cmpout + if {$cmpout != ""} then { + fail "$testname, $cmpout" + return + } + } else { + if {[file size xargs.out] != 0} then { + fail "$testname, output should be empty" + return + } + } + pass "$testname" +} + +# Called by runtest. +# Clean up (remove temporary files) before runtest exits. +proc xargs_exit {} { + catch "exec rm -f xargs.out" +} diff --git a/testsuite/inputs/eof.xi b/testsuite/inputs/eof.xi new file mode 100644 index 0000000..0a4baec --- /dev/null +++ b/testsuite/inputs/eof.xi @@ -0,0 +1,5 @@ +firstline +secondline + _ +thirdline +fourthline diff --git a/testsuite/inputs/eofstr.xi b/testsuite/inputs/eofstr.xi new file mode 100644 index 0000000..6e60cfd --- /dev/null +++ b/testsuite/inputs/eofstr.xi @@ -0,0 +1,5 @@ +firstline +secondline +EOF +thirdline +fourthline diff --git a/testsuite/inputs/files.xi b/testsuite/inputs/files.xi new file mode 100644 index 0000000..5590252 --- /dev/null +++ b/testsuite/inputs/files.xi @@ -0,0 +1,22 @@ +/src/gnu/autoconf-1.11 +/src/gnu/autoconf-1.11/README +/src/gnu/autoconf-1.11/Makefile.in +/src/gnu/autoconf-1.11/INSTALL +/src/gnu/autoconf-1.11/NEWS +/src/gnu/autoconf-1.11/COPYING +/src/gnu/autoconf-1.11/ChangeLog +/src/gnu/autoconf-1.11/autoconf.texi +/src/gnu/autoconf-1.11/acconfig.h +/src/gnu/autoconf-1.11/autoconf.sh +/src/gnu/autoconf-1.11/acgeneral.m4 +/src/gnu/autoconf-1.11/acspecific.m4 +/src/gnu/autoconf-1.11/configure +/src/gnu/autoconf-1.11/configure.in +/src/gnu/autoconf-1.11/autoheader.sh +/src/gnu/autoconf-1.11/mkinstalldirs +/src/gnu/autoconf-1.11/install.sh +/src/gnu/autoconf-1.11/autoconf.info +/src/gnu/autoconf-1.11/standards.texi +/src/gnu/autoconf-1.11/make-stds.texi +/src/gnu/autoconf-1.11/standards.info +/src/gnu/autoconf-1.11/texinfo.tex diff --git a/testsuite/inputs/files0.xi b/testsuite/inputs/files0.xi new file mode 100644 index 0000000000000000000000000000000000000000..08ab0e2f546702f9775b82c22da7863d9983cf97 GIT binary patch literal 753 zcwU8)(F%hg6oz}DlwdC~wirRRu?Az8q9;3KHnd=mzhsNu?AV>h|Ksy`lzS(+wlf#= zG|JJoX~wcF;eWUJHQyhroJuRvLyHC&S_&!Z=C&_ODz5W;LuH5az}c z){0H=LK2c;jhc0v8c!oqg9RrH8+J6~pCGYC8G$~%%nmkz$jC$0fl%NgEe}0fKM7+L pI!{4gBzE(4IB46Ef)8epDT7^Ff4H}C2tCn{x|>t literal 0 HcwPel00001 diff --git a/testsuite/inputs/quotes.xi b/testsuite/inputs/quotes.xi new file mode 100644 index 0000000..031e84f --- /dev/null +++ b/testsuite/inputs/quotes.xi @@ -0,0 +1,5 @@ + this is +"quoted stuff" +and \ +an embedded newline +with 'single quotes' as well. diff --git a/testsuite/xargs.gnu/0n3.exp b/testsuite/xargs.gnu/0n3.exp new file mode 100644 index 0000000..33e96a8 --- /dev/null +++ b/testsuite/xargs.gnu/0n3.exp @@ -0,0 +1 @@ +xargs_start p {-0 -n3} files0.xi diff --git a/testsuite/xargs.gnu/0n3.xo b/testsuite/xargs.gnu/0n3.xo new file mode 100644 index 0000000..e663e64 --- /dev/null +++ b/testsuite/xargs.gnu/0n3.xo @@ -0,0 +1,8 @@ +/src/gnu/autoconf-1.11 /src/gnu/autoconf-1.11/README /src/gnu/autoconf-1.11/Makefile.in +/src/gnu/autoconf-1.11/INSTALL /src/gnu/autoconf-1.11/NEWS /src/gnu/autoconf-1.11/COPYING +/src/gnu/autoconf-1.11/ChangeLog /src/gnu/autoconf-1.11/autoconf.texi /src/gnu/autoconf-1.11/acconfig.h +/src/gnu/autoconf-1.11/autoconf.sh /src/gnu/autoconf-1.11/acgeneral.m4 /src/gnu/autoconf-1.11/acspecific.m4 +/src/gnu/autoconf-1.11/configure /src/gnu/autoconf-1.11/configure.in /src/gnu/autoconf-1.11/autoheader.sh +/src/gnu/autoconf-1.11/mkinstalldirs /src/gnu/autoconf-1.11/install.sh /src/gnu/autoconf-1.11/autoconf.info +/src/gnu/autoconf-1.11/standards.texi /src/gnu/autoconf-1.11/make-stds.texi /src/gnu/autoconf-1.11/standards.info +/src/gnu/autoconf-1.11/texinfo.tex diff --git a/testsuite/xargs.gnu/nothing.exp b/testsuite/xargs.gnu/nothing.exp new file mode 100644 index 0000000..20c03fd --- /dev/null +++ b/testsuite/xargs.gnu/nothing.exp @@ -0,0 +1 @@ +xargs_start p {echo this plus that} {} diff --git a/testsuite/xargs.gnu/nothing.xo b/testsuite/xargs.gnu/nothing.xo new file mode 100644 index 0000000..b8e9a02 --- /dev/null +++ b/testsuite/xargs.gnu/nothing.xo @@ -0,0 +1 @@ +this plus that diff --git a/testsuite/xargs.gnu/r.exp b/testsuite/xargs.gnu/r.exp new file mode 100644 index 0000000..8de762f --- /dev/null +++ b/testsuite/xargs.gnu/r.exp @@ -0,0 +1 @@ +xargs_start p {-r echo this plus that} {} diff --git a/testsuite/xargs.posix/hithere.exp b/testsuite/xargs.posix/hithere.exp new file mode 100644 index 0000000..0256d4b --- /dev/null +++ b/testsuite/xargs.posix/hithere.exp @@ -0,0 +1 @@ +xargs_start p {-s470 echo hi there} files.xi diff --git a/testsuite/xargs.posix/hithere.xo b/testsuite/xargs.posix/hithere.xo new file mode 100644 index 0000000..0c6845b --- /dev/null +++ b/testsuite/xargs.posix/hithere.xo @@ -0,0 +1,2 @@ +hi there /src/gnu/autoconf-1.11 /src/gnu/autoconf-1.11/README /src/gnu/autoconf-1.11/Makefile.in /src/gnu/autoconf-1.11/INSTALL /src/gnu/autoconf-1.11/NEWS /src/gnu/autoconf-1.11/COPYING /src/gnu/autoconf-1.11/ChangeLog /src/gnu/autoconf-1.11/autoconf.texi /src/gnu/autoconf-1.11/acconfig.h /src/gnu/autoconf-1.11/autoconf.sh /src/gnu/autoconf-1.11/acgeneral.m4 /src/gnu/autoconf-1.11/acspecific.m4 /src/gnu/autoconf-1.11/configure +hi there /src/gnu/autoconf-1.11/configure.in /src/gnu/autoconf-1.11/autoheader.sh /src/gnu/autoconf-1.11/mkinstalldirs /src/gnu/autoconf-1.11/install.sh /src/gnu/autoconf-1.11/autoconf.info /src/gnu/autoconf-1.11/standards.texi /src/gnu/autoconf-1.11/make-stds.texi /src/gnu/autoconf-1.11/standards.info /src/gnu/autoconf-1.11/texinfo.tex diff --git a/testsuite/xargs.posix/n3.exp b/testsuite/xargs.posix/n3.exp new file mode 100644 index 0000000..472b614 --- /dev/null +++ b/testsuite/xargs.posix/n3.exp @@ -0,0 +1 @@ +xargs_start p {-n3} files.xi diff --git a/testsuite/xargs.posix/n3.xo b/testsuite/xargs.posix/n3.xo new file mode 100644 index 0000000..e663e64 --- /dev/null +++ b/testsuite/xargs.posix/n3.xo @@ -0,0 +1,8 @@ +/src/gnu/autoconf-1.11 /src/gnu/autoconf-1.11/README /src/gnu/autoconf-1.11/Makefile.in +/src/gnu/autoconf-1.11/INSTALL /src/gnu/autoconf-1.11/NEWS /src/gnu/autoconf-1.11/COPYING +/src/gnu/autoconf-1.11/ChangeLog /src/gnu/autoconf-1.11/autoconf.texi /src/gnu/autoconf-1.11/acconfig.h +/src/gnu/autoconf-1.11/autoconf.sh /src/gnu/autoconf-1.11/acgeneral.m4 /src/gnu/autoconf-1.11/acspecific.m4 +/src/gnu/autoconf-1.11/configure /src/gnu/autoconf-1.11/configure.in /src/gnu/autoconf-1.11/autoheader.sh +/src/gnu/autoconf-1.11/mkinstalldirs /src/gnu/autoconf-1.11/install.sh /src/gnu/autoconf-1.11/autoconf.info +/src/gnu/autoconf-1.11/standards.texi /src/gnu/autoconf-1.11/make-stds.texi /src/gnu/autoconf-1.11/standards.info +/src/gnu/autoconf-1.11/texinfo.tex diff --git a/testsuite/xargs.posix/quotes.exp b/testsuite/xargs.posix/quotes.exp new file mode 100644 index 0000000..574e1ec --- /dev/null +++ b/testsuite/xargs.posix/quotes.exp @@ -0,0 +1 @@ +xargs_start p {} quotes.xi diff --git a/testsuite/xargs.posix/quotes.xo b/testsuite/xargs.posix/quotes.xo new file mode 100644 index 0000000..9b6477f --- /dev/null +++ b/testsuite/xargs.posix/quotes.xo @@ -0,0 +1,2 @@ +this is quoted stuff and +an embedded newline with single quotes as well. diff --git a/testsuite/xargs.posix/s47.exp b/testsuite/xargs.posix/s47.exp new file mode 100644 index 0000000..0945b08 --- /dev/null +++ b/testsuite/xargs.posix/s47.exp @@ -0,0 +1 @@ +xargs_start f {-s47} files.xi diff --git a/testsuite/xargs.posix/s47.xo b/testsuite/xargs.posix/s47.xo new file mode 100644 index 0000000..26eed8f --- /dev/null +++ b/testsuite/xargs.posix/s47.xo @@ -0,0 +1,18 @@ +/src/gnu/autoconf-1.11 +/src/gnu/autoconf-1.11/README +/src/gnu/autoconf-1.11/Makefile.in +/src/gnu/autoconf-1.11/INSTALL +/src/gnu/autoconf-1.11/NEWS +/src/gnu/autoconf-1.11/COPYING +/src/gnu/autoconf-1.11/ChangeLog +/src/gnu/autoconf-1.11/autoconf.texi +/src/gnu/autoconf-1.11/acconfig.h +/src/gnu/autoconf-1.11/autoconf.sh +/src/gnu/autoconf-1.11/acgeneral.m4 +/src/gnu/autoconf-1.11/acspecific.m4 +/src/gnu/autoconf-1.11/configure +/src/gnu/autoconf-1.11/configure.in +/src/gnu/autoconf-1.11/autoheader.sh +/src/gnu/autoconf-1.11/mkinstalldirs +/src/gnu/autoconf-1.11/install.sh +/src/gnu/autoconf-1.11/autoconf.info diff --git a/testsuite/xargs.posix/s470.exp b/testsuite/xargs.posix/s470.exp new file mode 100644 index 0000000..0643926 --- /dev/null +++ b/testsuite/xargs.posix/s470.exp @@ -0,0 +1 @@ +xargs_start p {-s470} files.xi diff --git a/testsuite/xargs.posix/s470.xo b/testsuite/xargs.posix/s470.xo new file mode 100644 index 0000000..8107725 --- /dev/null +++ b/testsuite/xargs.posix/s470.xo @@ -0,0 +1,2 @@ +/src/gnu/autoconf-1.11 /src/gnu/autoconf-1.11/README /src/gnu/autoconf-1.11/Makefile.in /src/gnu/autoconf-1.11/INSTALL /src/gnu/autoconf-1.11/NEWS /src/gnu/autoconf-1.11/COPYING /src/gnu/autoconf-1.11/ChangeLog /src/gnu/autoconf-1.11/autoconf.texi /src/gnu/autoconf-1.11/acconfig.h /src/gnu/autoconf-1.11/autoconf.sh /src/gnu/autoconf-1.11/acgeneral.m4 /src/gnu/autoconf-1.11/acspecific.m4 /src/gnu/autoconf-1.11/configure /src/gnu/autoconf-1.11/configure.in +/src/gnu/autoconf-1.11/autoheader.sh /src/gnu/autoconf-1.11/mkinstalldirs /src/gnu/autoconf-1.11/install.sh /src/gnu/autoconf-1.11/autoconf.info /src/gnu/autoconf-1.11/standards.texi /src/gnu/autoconf-1.11/make-stds.texi /src/gnu/autoconf-1.11/standards.info /src/gnu/autoconf-1.11/texinfo.tex diff --git a/testsuite/xargs.posix/s48.exp b/testsuite/xargs.posix/s48.exp new file mode 100644 index 0000000..506e77b --- /dev/null +++ b/testsuite/xargs.posix/s48.exp @@ -0,0 +1 @@ +xargs_start p {-s48} files.xi diff --git a/testsuite/xargs.posix/s48.xo b/testsuite/xargs.posix/s48.xo new file mode 100644 index 0000000..5590252 --- /dev/null +++ b/testsuite/xargs.posix/s48.xo @@ -0,0 +1,22 @@ +/src/gnu/autoconf-1.11 +/src/gnu/autoconf-1.11/README +/src/gnu/autoconf-1.11/Makefile.in +/src/gnu/autoconf-1.11/INSTALL +/src/gnu/autoconf-1.11/NEWS +/src/gnu/autoconf-1.11/COPYING +/src/gnu/autoconf-1.11/ChangeLog +/src/gnu/autoconf-1.11/autoconf.texi +/src/gnu/autoconf-1.11/acconfig.h +/src/gnu/autoconf-1.11/autoconf.sh +/src/gnu/autoconf-1.11/acgeneral.m4 +/src/gnu/autoconf-1.11/acspecific.m4 +/src/gnu/autoconf-1.11/configure +/src/gnu/autoconf-1.11/configure.in +/src/gnu/autoconf-1.11/autoheader.sh +/src/gnu/autoconf-1.11/mkinstalldirs +/src/gnu/autoconf-1.11/install.sh +/src/gnu/autoconf-1.11/autoconf.info +/src/gnu/autoconf-1.11/standards.texi +/src/gnu/autoconf-1.11/make-stds.texi +/src/gnu/autoconf-1.11/standards.info +/src/gnu/autoconf-1.11/texinfo.tex diff --git a/testsuite/xargs.posix/s6.exp b/testsuite/xargs.posix/s6.exp new file mode 100644 index 0000000..d572ae1 --- /dev/null +++ b/testsuite/xargs.posix/s6.exp @@ -0,0 +1 @@ +xargs_start f {-s6} files.xi diff --git a/testsuite/xargs.sysv/eEOF.exp b/testsuite/xargs.sysv/eEOF.exp new file mode 100644 index 0000000..8ac68aa --- /dev/null +++ b/testsuite/xargs.sysv/eEOF.exp @@ -0,0 +1 @@ +xargs_start p {-eEOF} eofstr.xi diff --git a/testsuite/xargs.sysv/eEOF.xo b/testsuite/xargs.sysv/eEOF.xo new file mode 100644 index 0000000..7f4c1cd --- /dev/null +++ b/testsuite/xargs.sysv/eEOF.xo @@ -0,0 +1 @@ +firstline secondline diff --git a/testsuite/xargs.sysv/eof.exp b/testsuite/xargs.sysv/eof.exp new file mode 100644 index 0000000..73a3426 --- /dev/null +++ b/testsuite/xargs.sysv/eof.exp @@ -0,0 +1 @@ +xargs_start p {} eof.xi diff --git a/testsuite/xargs.sysv/eof.xo b/testsuite/xargs.sysv/eof.xo new file mode 100644 index 0000000..7f4c1cd --- /dev/null +++ b/testsuite/xargs.sysv/eof.xo @@ -0,0 +1 @@ +firstline secondline diff --git a/testsuite/xargs.sysv/iARG.exp b/testsuite/xargs.sysv/iARG.exp new file mode 100644 index 0000000..2e2ec75 --- /dev/null +++ b/testsuite/xargs.sysv/iARG.exp @@ -0,0 +1 @@ +xargs_start p {-iARG echo ARG is xARGx} files.xi diff --git a/testsuite/xargs.sysv/iARG.xo b/testsuite/xargs.sysv/iARG.xo new file mode 100644 index 0000000..f28510b --- /dev/null +++ b/testsuite/xargs.sysv/iARG.xo @@ -0,0 +1,22 @@ +/src/gnu/autoconf-1.11 is x/src/gnu/autoconf-1.11x +/src/gnu/autoconf-1.11/README is x/src/gnu/autoconf-1.11/READMEx +/src/gnu/autoconf-1.11/Makefile.in is x/src/gnu/autoconf-1.11/Makefile.inx +/src/gnu/autoconf-1.11/INSTALL is x/src/gnu/autoconf-1.11/INSTALLx +/src/gnu/autoconf-1.11/NEWS is x/src/gnu/autoconf-1.11/NEWSx +/src/gnu/autoconf-1.11/COPYING is x/src/gnu/autoconf-1.11/COPYINGx +/src/gnu/autoconf-1.11/ChangeLog is x/src/gnu/autoconf-1.11/ChangeLogx +/src/gnu/autoconf-1.11/autoconf.texi is x/src/gnu/autoconf-1.11/autoconf.texix +/src/gnu/autoconf-1.11/acconfig.h is x/src/gnu/autoconf-1.11/acconfig.hx +/src/gnu/autoconf-1.11/autoconf.sh is x/src/gnu/autoconf-1.11/autoconf.shx +/src/gnu/autoconf-1.11/acgeneral.m4 is x/src/gnu/autoconf-1.11/acgeneral.m4x +/src/gnu/autoconf-1.11/acspecific.m4 is x/src/gnu/autoconf-1.11/acspecific.m4x +/src/gnu/autoconf-1.11/configure is x/src/gnu/autoconf-1.11/configurex +/src/gnu/autoconf-1.11/configure.in is x/src/gnu/autoconf-1.11/configure.inx +/src/gnu/autoconf-1.11/autoheader.sh is x/src/gnu/autoconf-1.11/autoheader.shx +/src/gnu/autoconf-1.11/mkinstalldirs is x/src/gnu/autoconf-1.11/mkinstalldirsx +/src/gnu/autoconf-1.11/install.sh is x/src/gnu/autoconf-1.11/install.shx +/src/gnu/autoconf-1.11/autoconf.info is x/src/gnu/autoconf-1.11/autoconf.infox +/src/gnu/autoconf-1.11/standards.texi is x/src/gnu/autoconf-1.11/standards.texix +/src/gnu/autoconf-1.11/make-stds.texi is x/src/gnu/autoconf-1.11/make-stds.texix +/src/gnu/autoconf-1.11/standards.info is x/src/gnu/autoconf-1.11/standards.infox +/src/gnu/autoconf-1.11/texinfo.tex is x/src/gnu/autoconf-1.11/texinfo.texx diff --git a/testsuite/xargs.sysv/iquotes.exp b/testsuite/xargs.sysv/iquotes.exp new file mode 100644 index 0000000..43b8877 --- /dev/null +++ b/testsuite/xargs.sysv/iquotes.exp @@ -0,0 +1 @@ +xargs_start p {-i__ echo FIRST __ IS OK} quotes.xi diff --git a/testsuite/xargs.sysv/iquotes.xo b/testsuite/xargs.sysv/iquotes.xo new file mode 100644 index 0000000..10ce85c --- /dev/null +++ b/testsuite/xargs.sysv/iquotes.xo @@ -0,0 +1,5 @@ +FIRST this is IS OK +FIRST quoted stuff IS OK +FIRST and +an embedded newline IS OK +FIRST with single quotes as well. IS OK diff --git a/testsuite/xargs.sysv/l1n4.exp b/testsuite/xargs.sysv/l1n4.exp new file mode 100644 index 0000000..4eef466 --- /dev/null +++ b/testsuite/xargs.sysv/l1n4.exp @@ -0,0 +1 @@ +xargs_start p {-l1 -n4} files.xi diff --git a/testsuite/xargs.sysv/l1n4.xo b/testsuite/xargs.sysv/l1n4.xo new file mode 100644 index 0000000..03f29b5 --- /dev/null +++ b/testsuite/xargs.sysv/l1n4.xo @@ -0,0 +1,6 @@ +/src/gnu/autoconf-1.11 /src/gnu/autoconf-1.11/README /src/gnu/autoconf-1.11/Makefile.in /src/gnu/autoconf-1.11/INSTALL +/src/gnu/autoconf-1.11/NEWS /src/gnu/autoconf-1.11/COPYING /src/gnu/autoconf-1.11/ChangeLog /src/gnu/autoconf-1.11/autoconf.texi +/src/gnu/autoconf-1.11/acconfig.h /src/gnu/autoconf-1.11/autoconf.sh /src/gnu/autoconf-1.11/acgeneral.m4 /src/gnu/autoconf-1.11/acspecific.m4 +/src/gnu/autoconf-1.11/configure /src/gnu/autoconf-1.11/configure.in /src/gnu/autoconf-1.11/autoheader.sh /src/gnu/autoconf-1.11/mkinstalldirs +/src/gnu/autoconf-1.11/install.sh /src/gnu/autoconf-1.11/autoconf.info /src/gnu/autoconf-1.11/standards.texi /src/gnu/autoconf-1.11/make-stds.texi +/src/gnu/autoconf-1.11/standards.info /src/gnu/autoconf-1.11/texinfo.tex diff --git a/testsuite/xargs.sysv/l2.exp b/testsuite/xargs.sysv/l2.exp new file mode 100644 index 0000000..c50b16c --- /dev/null +++ b/testsuite/xargs.sysv/l2.exp @@ -0,0 +1 @@ +xargs_start p {-l2} files.xi diff --git a/testsuite/xargs.sysv/l2.xo b/testsuite/xargs.sysv/l2.xo new file mode 100644 index 0000000..0fa9424 --- /dev/null +++ b/testsuite/xargs.sysv/l2.xo @@ -0,0 +1,11 @@ +/src/gnu/autoconf-1.11 /src/gnu/autoconf-1.11/README +/src/gnu/autoconf-1.11/Makefile.in /src/gnu/autoconf-1.11/INSTALL +/src/gnu/autoconf-1.11/NEWS /src/gnu/autoconf-1.11/COPYING +/src/gnu/autoconf-1.11/ChangeLog /src/gnu/autoconf-1.11/autoconf.texi +/src/gnu/autoconf-1.11/acconfig.h /src/gnu/autoconf-1.11/autoconf.sh +/src/gnu/autoconf-1.11/acgeneral.m4 /src/gnu/autoconf-1.11/acspecific.m4 +/src/gnu/autoconf-1.11/configure /src/gnu/autoconf-1.11/configure.in +/src/gnu/autoconf-1.11/autoheader.sh /src/gnu/autoconf-1.11/mkinstalldirs +/src/gnu/autoconf-1.11/install.sh /src/gnu/autoconf-1.11/autoconf.info +/src/gnu/autoconf-1.11/standards.texi /src/gnu/autoconf-1.11/make-stds.texi +/src/gnu/autoconf-1.11/standards.info /src/gnu/autoconf-1.11/texinfo.tex diff --git a/xargs/Makefile.am b/xargs/Makefile.am new file mode 100644 index 0000000..46e1cd6 --- /dev/null +++ b/xargs/Makefile.am @@ -0,0 +1,9 @@ +PROGRAMS = xargs +MANS = xargs.1 +INCLUDES = -I.. -I$(top_srcdir)/lib +LDADD = ../find/version.o ../lib/libfind.a +CONFIG_HEADER = ../config.h + +$(PROGRAMS): ../find/version.o ../lib/libfind.a + +xargs.o: ../lib/wait.h diff --git a/xargs/Makefile.in b/xargs/Makefile.in new file mode 100644 index 0000000..4d57ef6 --- /dev/null +++ b/xargs/Makefile.in @@ -0,0 +1,156 @@ +# Makefile.in generated automatically by automake from Makefile.am. +# Copyright (C) 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libexecdir = $(exec_prefix)/libexec +datadir = $(prefix)/share +sysconfdir = $(prefix)/etc +sharedstatedir = $(prefix)/com +localstatedir = $(prefix)/var +libdir = $(exec_prefix)/lib +infodir = $(prefix)/info +mandir = $(prefix)/man +includedir = $(prefix)/include +oldincludedir = /usr/include + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +transform = @program_transform_name@ + +ALL = ${PROGRAMS} ${LIBPROGRAMS} ${SCRIPTS} ${LIBSCRIPTS} ${LIBFILES} +CC = @CC@ +LEX = @LEX@ +YACC = @YACC@ +ANSI2KNR = ./ansi2knr + +DEFS = @DEFS@ +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +xargs_SOURCES = xargs.c +xargs_OBJECTS = xargs.o +NROFF = nroff + +SOURCES = xargs.c +DIST_CONF = Makefile.am Makefile.in +DIST_FILES = $(DIST_CONF) $(SOURCES) $(TEXINFOS) $(INFOS) $(MANS) $(DIST_OTHER) + +PROGRAMS = xargs +MANS = xargs.1 +INCLUDES = -I.. -I$(top_srcdir)/lib +LDADD = ../find/version.o ../lib/libfind.a +CONFIG_HEADER = ../config.h + +all:: ${ALL} + +.c.o: + $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $< + +$(xargs_OBJECTS): ../config.h +install:: install-programs + +install-programs: $(PROGRAMS) $(SCRIPTS) + $(top_srcdir)/mkinstalldirs $(bindir) + for p in $(PROGRAMS) $(SCRIPTS); do \ + $(INSTALL_PROGRAM) $$p $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +uninstall:: uninstall-programs + +uninstall-programs: + for p in $(PROGRAMS) $(SCRIPTS); do \ + rm -f $(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +xargs: $(xargs_OBJECTS) + $(CC) -o $@ $(xargs_OBJECTS) $(LDADD) $(LDFLAGS) $(LIBS) + +install:: install-man + +install-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\.\([0-9][a-z]*\)$$%\1%'`; \ + inst=`basename $$man $$sect|sed '$(transform)'`$$sect; \ + mdir=$(mandir)/man$$sect; \ + $(top_srcdir)/mkinstalldirs $$mdir; \ + echo installing $$man as $$mdir/$$inst; \ + $(INSTALL_DATA) $(srcdir)/$$man $$mdir/$$inst; \ + cdir=$(mandir)/cat$$sect; \ + if test -d $$cdir; then \ + echo formatting $$man as $$cdir/$$inst; \ + $(NROFF) -man $(srcdir)/$$man > $$cdir/$$inst; \ + fi; \ + done + +uninstall:: uninstall-man + +uninstall-man: + for man in $(MANS); do \ + sect=`echo $$man|sed 's%.*\(\.[0-9][a-z]*\)$$%\1%'; \ + inst=`basename $$man $sect|sed '$(transform)'`.$$sect; \ + mdir=$(mandir)/man$$sect; \ + cdir=$(mandir)/cat$$sect; \ + rm -f $$mdir/$$inst $$cdir/$$inst; \ + done + +mostlyclean: + rm -f *.o core + +clean: mostlyclean + rm -f $(PROGRAMS) $(LIBPROGRAMS) $(LIBFILES) $(TEXFILES) $(CLEANFILES) + +distclean: clean + rm -f Makefile *.tab.c $(DISTCLEANFILES) + rm -f config.cache config.log config.status ${CONFIG_HEADER} stamp-h + +realclean: distclean + rm -f TAGS $(INFOS) + +dist: $(DIST_FILES) $(DIST_DIRS) + -mkdir ../`cat ../distname`/$(subdir) + @for file in $(DIST_FILES); do \ + echo linking $$file; \ + ln $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file || \ + { echo copying $$file instead; cp -p $(srcdir)/$$file ../`cat ../distname`/$(subdir)/$$file;}; \ + done + +check dvi info install uninstall:: + +tags:: TAGS + +TAGS:: + cd $(srcdir); etags $(SOURCES) + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: + +$(PROGRAMS): ../find/version.o ../lib/libfind.a + +xargs.o: ../lib/wait.h diff --git a/xargs/xargs.1 b/xargs/xargs.1 new file mode 100644 index 0000000..a9f39a7 --- /dev/null +++ b/xargs/xargs.1 @@ -0,0 +1,112 @@ +.TH XARGS 1L \" -*- nroff -*- +.SH NAME +xargs \- build and execute command lines from standard input +.SH SYNOPSIS +.B xargs +[\-0prtx] [\-e[eof-str]] [\-i[replace-str]] [\-l[max-lines]] +[\-n max-args] [\-s max-chars] [\-P max-procs] [\-\-null] [\-\-eof[=eof-str]] +[\-\-replace[=replace-str]] [\-\-max-lines[=max-lines]] [\-\-interactive] +[\-\-max-chars=max-chars] [\-\-verbose] [\-\-exit] [\-\-max-procs=max-procs] +[\-\-max-args=max-args] [\-\-no-run-if-empty] [\-\-version] [\-\-help] +[command [initial-arguments]] +.SH DESCRIPTION +This manual page +documents the GNU version of +.BR xargs . +.B xargs +reads arguments from the standard input, delimited by blanks (which can be +protected with double or single quotes or a backslash) or newlines, +and executes the +.I command +(default is /bin/echo) one or more times with any +.I initial-arguments +followed by arguments read from standard input. Blank lines on the +standard input are ignored. +.P +.B xargs +exits with the following status: +.nf +0 if it succeeds +123 if any invocation of the command exited with status 1-125 +124 if the command exited with status 255 +125 if the command is killed by a signal +126 if the command cannot be run +127 if the command is not found +1 if some other error occurred. +.fi +.SS OPTIONS +.TP +.I "\-\-null, \-0" +Input filenames are terminated by a null character instead of by +whitespace, and the quotes and backslash are not special (every +character is taken literally). Disables the end of file string, which +is treated like any other argument. Useful when arguments might +contain white space, quote marks, or backslashes. The GNU find +\-print0 option produces input suitable for this mode. +.TP +.I "\-\-eof[=eof-str], \-e[eof-str]" +Set the end of file string to \fIeof-str\fR. If the end of file +string occurs as a line of input, the rest of the input is ignored. +If \fIeof-str\fR is omitted, there is no end of file string. If this +option is not given, the end of file string defaults to "_". +.TP +.I "\-\-help" +Print a summary of the options to +.B xargs +and exit. +.TP +.I "\-\-replace[=replace-str], \-i[replace-str]" +Replace occurences of \fIreplace-str\fR in the initial arguments with +names read from standard input. +Also, unquoted blanks do not terminate arguments. +If \fIreplace-str\fR is omitted, it +defaults to "{}" (like for `find \-exec'). Implies \fI\-x\fP and +\fI\-l 1\fP. +.TP +.I "\-\-max-lines[=max-lines], -l[max-lines]" +Use at most \fImax-lines\fR nonblank input lines per command line; +\fImax-lines\fR defaults to 1 if omitted. Trailing blanks cause an +input line to be logically continued on the next input line. Implies +\fI\-x\fR. +.TP +.I "\-\-max-args=max-args, \-n max-args" +Use at most \fImax-args\fR arguments per command line. Fewer than +\fImax-args\fR arguments will be used if the size (see the \-s option) +is exceeded, unless the \-x option is given, in which case \fBxargs\fR +will exit. +.TP +.I "\-\-interactive, \-p" +Prompt the user about whether to run each command line and read a line +from the terminal. Only run the command line if the response starts +with `y' or `Y'. Implies \fI\-t\fR. +.TP +.I "\-\-no-run-if-empty, \-r" +If the standard input does not contain any nonblanks, do not run the +command. Normally, the command is run once even if there is no input. +.TP +.I "\-\-max-chars=max-chars, \-s max-chars" +Use at most \fImax-chars\fR characters per command line, including the +command and initial arguments and the terminating nulls at the ends of +the argument strings. The default is as large as possible, up to 20k +characters. +.TP +.I "\-\-verbose, \-t" +Print the command line on the standard error output before executing +it. +.TP +.I "\-\-version" +Print the version number of +.B xargs +and exit. +.TP +.I "\-\-exit, \-x" +Exit if the size (see the \fI\-s\fR option) is exceeded. +.TP +.I "\-\-max-procs=max-procs, \-P max-procs" +Run up to \fImax-procs\fR processes at a time; the default is 1. If +\fImax-procs\fR is 0, \fBxargs\fR will run as many processes as +possible at a time. Use the \fI\-n\fR option with \fI\-P\fR; +otherwise chances are that only one exec will be done. +.SH "SEE ALSO" +\fBfind\fP(1L), \fBlocate\fP(1L), \fBlocatedb\fP(5L), \fBupdatedb\fP(1) +\fBFinding Files\fP (on-line in Info, or printed) diff --git a/xargs/xargs.c b/xargs/xargs.c new file mode 100644 index 0000000..1a1e89d --- /dev/null +++ b/xargs/xargs.c @@ -0,0 +1,918 @@ +/* xargs -- build and execute command lines from standard input + Copyright (C) 1990, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Mike Rendell + and David MacKenzie . */ + +#include + +#if __STDC__ +#define P_(s) s +#else +#define P_(s) () +#endif + +#define _GNU_SOURCE +#include + +#if !defined (isascii) || defined (STDC_HEADERS) +#ifdef isascii +#undef isascii +#endif +#define isascii(c) 1 +#endif + +#ifdef isblank +#define ISBLANK(c) (isascii (c) && isblank (c)) +#else +#define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif + +#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \ + || (c) == '\f' || (c) == '\v') + +#include +#include +#include +#include + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include +#if !defined(STDC_HEADERS) +#include +#endif +#else +#include +#define memcpy(dest, source, count) (bcopy((source), (dest), (count))) +#endif + +char *strstr (); +char *strdup (); + +#ifndef _POSIX_SOURCE +#include +#endif + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#if !defined(SIGCHLD) && defined(SIGCLD) +#define SIGCHLD SIGCLD +#endif + +/* COMPAT: SYSV version defaults size (and has a max value of) to 470. + We try to make it as large as possible. */ +#if !defined(ARG_MAX) && defined(_SC_ARG_MAX) +#define ARG_MAX sysconf (_SC_ARG_MAX) +#endif +#ifndef ARG_MAX +#define ARG_MAX NCARGS +#endif + +#include "wait.h" + +/* States for read_line. */ +#define NORM 0 +#define SPACE 1 +#define QUOTE 2 +#define BACKSLASH 3 + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +void exit (); +void free (); +long strtol (); + +extern int errno; +#endif + +/* Return nonzero if S is the EOF string. */ +#define EOF_STR(s) (eof_str && *eof_str == *s && !strcmp (eof_str, s)) + +extern char **environ; + +/* Not char because of type promotion; NeXT gcc can't handle it. */ +typedef int boolean; +#define true 1 +#define false 0 + +#if __STDC__ +#define VOID void +#else +#define VOID char +#endif + +VOID *xmalloc P_ ((size_t n)); +VOID *xrealloc P_ ((VOID * p, size_t n)); +void error P_ ((int status, int errnum, char *message,...)); + +extern char *version_string; + +/* The name this program was run with. */ +char *program_name; + +/* Buffer for reading arguments from stdin. */ +static char *linebuf; + +/* Line number in stdin since the last command was executed. */ +static int lineno = 0; + +/* If nonzero, then instead of putting the args from stdin at + the end of the command argument list, they are each stuck into the + initial args, replacing each occurrence of the `replace_pat' in the + initial args. */ +static char *replace_pat = NULL; + +/* The length of `replace_pat'. */ +static size_t rplen = 0; + +/* If nonzero, when this string is read on stdin it is treated as + end of file. + I don't like this - it should default to NULL. */ +static char *eof_str = "_"; + +/* If nonzero, the maximum number of nonblank lines from stdin to use + per command line. */ +static long lines_per_exec = 0; + +/* The maximum number of arguments to use per command line. */ +static long args_per_exec = 1024; + +/* If true, exit if lines_per_exec or args_per_exec is exceeded. */ +static boolean exit_if_size_exceeded = false; + +/* The maximum number of characters that can be used per command line. */ +static long arg_max; + +/* Storage for elements of `cmd_argv'. */ +static char *argbuf; + +/* The list of args being built. */ +static char **cmd_argv = NULL; + +/* Number of elements allocated for `cmd_argv'. */ +static int cmd_argv_alloc = 0; + +/* Number of valid elements in `cmd_argv'. */ +static int cmd_argc = 0; + +/* Number of chars being used in `cmd_argv'. */ +static int cmd_argv_chars = 0; + +/* Number of initial arguments given on the command line. */ +static int initial_argc = 0; + +/* Number of chars in the initial args. */ +static int initial_argv_chars = 0; + +/* true when building up initial arguments in `cmd_argv'. */ +static boolean initial_args = true; + +/* If nonzero, the maximum number of child processes that can be running + at once. */ +static int proc_max = 1; + +/* Total number of child processes that have been executed. */ +static int procs_executed = 0; + +/* The number of elements in `pids'. */ +static int procs_executing = 0; + +/* List of child processes currently executing. */ +static pid_t *pids = NULL; + +/* The number of allocated elements in `pids'. */ +static int pids_alloc = 0; + +/* Exit status; nonzero if any child process exited with a + status of 1-125. */ +static int child_error = 0; + +/* If true, print each command on stderr before executing it. */ +static boolean print_command = false; + +/* If true, query the user before executing each command, and only + execute the command if the user responds affirmatively. */ +static boolean query_before_executing = false; + +static struct option const longopts[] = +{ + {"null", no_argument, NULL, '0'}, + {"eof", optional_argument, NULL, 'e'}, + {"replace", optional_argument, NULL, 'i'}, + {"max-lines", optional_argument, NULL, 'l'}, + {"max-args", required_argument, NULL, 'n'}, + {"interactive", no_argument, NULL, 'p'}, + {"no-run-if-empty", no_argument, NULL, 'r'}, + {"max-chars", required_argument, NULL, 's'}, + {"verbose", no_argument, NULL, 't'}, + {"exit", no_argument, NULL, 'x'}, + {"max-procs", required_argument, NULL, 'P'}, + {"version", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, no_argument, NULL, 0} +}; + +static int read_line P_ ((void)); +static int read_string P_ ((void)); +static void do_insert P_ ((char *arg, size_t arglen, size_t lblen)); +static void push_arg P_ ((char *arg, size_t len)); +static boolean print_args P_ ((boolean ask)); +static void do_exec P_ ((void)); +static void add_proc P_ ((pid_t pid)); +static void wait_for_proc P_ ((boolean all)); +static long parse_num P_ ((char *str, int option, long min, long max)); +static long env_size P_ ((char **envp)); +static void usage P_ ((FILE * stream, int status)); + +void +main (argc, argv) + int argc; + char **argv; +{ + int optc; + int always_run_command = 1; + long orig_arg_max; + char *default_cmd = "/bin/echo"; + int (*read_args) P_ ((void)) = read_line; + + program_name = argv[0]; + + orig_arg_max = ARG_MAX - 2048; /* POSIX.2 requires subtracting 2048. */ + arg_max = orig_arg_max; + + /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which + have it at 1 meg). Things will work fine with a large ARG_MAX but it + will probably hurt the system more than it needs to; an array of this + size is allocated. */ + if (arg_max > 20 * 1024) + arg_max = 20 * 1024; + + /* Take the size of the environment into account. */ + arg_max -= env_size (environ); + if (arg_max <= 0) + error (1, 0, "environment is too large for exec"); + + while ((optc = getopt_long (argc, argv, "+0e::i::l::n:prs:txP:", + longopts, (int *) 0)) != -1) + { + switch (optc) + { + case '0': + read_args = read_string; + break; + + case 'e': + if (optarg) + eof_str = optarg; + else + eof_str = 0; + break; + + case 'h': + usage (stdout, 0); + + case 'i': + if (optarg) + replace_pat = optarg; + else + replace_pat = "{}"; + /* -i excludes -n -l. */ + args_per_exec = 0; + lines_per_exec = 0; + break; + + case 'l': + if (optarg) + lines_per_exec = parse_num (optarg, 'l', 1L, -1L); + else + lines_per_exec = 1; + /* -l excludes -i -n. */ + args_per_exec = 0; + replace_pat = NULL; + break; + + case 'n': + args_per_exec = parse_num (optarg, 'n', 1L, -1L); + /* -n excludes -i -l. */ + lines_per_exec = 0; + replace_pat = NULL; + break; + + case 's': + arg_max = parse_num (optarg, 's', 1L, orig_arg_max); + break; + + case 't': + print_command = true; + break; + + case 'x': + exit_if_size_exceeded = true; + break; + + case 'p': + query_before_executing = true; + print_command = true; + break; + + case 'r': + always_run_command = 0; + break; + + case 'P': + proc_max = parse_num (optarg, 'P', 0L, -1L); + break; + + case 'v': + printf ("GNU xargs version %s\n", version_string); + exit (0); + + default: + usage (stderr, 1); + } + } + + if (replace_pat || lines_per_exec) + exit_if_size_exceeded = true; + + if (optind == argc) + { + optind = 0; + argc = 1; + argv = &default_cmd; + } + + linebuf = (char *) xmalloc (arg_max + 1); + argbuf = (char *) xmalloc (arg_max + 1); + + /* Make sure to listen for the kids. */ + signal (SIGCHLD, SIG_DFL); + + if (!replace_pat) + { + for (; optind < argc; optind++) + push_arg (argv[optind], strlen (argv[optind]) + 1); + initial_args = false; + initial_argc = cmd_argc; + initial_argv_chars = cmd_argv_chars; + + while ((*read_args) () != -1) + if (lines_per_exec && lineno >= lines_per_exec) + { + do_exec (); + lineno = 0; + } + + /* SYSV xargs seems to do at least one exec, even if the + input is empty. */ + if (cmd_argc != initial_argc + || (always_run_command && procs_executed == 0)) + do_exec (); + } + else + { + int i; + size_t len; + size_t *arglen = (size_t *) xmalloc (sizeof (size_t) * argc); + + for (i = optind; i < argc; i++) + arglen[i] = strlen(argv[i]); + rplen = strlen (replace_pat); + while ((len = (*read_args) ()) != -1) + { + /* Don't do insert on the command name. */ + push_arg (argv[optind], arglen[optind] + 1); + len--; + for (i = optind + 1; i < argc; i++) + do_insert (argv[i], arglen[i], len); + do_exec (); + } + } + + wait_for_proc (true); + exit (child_error); +} + +/* Read a line of arguments from stdin and add them to the list of + arguments to pass to the command. Ignore blank lines and initial blanks. + Single and double quotes and backslashes quote metacharacters and blanks + as they do in the shell. + Return -1 if eof (either physical or logical) is reached, + otherwise the length of the last string read (including the null). */ + +static int +read_line () +{ + static boolean eof = false; + /* Start out in mode SPACE to always strip leading spaces (even with -i). */ + int state = SPACE; /* The type of character we last read. */ + int prevc; /* The previous value of c. */ + int quotc = 0; /* The last quote character read. */ + int c = EOF; + boolean first = true; /* true if reading first arg on line. */ + int len; + char *p = linebuf; + /* Including the NUL, the args must not grow past this point. */ + char *endbuf = linebuf + arg_max - initial_argv_chars - 1; + + if (eof) + return -1; + while (1) + { + prevc = c; + c = getc (stdin); + if (c == EOF) + { + /* COMPAT: SYSV seems to ignore stuff on a line that + ends without a \n; we don't. */ + eof = true; + if (p == linebuf) + return -1; + *p++ = '\0'; + len = p - linebuf; + /* FIXME we don't check for unterminated quotes here. */ + if (first && EOF_STR (linebuf)) + return -1; + if (!replace_pat) + push_arg (linebuf, len); + return len; + } + switch (state) + { + case SPACE: + if (ISSPACE (c)) + continue; + state = NORM; + /* aaahhhh.... */ + + case NORM: + if (c == '\n') + { + if (!ISBLANK (prevc)) + lineno++; /* For -l. */ + if (p == linebuf) + { + /* Blank line. */ + state = SPACE; + continue; + } + *p++ = '\0'; + len = p - linebuf; + if (EOF_STR (linebuf)) + { + eof = true; + return first ? -1 : len; + } + if (!replace_pat) + push_arg (linebuf, len); + return len; + } + if (!replace_pat && ISSPACE (c)) + { + *p++ = '\0'; + len = p - linebuf; + if (EOF_STR (linebuf)) + { + eof = true; + return first ? -1 : len; + } + push_arg (linebuf, len); + p = linebuf; + state = SPACE; + first = false; + continue; + } + switch (c) + { + case '\\': + state = BACKSLASH; + continue; + + case '\'': + case '"': + state = QUOTE; + quotc = c; + continue; + } + break; + + case QUOTE: + if (c == '\n') + error (1, 0, "unmatched %s quote", + quotc == '"' ? "double" : "single"); + if (c == quotc) + { + state = NORM; + continue; + } + break; + + case BACKSLASH: + state = NORM; + break; + } + if (p >= endbuf) + error (1, 0, "argument line too long"); + *p++ = c; + } +} + +/* Read a null-terminated string from stdin and add it to the list of + arguments to pass to the command. + Return -1 if eof (either physical or logical) is reached, + otherwise the length of the string read (including the null). */ + +static int +read_string () +{ + static boolean eof = false; + int len; + char *p = linebuf; + /* Including the NUL, the args must not grow past this point. */ + char *endbuf = linebuf + arg_max - initial_argv_chars - 1; + + if (eof) + return -1; + while (1) + { + int c = getc (stdin); + if (c == EOF) + { + eof = true; + if (p == linebuf) + return -1; + *p++ = '\0'; + len = p - linebuf; + if (!replace_pat) + push_arg (linebuf, len); + return len; + } + if (c == '\0') + { + lineno++; /* For -l. */ + *p++ = '\0'; + len = p - linebuf; + if (!replace_pat) + push_arg (linebuf, len); + return len; + } + if (p >= endbuf) + error (1, 0, "argument line too long"); + *p++ = c; + } +} + +/* Replace all instances of `replace_pat' in ARG with `linebuf', + and add the resulting string to the list of arguments for the command + to execute. + ARGLEN is the length of ARG, not including the null. + LBLEN is the length of `linebuf', not including the null. + + COMPAT: insertions on the SYSV version are limited to 255 chars per line, + and a max of 5 occurences of replace_pat in the initial-arguments. + Those restrictions do not exist here. */ + +static void +do_insert (arg, arglen, lblen) + char *arg; + size_t arglen; + size_t lblen; +{ + /* Temporary copy of each arg with the replace pattern replaced by the + real arg. */ + static char *insertbuf; + char *p; + int bytes_left = arg_max - 1; /* Bytes left on the command line. */ + + if (!insertbuf) + insertbuf = (char *) xmalloc (arg_max + 1); + p = insertbuf; + + do + { + size_t len; /* Length in ARG before `replace_pat'. */ + char *s = strstr (arg, replace_pat); + if (s) + len = s - arg; + else + len = arglen; + bytes_left -= len; + if (bytes_left <= 0) + break; + + strncpy (p, arg, len); + p += len; + arg += len; + + if (s) + { + bytes_left -= lblen; + if (bytes_left <= 0) + break; + strcpy (p, linebuf); + arg += rplen; + p += lblen; + } + } + while (*arg); + if (*arg) + error (1, 0, "command too long"); + *p++ = '\0'; + push_arg (insertbuf, p - insertbuf); +} + +/* Add ARG to the end of the list of arguments `cmd_argv' to pass + to the command. + LEN is the length of ARG, including the terminating null. + If this brings the list up to its maximum size, execute the command. */ + +static void +push_arg (arg, len) + char *arg; + size_t len; +{ + if (arg) + { + if (cmd_argv_chars + len > arg_max) + { + if (initial_args || cmd_argc == initial_argc) + error (1, 0, "can not fit single argument within argument list size limit"); + if (replace_pat + || (exit_if_size_exceeded && + (lines_per_exec || args_per_exec))) + error (1, 0, "argument list too long"); + do_exec (); + } + if (!initial_args && args_per_exec && + cmd_argc - initial_argc == args_per_exec) + do_exec (); + } + + if (cmd_argc >= cmd_argv_alloc) + { + if (!cmd_argv) + { + cmd_argv_alloc = 64; + cmd_argv = (char **) xmalloc (sizeof (char *) * cmd_argv_alloc); + } + else + { + cmd_argv_alloc *= 2; + cmd_argv = (char **) xrealloc (cmd_argv, + sizeof (char *) * cmd_argv_alloc); + } + } + + if (!arg) + cmd_argv[cmd_argc++] = NULL; + else + { + cmd_argv[cmd_argc++] = argbuf + cmd_argv_chars; + strcpy (argbuf + cmd_argv_chars, arg); + cmd_argv_chars += len; + } +} + +/* Print the arguments of the command to execute. + If ASK is nonzero, prompt the user for a response, and + if the user responds affirmatively, return true; + otherwise, return false. */ + +static boolean +print_args (ask) + boolean ask; +{ + int i; + + for (i = 0; i < cmd_argc - 1; i++) + fprintf (stderr, "%s ", cmd_argv[i]); + if (ask) + { + static FILE *tty_stream; + int c, savec; + + if (!tty_stream) + { + tty_stream = fopen ("/dev/tty", "r"); + if (!tty_stream) + error (1, errno, "/dev/tty"); + } + fputs ("?...", stderr); + fflush (stderr); + c = savec = getc (tty_stream); + while (c != EOF && c != '\n') + c = getc (tty_stream); + if (savec == 'y' || savec == 'Y') + return true; + } + else + putc ('\n', stderr); + + return false; +} + +/* Execute the command that has been built in `cmd_argv'. This may involve + waiting for processes that were previously executed. */ + +static void +do_exec () +{ + pid_t child; + + push_arg ((char *) NULL, 0); /* Null terminate the arg list. */ + if (!query_before_executing || print_args (true)) + { + if (proc_max && procs_executing >= proc_max) + wait_for_proc (false); + if (!query_before_executing && print_command) + print_args (false); + /* If we run out of processes, wait for a child to return and + try again. */ + while ((child = fork ()) < 0 && errno == EAGAIN && procs_executing) + wait_for_proc (false); + switch (child) + { + case -1: + error (1, errno, "cannot fork"); + + case 0: /* Child. */ + execvp (cmd_argv[0], cmd_argv); + error (0, errno, "%s", cmd_argv[0]); + _exit (errno == ENOENT ? 127 : 126); + } + add_proc (child); + } + + cmd_argc = initial_argc; + cmd_argv_chars = initial_argv_chars; +} + +/* Add the process with id PID to the list of processes that have + been executed. */ + +static void +add_proc (pid) + pid_t pid; +{ + int i; + + /* Find an empty slot. */ + for (i = 0; i < pids_alloc && pids[i]; i++) + ; + if (i == pids_alloc) + { + if (pids_alloc == 0) + { + pids_alloc = proc_max ? proc_max : 64; + pids = (pid_t *) xmalloc (sizeof (pid_t) * pids_alloc); + } + else + { + pids_alloc *= 2; + pids = (pid_t *) xrealloc (pids, + sizeof (pid_t) * pids_alloc); + } + memset (&pids[i], '\0', sizeof (pid_t) * (pids_alloc - i)); + } + pids[i] = pid; + procs_executing++; + procs_executed++; +} + +/* If ALL is true, wait for all child processes to finish; + otherwise, wait for one child process to finish. + Remove the processes that finish from the list of executing processes. */ + +static void +wait_for_proc (all) + boolean all; +{ + while (procs_executing) + { + int i, status; + + do + { + pid_t pid; + + pid = wait (&status); + if (pid < 0) + error (1, errno, "error waiting for child process"); + + /* Find the entry in `pids' for the child process + that exited. */ + for (i = 0; i < pids_alloc && pid != pids[i]; i++) + ; + } + while (i == pids_alloc); /* A child died that we didn't start? */ + + /* Remove the child from the list. */ + pids[i] = 0; + procs_executing--; + + if (WEXITSTATUS (status) == 126 || WEXITSTATUS (status) == 127) + exit (WEXITSTATUS (status)); /* Can't find or run the command. */ + if (WEXITSTATUS (status) == 255) + error (124, 0, "%s: exited with status 255; aborting", cmd_argv[0]); + if (WIFSTOPPED (status)) + error (125, 0, "%s: stopped by signal %d", cmd_argv[0], WSTOPSIG (status)); + if (WIFSIGNALED (status)) + error (125, 0, "%s: terminated by signal %d", cmd_argv[0], WTERMSIG (status)); + if (WEXITSTATUS (status) != 0) + child_error = 123; + + if (!all) + break; + } +} + +/* Return the value of the number represented in STR. + OPTION is the command line option to which STR is the argument. + If the value does not fall within the boundaries MIN and MAX, + Print an error message mentioning OPTION and exit. */ + +static long +parse_num (str, option, min, max) + char *str; + int option; + long min; + long max; +{ + char *eptr; + long val; + + val = strtol (str, &eptr, 10); + if (eptr == str || *eptr) + { + fprintf (stderr, "%s: invalid number for -%c option\n", + program_name, option); + usage (stderr, 1); + } + else if (val < min) + { + fprintf (stderr, "%s: value for -%c option must be >= %ld\n", + program_name, option, min); + usage (stderr, 1); + } + else if (max >= 0 && val > max) + { + fprintf (stderr, "%s: value for -%c option must be < %ld\n", + program_name, option, max); + usage (stderr, 1); + } + return val; +} + +/* Return how much of ARG_MAX is used by the environment. */ + +static long +env_size (envp) + char **envp; +{ + long len = 0; + + while (*envp) + len += strlen (*envp++) + 1; + + return len; +} + +static void +usage (stream, status) + FILE *stream; + int status; +{ + fprintf (stream, "\ +Usage: %s [-0prtx] [-e[eof-str]] [-i[replace-str]] [-l[max-lines]]\n\ + [-n max-args] [-s max-chars] [-P max-procs] [--null] [--eof[=eof-str]]\n\ + [--replace[=replace-str]] [--max-lines[=max-lines]] [--interactive]\n\ + [--max-chars=max-chars] [--verbose] [--exit] [--max-procs=max-procs]\n\ + [--max-args=max-args] [--no-run-if-empty] [--version] [--help]\n\ + [command [initial-arguments]]\n", + program_name); + exit (status); +} -- 2.11.4.GIT