From f87489ac4fbebdbc73c4b25df25f07204a02f00f Mon Sep 17 00:00:00 2001 From: "William F. Jolitz" Date: Tue, 25 Feb 1992 13:36:31 -0800 Subject: [PATCH] 386BSD 0.1 development Work on file usr/othersrc/public/bc-1.01/COPYING Work on file usr/othersrc/public/bc-1.01/Install Work on file usr/othersrc/public/bc-1.01/bc.y Work on file usr/othersrc/public/bc-1.01/const.h Work on file usr/othersrc/public/bc-1.01/version.h Work on file usr/othersrc/public/bc-1.01/execute.c Work on file usr/othersrc/public/bc-1.01/global.c Work on file usr/othersrc/public/bc-1.01/load.c Work on file usr/othersrc/public/bc-1.01/global.h Work on file usr/othersrc/public/bc-1.01/main.c Work on file usr/othersrc/public/bc-1.01/number.c Work on file usr/othersrc/public/bc-1.01/storage.c Work on file usr/othersrc/public/bc-1.01/number.h Work on file usr/othersrc/public/bc-1.01/proto.h Work on file usr/othersrc/public/bc-1.01/vfprintf.c Work on file usr/othersrc/public/bc-1.01/scan.l Work on file usr/othersrc/public/bc-1.01/util.c Work on file usr/othersrc/public/bc-1.01/README Work on file usr/othersrc/public/bc-1.01/bc.1 Work on file usr/othersrc/public/bc-1.01/sbc.y Work on file usr/othersrc/public/bc-1.01/fix_math.h Work on file usr/othersrc/public/bc-1.01/libmath.b Work on file usr/othersrc/public/bc-1.01/configure Work on file usr/othersrc/public/bc-1.01/ChangeLog Work on file usr/othersrc/public/bc-1.01/Examples/ckbook.b Work on file usr/othersrc/public/bc-1.01/Examples/primes.b Work on file usr/othersrc/public/bc-1.01/Test/array.b Work on file usr/othersrc/public/bc-1.01/Test/atan.b Work on file usr/othersrc/public/bc-1.01/Test/aryprm.b Work on file usr/othersrc/public/bc-1.01/Test/div.b Work on file usr/othersrc/public/bc-1.01/Test/exp.b Work on file usr/othersrc/public/bc-1.01/Test/checklib.b Work on file usr/othersrc/public/bc-1.01/Test/fact.b Work on file usr/othersrc/public/bc-1.01/Test/ln.b Work on file usr/othersrc/public/bc-1.01/Test/jn.b Work on file usr/othersrc/public/bc-1.01/Test/mul.b Work on file usr/othersrc/public/bc-1.01/Test/sine.b Work on file usr/othersrc/public/bc-1.01/Test/raise.b Work on file usr/othersrc/public/bc-1.01/Test/sqrt.b Work on file usr/othersrc/public/bc-1.01/Test/timetest Work on file usr/othersrc/public/bc-1.01/Test/testfn.b Work on file usr/othersrc/public/bc-1.01/config.h Work on file usr/othersrc/public/bc-1.01/scan.c Work on file usr/src/usr.bin/sed/ChangeLog Work on file usr/src/usr.bin/sed/README Work on file usr/src/usr.bin/sed/getopt.h Work on file usr/src/usr.bin/sed/regex.h Work on file usr/src/usr.bin/sed/sed.c Work on file usr/src/usr.bin/sed/utils.c Work on file usr/src/usr.bin/sed/regex.c Work on file usr/src/usr.bin/sed/getopt1.c Work on file usr/src/usr.bin/sed/getopt.c Work on file usr/src/usr.bin/sed/Makefile.gnu Work on file usr/src/usr.bin/tar/create.c Work on file usr/src/usr.bin/tar/extract.c Work on file usr/src/usr.bin/tar/getoldopt.c Work on file usr/src/usr.bin/tar/update.c Work on file usr/src/usr.bin/tar/gnu.c Work on file usr/src/usr.bin/tar/mangle.c Work on file usr/src/usr.bin/tar/list.c Work on file usr/src/usr.bin/tar/version.c Work on file usr/src/usr.bin/tar/names.c Work on file usr/src/usr.bin/tar/diffarch.c Work on file usr/src/usr.bin/tar/port.c Work on file usr/src/usr.bin/tar/wildmat.c Work on file usr/src/usr.bin/tar/getopt1.c Work on file usr/src/usr.bin/tar/getopt.c Work on file usr/src/usr.bin/tar/README Work on file usr/src/usr.bin/tar/regex.c Work on file usr/src/usr.bin/tar/ChangeLog Work on file usr/src/usr.bin/tar/port.h Work on file usr/src/usr.bin/tar/rmt.h Work on file usr/src/usr.bin/tar/open3.h Work on file usr/src/usr.bin/tar/regex.h Work on file usr/src/usr.bin/tar/getdate.y Work on file usr/src/usr.bin/tar/getopt.h Work on file usr/src/usr.bin/tar/rtape_lib.c Co-Authored-By: Lynne Greer Jolitz Synthesized-from: 386BSD-0.1 --- usr/othersrc/public/bc-1.01/COPYING | 341 ++ usr/othersrc/public/bc-1.01/ChangeLog | 47 + usr/othersrc/public/bc-1.01/Examples/ckbook.b | 16 + usr/othersrc/public/bc-1.01/Examples/primes.b | 32 + usr/othersrc/public/bc-1.01/Install | 107 + usr/othersrc/public/bc-1.01/README | 55 + usr/othersrc/public/bc-1.01/Test/array.b | 14 + usr/othersrc/public/bc-1.01/Test/aryprm.b | 16 + usr/othersrc/public/bc-1.01/Test/atan.b | 3 + usr/othersrc/public/bc-1.01/Test/checklib.b | 109 + usr/othersrc/public/bc-1.01/Test/div.b | 8 + usr/othersrc/public/bc-1.01/Test/exp.b | 3 + usr/othersrc/public/bc-1.01/Test/fact.b | 13 + usr/othersrc/public/bc-1.01/Test/jn.b | 6 + usr/othersrc/public/bc-1.01/Test/ln.b | 3 + usr/othersrc/public/bc-1.01/Test/mul.b | 7 + usr/othersrc/public/bc-1.01/Test/raise.b | 3 + usr/othersrc/public/bc-1.01/Test/sine.b | 3 + usr/othersrc/public/bc-1.01/Test/sqrt.b | 4 + usr/othersrc/public/bc-1.01/Test/testfn.b | 47 + usr/othersrc/public/bc-1.01/Test/timetest | 14 + usr/othersrc/public/bc-1.01/bc.1 | 713 +++++ usr/othersrc/public/bc-1.01/bc.y | 614 ++++ usr/othersrc/public/bc-1.01/config.h | 4 + usr/othersrc/public/bc-1.01/configure | 119 + usr/othersrc/public/bc-1.01/const.h | 87 + usr/othersrc/public/bc-1.01/execute.c | 780 +++++ usr/othersrc/public/bc-1.01/fix_math.h | 8 + usr/othersrc/public/bc-1.01/global.c | 42 + usr/othersrc/public/bc-1.01/global.h | 108 + usr/othersrc/public/bc-1.01/libmath.b | 255 ++ usr/othersrc/public/bc-1.01/load.c | 322 ++ usr/othersrc/public/bc-1.01/main.c | 204 ++ usr/othersrc/public/bc-1.01/number.c | 1402 +++++++++ usr/othersrc/public/bc-1.01/number.h | 60 + usr/othersrc/public/bc-1.01/proto.h | 165 + usr/othersrc/public/bc-1.01/sbc.y | 448 +++ usr/othersrc/public/bc-1.01/scan.c | 1313 ++++++++ usr/othersrc/public/bc-1.01/scan.l | 176 ++ usr/othersrc/public/bc-1.01/storage.c | 967 ++++++ usr/othersrc/public/bc-1.01/util.c | 794 +++++ usr/othersrc/public/bc-1.01/version.h | 3 + usr/othersrc/public/bc-1.01/vfprintf.c | 13 + usr/src/usr.bin/sed/ChangeLog | 194 ++ usr/src/usr.bin/sed/Makefile.gnu | 84 + usr/src/usr.bin/sed/README | 48 + usr/src/usr.bin/sed/getopt.c | 594 ++++ usr/src/usr.bin/sed/getopt.h | 102 + usr/src/usr.bin/sed/getopt1.c | 158 + usr/src/usr.bin/sed/regex.c | 2781 ++++++++++++++++ usr/src/usr.bin/sed/regex.h | 253 ++ usr/src/usr.bin/sed/sed.c | 1554 +++++++++ usr/src/usr.bin/sed/utils.c | 359 +++ usr/src/usr.bin/tar/ChangeLog | 898 ++++++ usr/src/usr.bin/tar/README | 50 + usr/src/usr.bin/tar/create.c | 1302 ++++++++ usr/src/usr.bin/tar/diffarch.c | 686 ++++ usr/src/usr.bin/tar/extract.c | 743 +++++ usr/src/usr.bin/tar/getdate.y | 896 ++++++ usr/src/usr.bin/tar/getoldopt.c | 88 + usr/src/usr.bin/tar/getopt.c | 596 ++++ usr/src/usr.bin/tar/getopt.h | 102 + usr/src/usr.bin/tar/getopt1.c | 160 + usr/src/usr.bin/tar/gnu.c | 630 ++++ usr/src/usr.bin/tar/list.c | 721 +++++ usr/src/usr.bin/tar/mangle.c | 226 ++ usr/src/usr.bin/tar/names.c | 135 + usr/src/usr.bin/tar/open3.h | 69 + usr/src/usr.bin/tar/port.c | 1324 ++++++++ usr/src/usr.bin/tar/port.h | 47 + usr/src/usr.bin/tar/regex.c | 2783 +++++++++++++++++ usr/src/usr.bin/tar/regex.h | 257 ++ usr/src/usr.bin/tar/rmt.h | 77 + usr/src/usr.bin/tar/rtape_lib.c | 649 ++++ usr/src/usr.bin/tar/update.c | 534 ++++ usr/src/usr.bin/tar/version.c | 20 + usr/src/usr.bin/tar/wildmat.c | 151 + 77 files changed, 28719 insertions(+) create mode 100644 usr/othersrc/public/bc-1.01/COPYING create mode 100644 usr/othersrc/public/bc-1.01/ChangeLog create mode 100644 usr/othersrc/public/bc-1.01/Examples/ckbook.b create mode 100644 usr/othersrc/public/bc-1.01/Examples/primes.b create mode 100644 usr/othersrc/public/bc-1.01/Install create mode 100644 usr/othersrc/public/bc-1.01/README create mode 100644 usr/othersrc/public/bc-1.01/Test/array.b create mode 100644 usr/othersrc/public/bc-1.01/Test/aryprm.b create mode 100644 usr/othersrc/public/bc-1.01/Test/atan.b create mode 100644 usr/othersrc/public/bc-1.01/Test/checklib.b create mode 100644 usr/othersrc/public/bc-1.01/Test/div.b create mode 100644 usr/othersrc/public/bc-1.01/Test/exp.b create mode 100644 usr/othersrc/public/bc-1.01/Test/fact.b create mode 100644 usr/othersrc/public/bc-1.01/Test/jn.b create mode 100644 usr/othersrc/public/bc-1.01/Test/ln.b create mode 100644 usr/othersrc/public/bc-1.01/Test/mul.b create mode 100644 usr/othersrc/public/bc-1.01/Test/raise.b create mode 100644 usr/othersrc/public/bc-1.01/Test/sine.b create mode 100644 usr/othersrc/public/bc-1.01/Test/sqrt.b create mode 100644 usr/othersrc/public/bc-1.01/Test/testfn.b create mode 100755 usr/othersrc/public/bc-1.01/Test/timetest create mode 100644 usr/othersrc/public/bc-1.01/bc.1 create mode 100644 usr/othersrc/public/bc-1.01/bc.y create mode 100644 usr/othersrc/public/bc-1.01/config.h create mode 100755 usr/othersrc/public/bc-1.01/configure create mode 100644 usr/othersrc/public/bc-1.01/const.h create mode 100644 usr/othersrc/public/bc-1.01/execute.c create mode 100755 usr/othersrc/public/bc-1.01/fix_math.h create mode 100644 usr/othersrc/public/bc-1.01/global.c create mode 100644 usr/othersrc/public/bc-1.01/global.h create mode 100644 usr/othersrc/public/bc-1.01/libmath.b create mode 100644 usr/othersrc/public/bc-1.01/load.c create mode 100644 usr/othersrc/public/bc-1.01/main.c create mode 100644 usr/othersrc/public/bc-1.01/number.c create mode 100644 usr/othersrc/public/bc-1.01/number.h create mode 100644 usr/othersrc/public/bc-1.01/proto.h create mode 100644 usr/othersrc/public/bc-1.01/sbc.y create mode 100644 usr/othersrc/public/bc-1.01/scan.c create mode 100644 usr/othersrc/public/bc-1.01/scan.l create mode 100644 usr/othersrc/public/bc-1.01/storage.c create mode 100644 usr/othersrc/public/bc-1.01/util.c create mode 100644 usr/othersrc/public/bc-1.01/version.h create mode 100644 usr/othersrc/public/bc-1.01/vfprintf.c create mode 100644 usr/src/usr.bin/sed/ChangeLog create mode 100644 usr/src/usr.bin/sed/Makefile.gnu create mode 100644 usr/src/usr.bin/sed/README create mode 100644 usr/src/usr.bin/sed/getopt.c create mode 100644 usr/src/usr.bin/sed/getopt.h create mode 100644 usr/src/usr.bin/sed/getopt1.c create mode 100644 usr/src/usr.bin/sed/regex.c create mode 100644 usr/src/usr.bin/sed/regex.h create mode 100644 usr/src/usr.bin/sed/sed.c create mode 100644 usr/src/usr.bin/sed/utils.c create mode 100644 usr/src/usr.bin/tar/ChangeLog create mode 100644 usr/src/usr.bin/tar/README create mode 100644 usr/src/usr.bin/tar/create.c create mode 100644 usr/src/usr.bin/tar/diffarch.c create mode 100644 usr/src/usr.bin/tar/extract.c create mode 100644 usr/src/usr.bin/tar/getdate.y create mode 100644 usr/src/usr.bin/tar/getoldopt.c create mode 100644 usr/src/usr.bin/tar/getopt.c create mode 100644 usr/src/usr.bin/tar/getopt.h create mode 100644 usr/src/usr.bin/tar/getopt1.c create mode 100644 usr/src/usr.bin/tar/gnu.c create mode 100644 usr/src/usr.bin/tar/list.c create mode 100644 usr/src/usr.bin/tar/mangle.c create mode 100644 usr/src/usr.bin/tar/names.c create mode 100644 usr/src/usr.bin/tar/open3.h create mode 100644 usr/src/usr.bin/tar/port.c create mode 100644 usr/src/usr.bin/tar/port.h create mode 100644 usr/src/usr.bin/tar/regex.c create mode 100644 usr/src/usr.bin/tar/regex.h create mode 100644 usr/src/usr.bin/tar/rmt.h create mode 100644 usr/src/usr.bin/tar/rtape_lib.c create mode 100644 usr/src/usr.bin/tar/update.c create mode 100644 usr/src/usr.bin/tar/version.c create mode 100644 usr/src/usr.bin/tar/wildmat.c diff --git a/usr/othersrc/public/bc-1.01/COPYING b/usr/othersrc/public/bc-1.01/COPYING new file mode 100644 index 0000000000..86cf81acb8 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/COPYING @@ -0,0 +1,341 @@ + + 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/usr/othersrc/public/bc-1.01/ChangeLog b/usr/othersrc/public/bc-1.01/ChangeLog new file mode 100644 index 0000000000..1882101db6 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/ChangeLog @@ -0,0 +1,47 @@ +Mon Nov 25 13:11:17 1991 Phil Nelson (phil at cs.wwu.edu) + + * Changed version number in version.h to 1.01 with current date. + + * Changed LIBFILE definition in Makefile. + + * Added a recursive function example to bc.1. + +Sun Nov 24 21:24:01 1991 Phil Nelson (phil at cs.wwu.edu) + + * Changed the Makefile to make sure configure is run first. + Added the $(CC) the configure call. Moved some defines + toward the front of the Makefile to make sure they are + read by installers. Also added SUBDIRS variable and updated + the GNU distribution to include the subdirectories. Included + math.h in the distribution for use by configure. Included + ChangeLog in the distribution. + + * Split the README into README and Install. Changed Install + to have current information. Documented the STRINGS_H define. + Updated the version number in README. + + * Added a check for in configure. + +Fri Nov 22 15:06:32 1991 Phil Nelson (phil at cs.wwu.edu) + + * Changed configure to check for varargs.h first. Also, added + checks to see if long strings (math.h) are accepted by the + C compiler. Also added parameters to configure. + + * Deleted #include from proto.h. Also made only + ANSI C compilers include . + + * Changed the Makefile to have the install bin directory be + /usr/local/bin and the install lib directory be /usr/local/lib. + + * Changed some files in the Test directory to eliminate the + = form that some older bcs don't like. + + * Made some small corrections in bc.1. + +Tue Oct 29 10:06:32 1991 Phil Nelson (phil at cs.wwu.edu) + + * Called current version 1.00. + + * Submitted GNU bc-1.00 to comp.sources.reviewed + diff --git a/usr/othersrc/public/bc-1.01/Examples/ckbook.b b/usr/othersrc/public/bc-1.01/Examples/ckbook.b new file mode 100644 index 0000000000..5815ea0207 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Examples/ckbook.b @@ -0,0 +1,16 @@ +scale=2 +print "\nCheck book program!\n" +print " Remember, deposits are negative transactions.\n" +print " Exit by a 0 transaction.\n\n" + +print "Initial balance? "; bal = read() +bal /= 1 +print "\n" +while (1) { + "current balance = "; bal + "transaction? "; trans = read() + if (trans == 0) break; + bal -= trans + bal /= 1 +} +quit diff --git a/usr/othersrc/public/bc-1.01/Examples/primes.b b/usr/othersrc/public/bc-1.01/Examples/primes.b new file mode 100644 index 0000000000..4bef8e8cd6 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Examples/primes.b @@ -0,0 +1,32 @@ + +/* An example that finds all primes between 2 and limit. */ + +define primes (limit) { + auto prime[], num, p, root, i + + prime[1] = 2; + prime[2] = 3; + num = 2; + if (limit >= 2) print "prime 1 = 2\n" + if (limit >= 3) print "prime 2 = 3\n"; + scale = 0; + + for ( p=5; p <= limit; p += 2) { + root = sqrt(p); + isprime = 1; + for ( i = 1; i < num && prime[i] <= root; i++ ) { + if ( p % prime[i] == 0 ) { + isprime = 0; + break; + } + } + if (isprime) { + num += 1; + prime [num] = p; + print "prime ", num, " = ", p, "\n" + } + } +} + + +print "\ntyping 'primes (10)' will print all primes less than 10.\n" diff --git a/usr/othersrc/public/bc-1.01/Install b/usr/othersrc/public/bc-1.01/Install new file mode 100644 index 0000000000..fb0162820b --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Install @@ -0,0 +1,107 @@ + +INSTALLATION +------------ + + a) Make sure you have a working vsprintf and vfprintf in your library. + (If your system does not have them in your library, you can get + a copy of the BSD4.3-tahoe stdio that is freely copyable. It + is available from uunet.uu.net. For Minix systems, a vsprintf.c + is included.) + + b) EDIT the Makefile for the correct definitions. Read the comments in + the the Makefile and make the changes needed. (A "full" explanation + of all the C defines are given at the end of this file.) If your are + on a MINIX machine, make sure the correct definitions for $O, $CFLAGS, + and $LDFLAGS are uncommented in the makefile. + NOTE: "configure" is a shell scritp that is automatically run by make + to configure the bc source to your operating system. You should still + look at the Makefile. + + d) "make derived" (If you need it.) + The distribution contains the files bc.c.dist y.tab.h.dist and + scan.c.dist. These are provided so that you do not need a working + yacc and flex (lex). This program was designed to work with the free + programs byacc (berkeley yacc) and flex. It should work with other + versions of yacc and lex, but it is not guaranteed to work. + + If you do not have yacc or flex, give the command to get the *.dist + files to their proper place. This insures that make will not try + to make these derived files from their original source. Use the + "make derived" before trying to compile bc. + + e) "make" + compiles bc + + f) "make install" + installs bc and libmath.b (if needed) in their directories. + + g) Use bc! + + +DEFINES +------- + + The following the are some defines that are automatically generated + by the configure script. In most cases, you do not need to deal + with them. The configure script is automatically run by make. + + BC_MATH_FILE=$(LIBFILE) + This defines the location of the math library file. If you + use this definition, the math library is read from LIBFILE. + If you do not define this, bc will include a "compiled" form + of the math library with the executable and will not need to + read any external file if the -l flag is given. + + VARARGS + The variable args mechanism is assumed to be ANSI stdarg. + Use -DVARARGS for Ultrix, SUN-OS, BSD, and other UNIX versions that + use traditional varargs, + For MINIX and ANSI compilers (gcc et al) do NOT use -DVARARGS. + + NO_LIMITS + Use this for systems without the /usr/include/limits.h file. + (BSD4.3 is one.) You should check the values of INT_MAX and + LONG_MAX in the file const.h if you use -DNO_LIMITS. + + NO_UNISTD + Use this for systems without /usr/include/unistd.h. + + -DNO_STDLIB + Use this for systems without /usr/include/stdlib.h. + + STRINGS_H + Include the file instead of . + + + The following defines may need to be added by hand to the Makefile if + you want them. They are not generated by the configure script. + + -DOLD_EQ_OP + Causes bc to recognize = as the same as =. The = is + the old style. It makes "a =- 1" ambiguous. With OLD_EQ_OP defined + it is equivalent to "a -= 1" and with OLD_EQ_OP undefined it is + equivalent to "a = -1". + + -DSMALL_BUF + Use this IF you are using flex AND you have a systems with limited + memory space. If you use this, you should also use -DBC_MATH_FILE. + This is "standard" for MINIX systems running on the IBM_PC. + + -DSHORTNAMES + If your compiler keeps a limited number of characters from names, + you should include this define. It is needed for the K&R compiler + on MINIX and is automatically added for MINIX on the IBM_PC. + + -DDEBUG=n + Compile the debugging code. The higher the number, the more + debugging code. The degugging code is not very good. + + +POSSIBLE PROBLEMS +----------------- + + There might be some possible problems compiling due to different + versions of the header files. scan.c may require to edit out some + definitions of malloc and free. Others should compile with the + correct set of defines. + diff --git a/usr/othersrc/public/bc-1.01/README b/usr/othersrc/public/bc-1.01/README new file mode 100644 index 0000000000..c445d47320 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/README @@ -0,0 +1,55 @@ +Program: GNU bc +Author: Philip A. Nelson +E-mail: phil@cs.wwu.edu +OS: UNIX (BSD, System V, MINIX, POSIX) +Copying: GNU GPL version 2 +Copyright holder: Free Software Foundation, Inc. +Version: bc version 1.01 +Required: vsprintf and vfprintf routines. +Machines: It has been compiled and run on the following environments: + BSD4.3 (VAX 11) + MINIX 1.5 (IBM PC, both K&R and ANSI compilers) + MINIX 1.5 (pc532) + SUN-OS 4.1 (SUN 3 and SUN 4) + SVR3V5 (Motorola 68K) + SVR3.2 (3B2) + SVR4.0.2 (a 386 box) + ULTRIX 4.1 (DEC 5000) + UTS (Amdahl) + +bc is an arbitrary precision numeric processing language. Syntax is +similar to C, but differs in many substantial areas. It supports +interactive execution of statements. bc is a utility included in the +POSIX P1003.2/D11 draft standard. + +This version was written to be a POSIX compliant bc processor with +several extensions to the draft standard. Option flags are available +to cause warning or rejection of the extensions to the POSIX standard. +For those who want only POSIX bc with no extensions, a grammar is +provided for exactly the language described in the POSIX document. +The grammar (sbc.y) comes from the POSIX document. The Makefile +contains rules to make sbc. (for Standard BC) + +Since the POSIX document does not specify how bc must be implemented, +this version does not use the historical method of having bc be a +compiler for the dc calculator. This version has a single executable +that both compiles the language and runs the a resulting "byte code". +The "byte code" is NOT the dc language. + +Also, included in the initial distribution is the library file +vfprintf.c for MINIX systems. My minix 1.5 did not have this file. +Also, you should verify that vsprintf.c works correctly on your +system. + +The extensions add some features I think are missing. The major +changes and additions for bc are (a) names are allowed to be full +identifiers ([a-z][a-z0-9_]*), (b) addition of the &&, ||, and ! +operators, (c) allowing comparison and boolean operations in any +expression, (d) addition of an else clause to the if statement, (e) +addition of a new standard function "read()" that reads a number from +the standard input under program control, (f) passing of arrays as +parameters by variable, (g) addition of the "halt" statement that is +an executable statement unlike the quit (i.e. "if (1 == 0) quit" will +halt bc but "if (1 == 0) halt" will not halt bc.), and (h) the +addition of the special variable "last" that is assigned the value of +each print as the number is printed. diff --git a/usr/othersrc/public/bc-1.01/Test/array.b b/usr/othersrc/public/bc-1.01/Test/array.b new file mode 100644 index 0000000000..a0341ec7d7 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/array.b @@ -0,0 +1,14 @@ +"This tests arrays! +" +define p(x,y) { + auto i; + for (i=x; i= t) { + "Bad Scales. Try again. +"; return; + } + + for (i = x; i < y; i += d) { + scale = s; + u = f(i); + scale = t; + v = f(i); + scale = s; + w = v / 1; + b += 1; + if (u != w) { + c += 1; +" +Failed: +" + " index = "; i; + " val1 = "; u; + " val2 = "; v; +" +" + } + } + +" +Total tests: "; b; +" +Total failures: "; c; +" +Percent failed: "; scale = 2; c*100/b; + +} + + +" +Checking e(x)" +define f(x) { + return (e(x)) +} +" +scale = 10" +j = t(-50,50,1,10,14) +" +scale = 20" +j = t(-50,50,1,20,24) + +" +Checking l(x)" +define f(x) { + return (l(x)) +} +" +scale = 10" +j = t(1,10000,100,10,14) +" +scale = 20" +j = t(1,10000,100,20,24) + +" +Checking s(x)" +define f(x) { + return (s(x)) +} +" +scale = 10" +j = t(0,8*a(1),.01,10,14) +" +scale = 20" +j = t(1,8*a(1),.01,20,24) + +" +Checking a(x)" +define f(x) { + return (a(x)) +} +" +scale = 10" +j = t(-100,100,1,10,14) +" +scale = 20" +j = t(-100,100,1,20,24) + +" +Checking j(n,x)" +define f(x) { + return (j(n,x)) +} +" +n=0, scale=10" +n=0 +j = t(0,30,.1,10,14) +" +n=1, scale=10" +n=1 +j = t(0,30,.1,10,14) +" +n=0, scale=20" +n=0 +j = t(0,30,.1,20,24) +" +n=1, scale=20" +n=1 +j = t(0,30,.1,20,24) + diff --git a/usr/othersrc/public/bc-1.01/Test/div.b b/usr/othersrc/public/bc-1.01/Test/div.b new file mode 100644 index 0000000000..3c7d377dca --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/div.b @@ -0,0 +1,8 @@ +scale = 20 +a=2/3 +for (i=0; i<1000; i++) { + for (j=1; j<100; j++) b=a/j +} +b +quit + diff --git a/usr/othersrc/public/bc-1.01/Test/exp.b b/usr/othersrc/public/bc-1.01/Test/exp.b new file mode 100644 index 0000000000..ed0e536384 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/exp.b @@ -0,0 +1,3 @@ +for (a=0; a<150; a++) x=e(a) +x +quit diff --git a/usr/othersrc/public/bc-1.01/Test/fact.b b/usr/othersrc/public/bc-1.01/Test/fact.b new file mode 100644 index 0000000000..8d1474702b --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/fact.b @@ -0,0 +1,13 @@ +define f (x) { + + if (x<=1) return(1) + return (f(x-1)*x) +} + +"Here we go" +for (a=1; a<100; a++) b+=f(a)/a +" +" +"b=";b +quit + diff --git a/usr/othersrc/public/bc-1.01/Test/jn.b b/usr/othersrc/public/bc-1.01/Test/jn.b new file mode 100644 index 0000000000..80ac915cc3 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/jn.b @@ -0,0 +1,6 @@ +scale = 30 +for (a=0; a<5; a=a+2) { + for (b=0; b<100; b=b+10) x=j(a,b) +} +x +quit diff --git a/usr/othersrc/public/bc-1.01/Test/ln.b b/usr/othersrc/public/bc-1.01/Test/ln.b new file mode 100644 index 0000000000..00a1deb781 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/ln.b @@ -0,0 +1,3 @@ +for (a=1; a<10000000000000000000000000000; a = a*2) x=l(a) +x +quit diff --git a/usr/othersrc/public/bc-1.01/Test/mul.b b/usr/othersrc/public/bc-1.01/Test/mul.b new file mode 100644 index 0000000000..1970ed1313 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/mul.b @@ -0,0 +1,7 @@ +scale = 20 +for (i=0; i<1000; i++) { + for (j=1; j<100; j++) b=i*j +} +b +quit + diff --git a/usr/othersrc/public/bc-1.01/Test/raise.b b/usr/othersrc/public/bc-1.01/Test/raise.b new file mode 100644 index 0000000000..a885815199 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/raise.b @@ -0,0 +1,3 @@ +for (i=0; i<1000; i++) a = 2^i; +a +quit diff --git a/usr/othersrc/public/bc-1.01/Test/sine.b b/usr/othersrc/public/bc-1.01/Test/sine.b new file mode 100644 index 0000000000..8dae0b6dd8 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/sine.b @@ -0,0 +1,3 @@ +for (i=0; i<8*a(1); i=i+.01) x=s(i) +x +quit diff --git a/usr/othersrc/public/bc-1.01/Test/sqrt.b b/usr/othersrc/public/bc-1.01/Test/sqrt.b new file mode 100644 index 0000000000..6365794b2f --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/sqrt.b @@ -0,0 +1,4 @@ +scale = 20 +for (a=1; a<500; a++) r=sqrt(a) +r +quit diff --git a/usr/othersrc/public/bc-1.01/Test/testfn.b b/usr/othersrc/public/bc-1.01/Test/testfn.b new file mode 100644 index 0000000000..7578fc5d45 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/testfn.b @@ -0,0 +1,47 @@ +/* This function "t" tests the function "f" to see if computing at + two different scales has much effect on the accuracy. + test from f(x) to f(y) incrementing the index by d. f(i) is + computed at two scales, scale s and then scale t, where t>s. + the result from scale t is divided by 1 at scale s and the + results are compared. If they are different, the function is + said to have failed. It will then print out the value of i + (called index) and the two original values val1 (scale s) and + val2 (scale t) */ + +define t (x,y,d,s,t) { + auto u, v, w, i, b, c; + + if (s >= t) { + "Bad Scales. Try again. +"; return; + } + + for (i = x; i < y; i += d) { + scale = s; + u = f(i); + scale = t; + v = f(i); + scale = s; + w = v / 1; + b += 1; + if (u != w) { + c += 1; +" +Failed: +" + " index = "; i; + " val1 = "; u; + " val2 = "; v; +" +" + } + } + +" +Total tests: "; b; +" +Total failures: "; c; +" +Percent failed: "; scale = 2; c*100/b; + +} diff --git a/usr/othersrc/public/bc-1.01/Test/timetest b/usr/othersrc/public/bc-1.01/Test/timetest new file mode 100755 index 0000000000..90da6ab7ad --- /dev/null +++ b/usr/othersrc/public/bc-1.01/Test/timetest @@ -0,0 +1,14 @@ +#!/bin/sh +# +# Time the functions. +# +BC=../bc +SYSBC=/usr/bin/bc +for file in exp.b ln.b sine.b atan.b jn.b mul.b div.b raise.b sqrt.b +do +for prog in $BC $SYSBC +do +echo Timing $file with $prog +time $prog -l $file +done +done diff --git a/usr/othersrc/public/bc-1.01/bc.1 b/usr/othersrc/public/bc-1.01/bc.1 new file mode 100644 index 0000000000..334cf86993 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/bc.1 @@ -0,0 +1,713 @@ +.\" +.\" bc.1 - the *roff document processor source for the bc manual +.\" +.\" This file is part of bc written for MINIX. +.\" 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 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; see the file COPYING. If not, write to +.\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" You may contact the author by: +.\" e-mail: phil@cs.wwu.edu +.\" us-mail: Philip A. Nelson +.\" Computer Science Department, 9062 +.\" Western Washington University +.\" Bellingham, WA 98226-9062 +.\" +.\" +.TH bc 1 .\" "Command Manual" v1.01 "Nov 22, 1991" +.SH NAME +bc - An arbitrary precision calculator language +.SH SYNTAX +\fBbc\fR [ \fB-lws\fR ] [ \fI file ...\fR ] +.SH DESCRIPTION +\fBbc\fR is a language that supports arbitrary precision numbers +with interactive execution of statements. There are some similarities +in the syntax to the C programming language. +A standard math library is available by command line option. +If requested, the math library is defined before processing any files. +\fBbc\fR starts by processing code from all the files listed +on the command line in the order listed. After all files have been +processed, \fBbc\fR reads from the standard input. All code is +executed as it is read. (If a file contains a command to halt the +processor, \fBbc\fR will never read from the standard input.) +.PP +This version of \fBbc\fR contains several extensions beyond +traditional \fBbc\fR implementations and the POSIX draft standard. +Command line options can cause these extensions to print a warning +or to be rejected. This +document describes the language accepted by this processor. +Extensions will be identified as such. +.SS OPTIONS +.IP -l +Define the standard math library. +.IP -w +Give warnings for extensions to POSIX \fBbc\fR. +.IP -s +Process exactly the POSIX \fBbc\fR language. +.SS NUMBERS +The most basic element in \fBbc\fR is the number. Numbers are +arbitrary precision numbers. This precision is both in the integer +part and the fractional part. All numbers are represented internally +in decimal and all computation is done in decimal. (This version +truncates results from divide and multiply operations.) There are two +attributes of numbers, the length and the scale. The length is the +total number of significant decimal digits in a number and the scale +is the total number of decimal digits after the decimal point. For +example: +.nf +.RS + .000001 has a length of 6 and scale of 6. + 1935.000 has a length of 7 and a scale of 3. +.RE +.fi +.SS VARIABLES +Numbers are stored in two types of variables, simple variables and +arrays. Both simple variables and array variables are named. Names +begin with a letter followed by any number of letters, digits and +underscores. All letters must be lower case. (Full alpha-numeric +names are an extension. In POSIX \fBbc\fR all names are a single +lower case letter.) The type of variable is clear by the context +because all array variable names will be followed by brackets ([]). +.PP +There are four special variables, \fBscale, ibase, obase,\fR and +\fBlast\fR. \fBscale\fR defines how some operations use digits after the +decimal point. The default value of \fBscale\fR is 0. \fBibase\fR +and \fBobase\fR define the conversion base for input and output +numbers. The default for both input and output is base 10. +\fBlast\fR (an extension) is a variable that has the value of the last +printed number. These will be discussed in further detail where +appropriate. All of these variables may have values assigned to them +as well as used in expressions. +.SS COMMENTS +Comments in \fBbc\fR start with the characters \fB/*\fR and end with +the characters \fB*/\fR. Comments may start anywhere and appear as a +single space in the input. (This causes comments to delimit other +input items. For example, a comment can not be found in the middle of +a variable name.) Comments include any newlines (end of line) between +the start and the end of the comment. +.SS EXPRESSIONS +The numbers are manipulated by expressions and statements. Since +the language was designed to be interactive, statements and expressions +are executed as soon as possible. There is no "main" program. Instead, +code is executed as it is encountered. (Functions, discussed in +detail later, are defined when encountered.) +.PP +A simple expression is just a constant. \fBbc\fR converts constants +into internal decimal numbers using the current input base, specified +by the variable \fBibase\fR. (There is an exception in functions.) +The legal values of \fBibase\fR are 2 through 16 (F). Assigning a +value outside this range to \fBibase\fR will result in a value of 2 +or 16. Input numbers may contain the characters 0-9 and A-F. (Note: +They must be capitals. Lower case letters are variable names.) +Single digit numbers always have the value of the digit regardless of +the value of \fBibase\fR. (i.e. A = 10.) For multi-digit numbers, +\fBbc\fR changes all input digits greater or equal to ibase to the +value of \fBibase\fR-1. This makes the number \fBFFF\fR always be +the largest 3 digit number of the input base. +.PP +Full expressions are similar to many other high level languages. +Since there is only one kind of number, there are no rules for mixing +types. Instead, there are rules on the scale of expressions. Every +expression has a scale. This is derived from the scale of original +numbers, the operation performed and in many cases, the value of the +variable \fBscale\fR. Legal values of the variable \fBscale\fR are +0 to the maximum number representable by a C integer. +.PP +In the following descriptions of legal expressions, "expr" refers to a +complete expression and "var" refers to a simple or an array variable. +A simple variable is just a +.RS +\fIname\fR +.RE +and an array variable is specified as +.RS +\fIname\fR[\fIexpr\fR] +.RE +Unless specifically +mentioned the scale of the result is the maximum scale of the +expressions involved. +.IP "- expr" +The result is the negation of the expression. +.IP "++ var" +The variable is incremented by one and the new value is the result of +the expression. +.IP "-- var" +The variable +is decremented by one and the new value is the result of the +expression. +.IP "var ++" + The result of the expression is the value of +the variable and then the variable is incremented by one. +.IP "var --" +The result of the expression is the value of the variable and then +the variable is decremented by one. +.IP "expr + expr" +The result of the expression is the sum of the two expressions. +.IP "expr - expr" +The result of the expression is the difference of the two expressions. +.IP "expr * expr" +The result of the expression is the product of the two expressions. +.IP "expr / expr" +The result of the expression is the quotient of the two expressions. +The scale of the result is the value of the variable \fBscale\fR. +.IP "expr % expr" +The result of the expression is the "remainder" and it is computed in the +following way. To compute a%b, first a/b is computed to \fBscale\fR +digits. That result is used to compute a-(a/b)*b to the scale of the +maximum of \fBscale\fR+scale(b) and scale(a). If \fBscale\fR is set +to zero and both expressions are integers this expression is the +integer remainder function. +.IP "expr ^ expr" +The result of the expression is the value of the first raised to the +second. The second expression must be an integer. (If the second +expression is not an integer, a warning is generated and the +expression is truncated to get an integer value.) The scale of the +result is \fBscale\fR if the exponent is negative. If the exponent +is positive the scale of the result is the minimum of the scale of the +first expression times the value of the exponent and the maximum of +\fBscale\fR and the scale of the first expression. (e.g. scale(a^b) += min(scale(a)*b, max( \fBscale,\fR scale(a))).) It should be noted +that expr^0 will always return the value of 1. +.IP "( expr )" +This alters the standard precedence to force the evaluation of the +expression. +.IP "var = expr" +The variable is assigned the value of the expression. +.IP "var = expr" +This is equivalent to "var = var expr" with the exception that +the "var" part is evaluated only once. This can make a difference if +"var" is an array. +.PP + Relational expressions are a special kind of expression +that always evaluate to 0 or 1, 0 if the relation is false and 1 if +the relation is true. These may appear in any legal expression. +(POSIX bc requires that relational expressions are used only in if, +while, and for statements and that only one relational test may be +done in them.) The relational operators are +.IP "expr1 < expr2" +The result is 1 if expr1 is strictly less than expr2. +.IP "expr1 <= expr2" +The result is 1 if expr1 is less than or equal to expr2. +.IP "expr1 > expr2" +The result is 1 if expr1 is strictly greater than expr2. +.IP "expr1 >= expr2" +The result is 1 if expr1 is greater than or equal to expr2. +.IP "expr1 == expr2" +The result is 1 if expr1 is equal to expr2. +.IP "expr1 != expr2" +The result is 1 if expr1 is not equal to expr2. +.PP +Boolean operations are also legal. (POSIX \fBbc\fR does NOT have +boolean operations). The result of all boolean operations are 0 and 1 +(for false and true) as in relational expressions. The boolean +operators are: +.IP "!expr" +The result is 1 if expr is 0. +.IP "expr && expr" +The result is 1 if both expressions are non-zero. +.IP "expr || expr" +The result is 1 if either expression is non-zero. +.PP +The expression precedence is as follows: (lowest to highest) +.nf +.RS +|| operator, left associative +&& operator, left associative +! operator, nonassociative +Relational operators, left associative +Assignment operator, right associative ++ and - operators, left associative +*, / and % operators, left associative +^ operator, right associative +unary - operator, nonassociative +++ and -- operators, nonassociative +.RE +.fi +.PP +This precedence was chosen so that POSIX compliant \fBbc\fR programs +will run correctly. This will cause the use of the relational and +logical operators to have some unusual behavior when used with +assignment expressions. Consider the expression: +.RS +a = 3 < 5 +.RE +.PP +Most C programmers would assume this would assign the result of "3 < +5" (the value 1) to the variable "a". What this does in \fBbc\fR is +assign the value 3 to the variable "a" and then compare 3 to 5. It is +best to use parenthesis when using relational and logical operators +with the assignment operators. +.PP +There are a few more special expressions that are provided in \fBbc\fR. +These have to do with user defined functions and standard +functions. They all appear as "\fIname\fB(\fIparameters\fB)\fR". +See the section on functions for user defined functions. The standard +functions are: +.IP "length ( expression )" +The value of the length function is the number of significant digits in the +expression. +.IP "read ( )" +The read function (an extension) will read a number from the standard +input, regardless of where the function occurs. Beware, this can +cause problems with the mixing of data and program in the standard input. +The best use for this function is in a previously written program that +needs input from the user, but never allows program code to be input +from the user. The value of the read function is the number read from +the standard input using the current value of the variable +\fBibase\fR for the conversion base. +.IP "scale ( expression )" +The value of the scale function is the number of digits after the decimal +point in the expression. +.IP "sqrt ( expression )" +The value of the sqrt function is the square root of the expression. If +the expression is negative, a run time error is generated. +.SS STATEMENTS +Statements (as in most algebraic languages) provide the sequencing of +expression evaluation. In \fBbc\fR statements are executed "as soon +as possible." Execution happens when a newline in encountered and +there is one or more complete statements. Due to this immediate +execution, newlines are very important in \fBbc\fR. In fact, both a +semicolon and a newline are used as statement separators. An +improperly placed newline will cause a syntax error. Because newlines +are statement separators, it is possible to hide a newline by using +the backslash character. The sequence "\e", where is the +newline appears to \fBbc\fR as whitespace instead of a newline. A +statement list is a series of statements separated by semicolons and +newlines. The following is a list of \fBbc\fR statements and what +they do: (Things enclosed in brackets ([]) are optional parts of the +statement.) +.IP "expression" +This statement does one of two things. If the expression starts with +" ...", it is considered to be an assignment +statement. If the expression is not an assignment statement, the +expression is evaluated and printed to the output. After the number +is printed, a newline is printed. For example, "a=1" is an assignment +statement and "(a=1)" is an expression that has an embedded +assignment. All numbers that are printed are printed in the base +specified by the variable \fBobase\fR. The legal values for \fB +obase\fR are 2 through BC_BASE_MAX. (See the section LIMITS.) For +bases 2 through 16, the usual method of writing numbers is used. For +bases greater than 16, \fBbc\fR uses a multi-character digit method +of printing the numbers where each higher base digit is printed as a +base 10 number. The multi-character digits are separated by spaces. +Each digit contains the number of characters required to represent the +base ten value of "obase-1". Since numbers are of arbitrary +precision, some numbers may not be printable on a single output line. +These long numbers will be split across lines using the "\e" as the +last character on a line. The maximum number of characters printed +per line is 70. Due to the interactive nature of \fBbc\fR printing +a number cause the side effect of assigning the printed value the the +special variable \fBlast\fR. This allows the user to recover the +last value printed without having to retype the expression that +printed the number. Assigning to \fBlast\fR is legal and will +overwrite the last printed value with the assigned value. The newly +assigned value will remain until the next number is printed or another +value is assigned to \fBlast\fR. +.IP "string" +The string is printed to the output. Strings start with a double quote +character and contain all characters until the next double quote character. +All characters are take literally, including any newline. No newline +character is printed after the string. +.IP "\fBprint\fR list" +The print statement (an extension) provides another method of output. +The "list" is a list of strings and expressions separated by commas. +Each string or expression is printed in the order of the list. No +terminating newline is printed. Expressions are evaluated and their +value is printed and assigned the the variable \fBlast\fR. Strings +in the print statement are printed to the output and may contain +special characters. Special characters start with the backslash +character (\e). The special characters recognized by \fBbc\fR are +"b" (bell), "f" (form feed), "n" (newline), "r" (carriage return), "t" +(tab), and "\e" (backslash). Any other character following the +backslash will be ignored. This still does not allow the double quote +character to be part of any string. +.IP "{ statement_list }" +This is the compound statement. It allows multiple statements to be +grouped together for execution. +.IP "\fBif\fR ( expression ) \fBthen\fR statement1 [\fBelse\fR statement2]" +The if statement evaluates the expression and executes statement1 or +statement2 depending on the value of the expression. If the expression +is non-zero, statement1 is executed. If statement2 is present and +the value of the expression is 0, then statement2 is executed. (The +else clause is an extension.) +.IP "\fBwhile\fR ( expression ) statement" +The while statement will execute the statement while the expression +is non-zero. It evaluates the expression before each execution of +the statement. Termination of the loop is caused by a zero +expression value or the execution of a break statement. +.IP "\fBfor\fR ( [expression1] ; [expression2] ; [expression3] ) statement" +The for statement controls repeated execution of the statement. +Expression1 is evaluated before the loop. Expression2 is evaluated +before each execution of the statement. If it is non-zero, the statement +is evaluated. If it is zero, the loop is terminated. After each +execution of the statement, expression3 is evaluated before the reevaluation +of expression2. If expression1 or expression3 are missing, nothing is +evaluated at the point they would be evaluated. +If expression2 is missing, it is the same as substituting +the value 1 for expression2. (The optional expressions are an +extension. POSIX \fBbc\fR requires all three expressions.) +The following is equivalent code for the for statement: +.nf +.RS +expression1; +while (expression2) { + statement; + expression3; +} +.RE +.fi +.IP "\fBbreak\fR" +This statement causes a forced exit of the most recent enclosing while +statement or for statement. +.IP "\fBcontinue\fR" +The continue statement (an extension) causes the most recent enclosing +for statement to start the next iteration. +.IP "\fBhalt\fR" +The halt statement (an extension) is an executed statement that causes +the \fBbc\fR processor to quit only when it is executed. For example, +"if (0 == 1) halt" will not cause \fBbc\fR to terminate because the halt is +not executed. +.IP "\fBreturn\fR" +Return the value 0 from a function. (See the section on functions.) +.IP "\fBreturn\fR ( expression )" +Return the value of the expression from a function. (See the section on +functions.) +.SS PSEUDO STATEMENTS +These statements are not statements in the traditional sense. They are +not executed statements. Their function is performed at "compile" time. +.IP "\fBlimits\fR" +Print the local limits enforced by the local version of \fBbc\fR. This +is an extension. +.IP "\fBquit\fR" +When the quit statement is read, the \fBbc\fR processor +is terminated, regardless of where the quit statement is found. For +example, "if (0 == 1) quit" will cause \fBbc\fR to terminate. +.IP "\fBwarranty\fR" +Print a longer warranty notice. This is an extension. +.SS FUNCTIONS +Functions provide a method of defining a computation that can be executed +later. Functions in +.B bc +always compute a value and return it to the caller. Function definitions +are "dynamic" in the sense that a function is undefined until a definition +is encountered in the input. That definition is then used until another +definition function for the same name is encountered. The new definition +then replaces the older definition. A function is defined as follows: +.nf +.RS +\fBdefine \fIname \fB( \fIparameters \fB) { \fInewline +\fI auto_list statement_list \fB}\fR +.RE +.fi +A function call is just an expression of the form +"\fIname\fB(\fIparameters\fB)\fR". +.PP +Parameters are numbers or arrays (an extension). In the function definition, +zero or more parameters are defined by listing their names separated by +commas. Numbers are only call by value parameters. Arrays are only +call by variable. Arrays are specified in the parameter definition by +the notation "\fIname\fB[]\fR". In the function call, actual parameters +are full expressions for number parameters. The same notation is used +for passing arrays as for defining array parameters. The named array is +passed by variable to the function. Since function definitions are dynamic, +parameter numbers and types are checked when a function is called. Any +mismatch in number or types of parameters will cause a runtime error. +A runtime error will also occur for the call to an undefined function. +.PP +The \fIauto_list\f is an optional list of variables that are for +"local" use. The syntax of the auto list (if present) is "\fBauto +\fIname\fR, ... ;". (The semicolon is optional.) Each \fIname\fR is +the name of an auto variable. Arrays may be specified by using the +same notation as used in parameters. These variables have their +values pushed onto a stack at the start of the function. The +variables are then initialized to zero and used throughout the +execution of the function. At function exit, these variables are +popped so that the original value (at the time of the function call) +of these variables are restored. The parameters are really auto +variables that are initialized to a value provided in the function +call. Auto variables are different than traditional local variables +in the fact that if function A calls function B, B may access function +A's auto variables by just using the same name, unless function B has +called them auto variables. Due to the fact that auto variables and +parameters are pushed onto a stack, \fBbc\fR supports recursive functions. +.PP +The function body is a list of \fBbc\fR statements. Again, statements +are separated by semicolons or newlines. Return statements cause the +termination of a function and the return of a value. There are two +versions of the return statement. The first form, "\fBreturn\fR", returns +the value 0 to the calling expression. The second form, +"\fBreturn ( \fIexpression \fB)\fR", computes the value of the expression +and returns that value to the calling expression. There is an implied +"\fBreturn (0)\fR" at the end of every function. This allows a function +to terminate and return 0 without an explicit return statement. +.PP +Functions also change the usage of the variable \fBibase\fR. All +constants in the function body will be converted using the value of +\fBibase\fR at the time of the function call. Changes of \fBibase\fR +will be ignored during the execution of the function except for the +standard function \fBread\fR, which will always use the current value +of \fBibase\fR for conversion of numbers. +.SS MATH LIBRARY +If \fBbc\fR is invoked with the \fB-l\fR option, a math library is preloaded +and the default scale is set to 20. The math functions will calculate their +results to the scale set at the time of their call. +The math library defines the following functions: +.IP "s (\fIx\fR)" +The sine of x in radians. +.IP "c (\fIx\fR)" +The cosine of x in radians. +.IP "a (\fIx\fR)" +The arctangent of x. +.IP "l (\fIx\fR)" +The natural logarithm of x. +.IP "e (\fIx\fR)" +The exponential function of raising e to the value x. +.IP "j (\fIn,x\fR)" +The bessel function of integer order n of x. +.SS EXAMPLES +In /bin/sh, the following will assign the value of "pi" to the shell +variable \fBpi\fR. +.RS +\f(CW +pi=$(echo "scale=10; 4*a(1)" | bc -l) +\fR +.RE +.PP +The following is the definition of the exponential function used in the +math library. This function is written in POSIX \fBbc\fR. +.nf +.RS +\f(CW +scale = 20 + +/* Uses the fact that e^x = (e^(x/2))^2 + When x is small enough, we use the series: + e^x = 1 + x + x^2/2! + x^3/3! + ... +*/ + +define e(x) { + auto a, d, e, f, i, m, v, z + + /* Check the sign of x. */ + if (x<0) { + m = 1 + x = -x + } + + /* Precondition x. */ + z = scale; + scale = 4 + z + .44*x; + while (x > 1) { + f += 1; + x /= 2; + } + + /* Initialize the variables. */ + v = 1+x + a = x + d = 1 + + for (i=2; 1; i++) { + e = (a *= x) / (d *= i) + if (e == 0) { + if (f>0) while (f--) v = v*v; + scale = z + if (m) return (1/v); + return (v/1); + } + v += e + } +} +\fR +.RE +.fi +.PP +The following is code that uses the extended features of \fBbc\fR to +implement a simple program for calculating checkbook balances. This +program is best kept in a file so that it can be used many times +without having to retype it at every use. +.nf +.RS +\f(CW +scale=2 +print "\enCheck book program!\en" +print " Remember, deposits are negative transactions.\en" +print " Exit by a 0 transaction.\en\en" + +print "Initial balance? "; bal = read() +bal /= 1 +print "\en" +while (1) { + "current balance = "; bal + "transaction? "; trans = read() + if (trans == 0) break; + bal -= trans + bal /= 1 +} +quit +\fR +.RE +.fi +.PP +The following is the definition of the recursive factorial function. +.nf +.RS +\f(CW +define f (x) { + if (x <= 1) return (1); + return (f(x-1) * x); +} +\fR +.RE +.fi +.SS DIFFERENCES +This version of +.B bc +was implemented from the POSIX P1003.2/D11 draft and contains +several differences and extensions relative to the draft and +traditional implementations. +It is not implemented in the traditional way using +.I dc(1). +This version is a single process which parses and runs a byte code +translation of the program. There is an "undocumented" option (-c) +that causes the program to output the byte code to +the standard output instead of running it. It was mainly used for +debugging the parser and preparing the math library. +.PP +A major source of differences is +extensions, where a feature is extended to add more functionality and + additions, where new features are added. +The following is the list of differences and extensions. +.IP LANG environment +This version does not conform to the POSIX standard in the processing +of the LANG environment variable and all environment variables starting +with LC_. +.IP names +Traditional and POSIX +.B bc +have single letter names for functions, variables and arrays. They have +been extended to be multi-character names that start with a letter and +may contain letters, numbers and the underscore character. +.IP last +POSIX \fBbc\fR does not have a \fBlast\fR variable. Some implementations +of \fBbc\fR use the period (.) in a similar way. +.IP comparisons +POSIX \fBbc\fR allows comparisons only in the if statement, the while +statement, and the second expression of the for statement. Also, only +one relational operation is allowed in each of those statements. +.IP "if statement, else clause" +POSIX \fBbc\fR does not have an else clause. +.IP "for statement" +POSIX \fBbc\fR requires all expressions to be present in the for statement. +.IP "&&, ||, !" +POSIX \fBbc\fR does not have the logical operators. +.IP "read function" +POSIX \fBbc\fR does not have a read function. +.IP "print statement" +POSIX \fBbc\fR does not have a print statement . +.IP "continue statement" +POSIX \fBbc\fR does not have a continue statement. +.IP "array parameters" +POSIX \fBbc\fR does not have array parameters. Other implementations +of \fBbc\fR may have call by value array parameters. +.IP "=+, =-, =*, =/, =%, =^" +POSIX \fBbc\fR does not require these "old style" assignment operators to +be defined. This version may allow these "old style" assignments. Use +the limits statement to see if the installed version supports them. If +it does support the "old style" assignment operators, the statement +"a =- 1" will decrement \fBa\fR by 1 instead of setting \fBa\fR to the +value -1. +.IP "spaces in numbers" +Other implementations of \fBbc\fR allow spaces in numbers. For example, +"x=1 3" would assign the value 13 to the variable x. The same statement +would cause a syntax error in this version of \fBbc\fR. +.IP "errors and execution" +This implementation varies from other implementations in terms of what +code will be executed when syntax and other errors are found in the +program. If a syntax error is found in a function definition, error +recovery tries to find the beginning of a statement and continue to +parse the function. Once a syntax error is found in the function, the +function will not be callable and becomes undefined. +Syntax errors in the interactive execution code will invalidate the +current execution block. The execution block is terminated by an +end of line that appears after a complete sequence of statements. +For example, +.nf +.RS +a = 1 +b = 2 +.RE +.fi +has two execution blocks and +.nf +.RS +{ a = 1 + b = 2 } +.RE +.fi +has one execution block. Any runtime error will terminate the execution +of the current execution block. A runtime warning will not terminate the +current execution block. +.SS LIMITS +The following are the limits currently in place for this +.B bc +processor. Some of them may have been changed by an installation. +Use the limits statement to see the actual values. +.IP BC_BASE_MAX +The maximum output base is currently set at 999. The maximum input base +is 16. +.IP BC_DIM_MAX +This is currently an arbitrary limit of 65535 as distributed. Your +installation may be different. +.IP BC_SCALE_MAX +The number of digits after the decimal point is limited to INT_MAX digits. +Also, the number of digits before the decimal point is limited to INT_MAX +digits. +.IP BC_STRING_MAX +The limit on the number of characters in a string is INT_MAX characters. +.IP exponent +The value of the exponent in the raise operation (^) is limited to LONG_MAX. +.IP multiply +The multiply routine may yield incorrect results if a number +has more than LONG_MAX / 90 total digits. For 32 bit longs, this number is +23,860,929 digits. +.IP "code size" +Each function and the "main" program are limited to 10240 bytes of +compiled byte code each. This limit (BC_MAX_SEGS) can be easily changed +to have more than 10 segments of 1024 bytes. +.IP "variable names" +The current limit on the number of unique names is 32767 for each of +simple variables, arrays and functions. +.SH FILES +In most installations, \fBbc\fR is completely self-contained. +Where executable size is of importance or the C compiler does +not deal with very long strings, \fBbc\fR will read +the standard math library from the file /usr/local/lib/libmath.b. +(The actual location may vary. It may be /lib/libmath.b.) +.SH DIAGNOSTICS +If any file on the command line can not be opened, \fBbc\fR will report +that the file is unavailable and terminate. Also, there are compile +and run time diagnostics that should be self-explanatory. +.SH BUGS +Error recovery is not very good yet. +.SH AUTHOR +.nf +Philip A. Nelson +phil@cs.wwu.edu +.fi +.SH ACKNOWLEDGEMENTS +The author would like to thank Steve Sommars (sesv@iwtsf.att.com) for +his extensive help in testing the implementation. Many great suggestions +were given. This is a much better product due to his involvement. diff --git a/usr/othersrc/public/bc-1.01/bc.y b/usr/othersrc/public/bc-1.01/bc.y new file mode 100644 index 0000000000..48612e86e4 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/bc.y @@ -0,0 +1,614 @@ +%{ +/* bc.y: The grammar for a POSIX compatable bc processor with some + extensions to the language. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" +#include "proto.h" +%} + +%start program + +%union { + char *s_value; + char c_value; + int i_value; + arg_list *a_value; + } + +/* Extensions over POSIX bc. + a) NAME was LETTER. This grammer allows longer names. + Single letter names will still work. + b) Relational_expression allowed only one comparison. + This grammar has added boolean expressions with + && (and) || (or) and ! (not) and allowed all of them in + full expressions. + c) Added an else to the if. + d) Call by variable array parameters + e) read() procedure that reads a number under program control from stdin. + f) halt statement that halts the the program under program control. It + is an executed statement. + g) continue statement for for loops. + h) optional expressions in the for loop. + i) print statement to print multiple numbers per line. + j) warranty statement to print an extended warranty notice. + j) limits statement to print the processor's limits. +*/ + +%token NEWLINE AND OR NOT +%token STRING NAME NUMBER +/* '-', '+' are tokens themselves */ +%token MUL_OP +/* '*', '/', '%' */ +%token ASSIGN_OP +/* '=', '+=', '-=', '*=', '/=', '%=', '^=' */ +%token REL_OP +/* '==', '<=', '>=', '!=', '<', '>' */ +%token INCR_DECR +/* '++', '--' */ +%token Define Break Quit Length +/* 'define', 'break', 'quit', 'length' */ +%token Return For If While Sqrt Else +/* 'return', 'for', 'if', 'while', 'sqrt', 'else' */ +%token Scale Ibase Obase Auto Read +/* 'scale', 'ibase', 'obase', 'auto', 'read' */ +%token Warranty, Halt, Last, Continue, Print, Limits +/* 'warranty', 'halt', 'last', 'continue', 'print', 'limits' */ + +/* Types of all other things. */ +%type expression return_expression named_expression opt_expression +%type '+' '-' +%type opt_parameter_list opt_auto_define_list define_list +%type opt_argument_list argument_list +%type NAME STRING NUMBER +%type program input_item semicolon_list statement_list +%type statement function statement_or_error +%type NEWLINE AND OR NOT + +/* precedence */ +%left OR +%left AND +%nonassoc NOT +%left REL_OP +%right ASSIGN_OP +%left '+' '-' +%left MUL_OP +%right '^' +%nonassoc UNARY_MINUS +%nonassoc INCR_DECR + +%% +program : /* empty */ + { + $$ = 0; + if (interactive) + { + printf ("%s\n", BC_VERSION); + welcome (); + } + } + | program input_item + ; +input_item : semicolon_list NEWLINE + { run_code (); } + | function + { run_code (); } + | error NEWLINE + { + yyerrok; + init_gen (); + } + ; +semicolon_list : /* empty */ + { $$ = 0; } + | statement_or_error + | semicolon_list ';' statement_or_error + | semicolon_list ';' + ; +statement_list : /* empty */ + { $$ = 0; } + | statement_or_error + | statement_list NEWLINE + | statement_list NEWLINE statement_or_error + | statement_list ';' + | statement_list ';' statement + ; +statement_or_error : statement + | error statement + { $$ = $2; } + ; +statement : Warranty + { warranty (""); } + | Limits + { limits (); } + | expression + { + if ($1 & 2) + warn ("comparison in expression"); + if ($1 & 1) + generate ("W"); + else + generate ("p"); + } + | STRING + { + $$ = 0; + generate ("w"); + generate ($1); + free ($1); + } + | Break + { + if (break_label == 0) + yyerror ("Break outside a for/while"); + else + { + sprintf (genstr, "J%1d:", break_label); + generate (genstr); + } + } + | Continue + { + warn ("Continue statement"); + if (continue_label == 0) + yyerror ("Continue outside a for"); + else + { + sprintf (genstr, "J%1d:", continue_label); + generate (genstr); + } + } + | Quit + { exit (0); } + | Halt + { generate ("h"); } + | Return + { generate ("0R"); } + | Return '(' return_expression ')' + { generate ("R"); } + | For + { + $1 = break_label; + break_label = next_label++; + } + '(' opt_expression ';' + { + if ($4 > 1) + warn ("Comparison in first for expression"); + $4 = next_label++; + if ($4 < 0) + sprintf (genstr, "N%1d:", $4); + else + sprintf (genstr, "pN%1d:", $4); + generate (genstr); + } + opt_expression ';' + { + if ($7 < 0) generate ("1"); + $7 = next_label++; + sprintf (genstr, "B%1d:J%1d:", $7, break_label); + generate (genstr); + $$ = continue_label; + continue_label = next_label++; + sprintf (genstr, "N%1d:", continue_label); + generate (genstr); + } + opt_expression ')' + { + if ($10 > 1) + warn ("Comparison in third for expression"); + if ($10 < 0) + sprintf (genstr, "J%1d:N%1d:", $4, $7); + else + sprintf (genstr, "pJ%1d:N%1d:", $4, $7); + generate (genstr); + } + statement + { + sprintf (genstr, "J%1d:N%1d:", + continue_label, break_label); + generate (genstr); + break_label = $1; + continue_label = $9; + } + | If '(' expression ')' + { + $3 = if_label; + if_label = next_label++; + sprintf (genstr, "Z%1d:", if_label); + generate (genstr); + } + statement opt_else + { + sprintf (genstr, "N%1d:", if_label); + generate (genstr); + if_label = $3; + } + | While + { + $1 = next_label++; + sprintf (genstr, "N%1d:", $1); + generate (genstr); + } + '(' expression + { + $4 = break_label; + break_label = next_label++; + sprintf (genstr, "Z%1d:", break_label); + generate (genstr); + } + ')' statement + { + sprintf (genstr, "J%1d:N%1d:", $1, break_label); + generate (genstr); + break_label = $4; + } + | '{' statement_list '}' + { $$ = 0; } + | Print + { warn ("print statement"); } + print_list + ; +print_list : print_element + | print_element ',' print_list + ; +print_element : STRING + { + generate ("O"); + generate ($1); + free ($1); + } + | expression + { generate ("P"); } + ; +opt_else : /* nothing */ + | Else + { + warn ("else clause in if statement"); + $1 = next_label++; + sprintf (genstr, "J%d:N%1d:", $1, if_label); + generate (genstr); + if_label = $1; + } + statement +function : Define NAME '(' opt_parameter_list ')' '{' + NEWLINE opt_auto_define_list + { + /* Check auto list against parameter list? */ + check_params ($4,$8); + sprintf (genstr, "F%d,%s.%s[", lookup($2,FUNCT), + arg_str ($4,TRUE), arg_str ($8,TRUE)); + generate (genstr); + free_args ($4); + free_args ($8); + $1 = next_label; + next_label = 0; + } + statement_list NEWLINE '}' + { + generate ("0R]"); + next_label = $1; + } + ; +opt_parameter_list : /* empty */ + { $$ = NULL; } + | define_list + ; +opt_auto_define_list : /* empty */ + { $$ = NULL; } + | Auto define_list NEWLINE + { $$ = $2; } + | Auto define_list ';' + { $$ = $2; } + ; +define_list : NAME + { $$ = nextarg (NULL, lookup ($1,SIMPLE)); } + | NAME '[' ']' + { $$ = nextarg (NULL, lookup ($1,ARRAY)); } + | define_list ',' NAME + { $$ = nextarg ($1, lookup ($3,SIMPLE)); } + | define_list ',' NAME '[' ']' + { $$ = nextarg ($1, lookup ($3,ARRAY)); } + ; +opt_argument_list : /* empty */ + { $$ = NULL; } + | argument_list + ; +argument_list : expression + { + if ($1 > 1) warn ("comparison in argument"); + $$ = nextarg (NULL,0); + } + | NAME '[' ']' + { + sprintf (genstr, "K%d:", -lookup ($1,ARRAY)); + generate (genstr); + $$ = nextarg (NULL,1); + } + | argument_list ',' expression + { + if ($3 > 1) warn ("comparison in argument"); + $$ = nextarg ($1,0); + } + | argument_list ',' NAME '[' ']' + { + sprintf (genstr, "K%d:", -lookup ($3,ARRAY)); + generate (genstr); + $$ = nextarg ($1,1); + } + ; +opt_expression : /* empty */ + { + $$ = -1; + warn ("Missing expression in for statement"); + } + | expression + ; +return_expression : /* empty */ + { + $$ = 0; + generate ("0"); + } + | expression + { + if ($1 > 1) + warn ("comparison in return expresion"); + } + ; +expression : named_expression ASSIGN_OP + { + if ($2 != '=') + { + if ($1 < 0) + sprintf (genstr, "DL%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + } + expression + { + if ($4 > 1) warn("comparison in assignment"); + if ($2 != '=') + { + sprintf (genstr, "%c", $2); + generate (genstr); + } + if ($1 < 0) + sprintf (genstr, "S%d:", -$1); + else + sprintf (genstr, "s%d:", $1); + generate (genstr); + $$ = 0; + } + ; + | expression AND + { + warn("&& operator"); + $2 = next_label++; + sprintf (genstr, "DZ%d:p", $2); + generate (genstr); + } + expression + { + sprintf (genstr, "DZ%d:p1N%d:", $2, $2); + generate (genstr); + $$ = $1 | $4; + } + | expression OR + { + warn("|| operator"); + $2 = next_label++; + sprintf (genstr, "B%d:", $2); + generate (genstr); + } + expression + { + int tmplab; + tmplab = next_label++; + sprintf (genstr, "B%d:0J%d:N%d:1N%d:", + $2, tmplab, $2, tmplab); + generate (genstr); + $$ = $1 | $4; + } + | NOT expression + { + $$ = $2; + warn("! operator"); + generate ("!"); + } + | expression REL_OP expression + { + $$ = 3; + switch (*($2)) + { + case '=': + generate ("="); + break; + + case '!': + generate ("#"); + break; + + case '<': + if ($2[1] == '=') + generate ("{"); + else + generate ("<"); + break; + + case '>': + if ($2[1] == '=') + generate ("}"); + else + generate (">"); + break; + } + } + | expression '+' expression + { + generate ("+"); + $$ = $1 | $3; + } + | expression '-' expression + { + generate ("-"); + $$ = $1 | $3; + } + | expression MUL_OP expression + { + genstr[0] = $2; + genstr[1] = 0; + generate (genstr); + $$ = $1 | $3; + } + | expression '^' expression + { + generate ("^"); + $$ = $1 | $3; + } + | '-' expression %prec UNARY_MINUS + { + generate ("n"); + $$ = $2; + } + | named_expression + { + $$ = 1; + if ($1 < 0) + sprintf (genstr, "L%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + | NUMBER + { + int len = strlen($1); + $$ = 1; + if (len == 1 && *$1 == '0') + generate ("0"); + else if (len == 1 && *$1 == '1') + generate ("1"); + else + { + generate ("K"); + generate ($1); + generate (":"); + } + free ($1); + } + | '(' expression ')' + { $$ = $2 | 1; } + | NAME '(' opt_argument_list ')' + { + $$ = 1; + if ($3 != NULL) + { + sprintf (genstr, "C%d,%s:", + lookup ($1,FUNCT), + arg_str ($3,FALSE)); + free_args ($3); + } + else + { + sprintf (genstr, "C%d:", lookup ($1,FUNCT)); + } + generate (genstr); + } + | INCR_DECR named_expression + { + $$ = 1; + if ($2 < 0) + { + if ($1 == '+') + sprintf (genstr, "DA%d:L%d:", -$2, -$2); + else + sprintf (genstr, "DM%d:L%d:", -$2, -$2); + } + else + { + if ($1 == '+') + sprintf (genstr, "i%d:l%d:", $2, $2); + else + sprintf (genstr, "d%d:l%d:", $2, $2); + } + generate (genstr); + } + | named_expression INCR_DECR + { + $$ = 1; + if ($1 < 0) + { + sprintf (genstr, "DL%d:x", -$1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "A%d:", -$1); + else + sprintf (genstr, "M%d:", -$1); + } + else + { + sprintf (genstr, "l%d:", $1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "i%d:", $1); + else + sprintf (genstr, "d%d:", $1); + } + generate (genstr); + } + | Length '(' expression ')' + { generate ("cL"); $$ = 1;} + | Sqrt '(' expression ')' + { generate ("cR"); $$ = 1;} + | Scale '(' expression ')' + { generate ("cS"); $$ = 1;} + | Read '(' ')' + { + warn ("read function"); + generate ("cI"); $$ = 1; + } + ; +named_expression : NAME + { $$ = lookup($1,SIMPLE); } + | NAME '[' expression ']' + { + if ($3 > 1) warn("comparison in subscript"); + $$ = lookup($1,ARRAY); + } + | Ibase + { $$ = 0; } + | Obase + { $$ = 1; } + | Scale + { $$ = 2; } + | Last + { $$ = 3; } + ; +%% diff --git a/usr/othersrc/public/bc-1.01/config.h b/usr/othersrc/public/bc-1.01/config.h new file mode 100644 index 0000000000..a9bd0be5f5 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/config.h @@ -0,0 +1,4 @@ +/* config.h */ +#ifndef __STDC__ +#define VARARGS +#endif diff --git a/usr/othersrc/public/bc-1.01/configure b/usr/othersrc/public/bc-1.01/configure new file mode 100755 index 0000000000..598cb915ce --- /dev/null +++ b/usr/othersrc/public/bc-1.01/configure @@ -0,0 +1,119 @@ +#!/bin/sh +# +# The shell script for bc to automatically create the config.h file. +# +# Syntax: configure [ include-dir [ lib-file [ c-compiler ]]] +# +# Set some standard things. +# +INCLUDE=${1-/usr/include} +LIBFILE=${2-libmath.b} +CC=${3-cc} +# +# Initialize the exit status +# +EXIT=0 +# +# Remove the current copy and make sure there is at least an empty file +# +rm -f config.h +echo "/* config.h */" > config.h +# +# Check for the argument passing mechanism. +# +if test -r $INCLUDE/varargs.h +then +echo "Using varargs.h for variable arguments (non ansi compilers). " +echo "#ifndef __STDC__" >> config.h +echo "#define VARARGS" >> config.h +echo "#endif" >> config.h +else +if test -r $INCLUDE/stdarg.h +then +echo "Using stdarg.h for variable arguments. " +else +echo "Unknown variable argument mechanism. " +EXIT=1 +fi +fi +# +# Limits file? +# +if test -r $INCLUDE/limits.h +then +echo "You have a limits.h file." +else +echo "You need to check the limits in const.h." +echo "#define NO_LIMITS" >> config.h +fi +# +# unistd? +# +if test -r $INCLUDE/unistd.h +then +echo "You have a unistd.h file." +else +echo "#define NO_UNISTD" >> config.h +fi +# +# Stdlib? +# +if test -r $INCLUDE/stdlib.h +then +echo "You have a stdlib.h file." +else +echo "#define NO_STDLIB" >> config.h +fi +# +# Strinog? +# +if test -r $INCLUDE/string.h +then +echo "You have a string.h file." +else +echo "#define STRINGS_H" >> config.h +fi +# +# At least one BSD system did not define "extern int errno;" in errno.h +# +if grep -s extern $INCLUDE/errno.h +then +true +else +echo "extern int errno;" >> config.h +fi +# +# On MINIX pc systems we want to define some extra files. +# +if test -r $INCLUDE/minix +then +# We must be on a minix system. Check for the machine type. +if test `machine` = IBM_PC +then +echo "#define SMALL_BUF" >> config.h +echo "#define BC_MATH_FILE \"$LIBFILE\"" >> config.h +echo "#define SHORTNAMES" >> config.h +else +echo "Your Minix is not on a PC. The bc library will be loaded in the" +echo "executable file. You will not have to install a library file." +fi +else +# Test to see if the compiler will compile long strings... +echo "char libmath[] =" > JUNK.c +echo "#include \"math.h\"" >> JUNK.c +echo ";" >> JUNK.c +if $CC -c JUNK.c >/dev/null 2>&1 +then +echo "The bc library will be loaded in the executable file. You will" +echo "not have to install a library file." +else +echo "Your C compiler does not like long strings. You will have to" +echo "load the math library from a file." +echo "#define BC_MATH_FILE \"$LIBFILE\"" >> config.h +fi +rm -f JUNK.c JUNK.o +fi +# +# exit +# +exit $EXIT diff --git a/usr/othersrc/public/bc-1.01/const.h b/usr/othersrc/public/bc-1.01/const.h new file mode 100644 index 0000000000..58ddf8a446 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/const.h @@ -0,0 +1,87 @@ +/* const.h: Constants for bc. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + + +/* Define INT_MAX and LONG_MAX if not defined. Assuming 32 bits... */ + +#ifdef NO_LIMITS +#define INT_MAX 0x7FFFFFFF +#define LONG_MAX 0x7FFFFFFF +#endif + + +/* Define constants in some reasonable size. The next 4 constants are + POSIX constants. */ + +#define BC_BASE_MAX 999 /* No good reason for this value. */ +#define BC_SCALE_MAX INT_MAX +#define BC_STRING_MAX INT_MAX + + +/* Definitions for arrays. */ + +#define BC_DIM_MAX 65535 /* this should be NODE_SIZE^NODE_DEPTH-1 */ + +#define NODE_SIZE 16 /* Must be a power of 2. */ +#define NODE_MASK 0xf /* Must be NODE_SIZE-1. */ +#define NODE_SHIFT 4 /* Number of 1 bits in NODE_MASK. */ +#define NODE_DEPTH 4 + + +/* Other BC limits defined but not part of POSIX. */ + +#define BC_LABEL_GROUP 64 +#define BC_LABEL_LOG 6 +#define BC_MAX_SEGS 10 /* Code segments. */ +#define BC_SEG_SIZE 1024 +#define BC_SEG_LOG 10 + +/* Maximum number of variables, arrays and functions and the + allocation increment for the dynamic arrays. */ + +#define MAX_STORE 32767 +#define STORE_INCR 32 + +/* Other interesting constants. */ + +#define FALSE 0 +#define TRUE 1 +#define SIMPLE 0 +#define ARRAY 1 +#define FUNCT 2 +#define EXTERN extern +#ifdef __STDC__ +#define CONST const +#define VOID void +#else +#define CONST +#define VOID +#endif + +/* Include the version definition. */ +#include "version.h" diff --git a/usr/othersrc/public/bc-1.01/execute.c b/usr/othersrc/public/bc-1.01/execute.c new file mode 100644 index 0000000000..191dad8e11 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/execute.c @@ -0,0 +1,780 @@ +/* execute.c - run a bc program. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include +#include +#include "global.h" +#include "proto.h" + +/* Local variable for SIGINT interrupt of an execution. */ +static jmp_buf env; + + +/* The SIGINT interrupt handling routine. */ + +void +stop_execution (sig) + int sig; +{ + printf ("\n"); + rt_error ("interrupted execution"); + longjmp (env, 1); /* Jump to the main code. */ +} + + +/* Get the current byte and advance the PC counter. */ + +unsigned char +byte (pc) + program_counter *pc; +{ + int seg, offset; + + seg = pc->pc_addr >> BC_SEG_LOG; + offset = pc->pc_addr++ % BC_SEG_SIZE; + return (functions[pc->pc_func].f_body[seg][offset]); +} + + +/* The routine that actually runs the machine. */ + +void +execute () +{ + int label_num, l_gp, l_off; + bc_label_group *gp; + + char inst, ch; + int new_func; + int var_name; + + int const_base; + + bc_num temp_num; + arg_list *auto_list; + + /* Initialize this run... */ + pc.pc_func = 0; + pc.pc_addr = 0; + runtime_error = FALSE; + init_num (&temp_num); + + /* Set up the interrupt mechanism for an interactive session. */ + if (interactive) + if (setjmp (env) == 0) + signal (SIGINT, stop_execution); + + while (pc.pc_addr < functions[pc.pc_func].f_code_size && !runtime_error) + { + inst = byte(&pc); + +#if DEBUG > 3 + { /* Print out address and the stack before each instruction.*/ + int depth; estack_rec *temp = ex_stack; + + printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst); + if (temp == NULL) printf ("empty stack.\n", inst); + else + { + depth = 1; + while (temp != NULL) + { + printf (" %d = ", depth); + out_num (temp->s_num, 10, out_char); + depth++; + temp = temp->s_next; + } + } + } +#endif + + switch ( inst ) + { + + case 'A' : /* increment array variable (Add one). */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + incr_array (var_name); + break; + + case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */ + case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */ + c_code = !is_zero (ex_stack->s_num); + pop (); + case 'J' : /* Jump to a label. */ + label_num = byte(&pc); /* Low order bits first. */ + label_num += byte(&pc) << 8; + if (inst == 'J' || (inst == 'B' && c_code) + || (inst == 'Z' && !c_code)) { + gp = functions[pc.pc_func].f_label; + l_gp = label_num >> BC_LABEL_LOG; + l_off = label_num % BC_LABEL_GROUP; + while (l_gp-- > 0) gp = gp->l_next; + pc.pc_addr = gp->l_adrs[l_off]; + } + break; + + case 'C' : /* Call a function. */ + /* Get the function number. */ + new_func = byte(&pc); + if ((new_func & 0x80) != 0) + new_func = ((new_func << 8) & 0x7f) + byte(&pc); + + /* Check to make sure it is defined. */ + if (!functions[new_func].f_defined) + { + rt_error ("Function %s not defined.", f_names[new_func]); + break; + } + + /* Check and push parameters. */ + process_params (&pc, new_func); + + /* Push auto variables. */ + for (auto_list = functions[new_func].f_autos; + auto_list != NULL; + auto_list = auto_list->next) + auto_var (auto_list->av_name); + + /* Push pc and ibase. */ + fpush (pc.pc_func); + fpush (pc.pc_addr); + fpush (i_base); + + /* Reset pc to start of function. */ + pc.pc_func = new_func; + pc.pc_addr = 0; + break; + + case 'D' : /* Duplicate top of stack */ + push_copy (ex_stack->s_num); + break; + + case 'K' : /* Push a constant */ + /* Get the input base and convert it to a bc number. */ + if (pc.pc_func == 0) + const_base = i_base; + else + const_base = fn_stack->s_val; + if (const_base == 10) + push_b10_const (&pc); + else + push_constant (prog_char, const_base); + break; + + case 'L' : /* load array variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + load_array (var_name); + break; + + case 'M' : /* decrement array variable (Minus!) */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + decr_array (var_name); + break; + + case 'O' : /* Write a string to the output with processing. */ + while ((ch = byte(&pc)) != '"') + if (ch != '\\') + out_char (ch); + else + { + ch = byte(&pc); + if (ch == '"') break; + switch (ch) + { + case 'n': out_char ('\n'); break; + case 't': out_char ('\t'); break; + case 'r': out_char ('\r'); break; + case 'b': out_char (007); break; + case 'f': out_char ('\f'); break; + case '\\': out_char ('\\'); break; + default: break; + } + } + if (interactive) fflush (stdout); + break; + + case 'R' : /* Return from function */ + if (pc.pc_func != 0) + { + /* "Pop" autos and parameters. */ + pop_vars(functions[pc.pc_func].f_autos); + pop_vars(functions[pc.pc_func].f_params); + /* reset the pc. */ + fpop (); + pc.pc_addr = fpop (); + pc.pc_func = fpop (); + } + else + rt_error ("Return from main program."); + break; + + case 'S' : /* store array variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + store_array (var_name); + break; + + case 'T' : /* Test tos for zero */ + c_code = is_zero (ex_stack->s_num); + assign (c_code); + break; + + case 'W' : /* Write the value on the top of the stack. */ + case 'P' : /* Write the value on the top of the stack. No newline. */ + out_num (ex_stack->s_num, o_base, out_char); + if (inst == 'W') out_char ('\n'); + store_var (3); /* Special variable "last". */ + if (interactive) fflush (stdout); + break; + + case 'c' : /* Call special function. */ + new_func = byte(&pc); + + switch (new_func) + { + case 'L': /* Length function. */ + /* For the number 0.xxxx, 0 is not significant. */ + if (ex_stack->s_num->n_len == 1 && + ex_stack->s_num->n_scale != 0 && + ex_stack->s_num->n_value[0] == 0 ) + int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); + else + int2num (&ex_stack->s_num, ex_stack->s_num->n_len + + ex_stack->s_num->n_scale); + break; + + case 'S': /* Scale function. */ + int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); + break; + + case 'R': /* Square Root function. */ + if (!bc_sqrt (&ex_stack->s_num, scale)) + rt_error ("Square root of a negative number"); + break; + + case 'I': /* Read function. */ + push_constant (input_char, i_base); + break; + } + break; + + case 'd' : /* Decrement number */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + decr_var (var_name); + break; + + case 'h' : /* Halt the machine. */ + exit (0); + + case 'i' : /* increment number */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + incr_var (var_name); + break; + + case 'l' : /* load variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + load_var (var_name); + break; + + case 'n' : /* Negate top of stack. */ + bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num); + break; + + case 'p' : /* Pop the execution stack. */ + pop (); + break; + + case 's' : /* store variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + store_var (var_name); + break; + + case 'w' : /* Write a string to the output. */ + while ((ch = byte(&pc)) != '"') out_char (ch); + if (interactive) fflush (stdout); + break; + + case 'x' : /* Exchange Top of Stack with the one under the tos. */ + if (check_stack(2)) { + bc_num temp = ex_stack->s_num; + ex_stack->s_num = ex_stack->s_next->s_num; + ex_stack->s_next->s_num = temp; + } + break; + + case '0' : /* Load Constant 0. */ + push_copy (_zero_); + break; + + case '1' : /* Load Constant 0. */ + push_copy (_one_); + break; + + case '!' : /* Negate the boolean value on top of the stack. */ + c_code = is_zero (ex_stack->s_num); + assign (c_code); + break; + + case '&' : /* compare greater than */ + if (check_stack(2)) + { + c_code = !is_zero (ex_stack->s_next->s_num) + && !is_zero (ex_stack->s_num); + pop (); + assign (c_code); + } + break; + + case '|' : /* compare greater than */ + if (check_stack(2)) + { + c_code = !is_zero (ex_stack->s_next->s_num) + || !is_zero (ex_stack->s_num); + pop (); + assign (c_code); + } + break; + + case '+' : /* add */ + if (check_stack(2)) + { + bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '-' : /* subtract */ + if (check_stack(2)) + { + bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '*' : /* multiply */ + if (check_stack(2)) + { + bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num, + &temp_num, scale); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '/' : /* divide */ + if (check_stack(2)) + { + if (bc_divide (ex_stack->s_next->s_num, + ex_stack->s_num, &temp_num, scale) == 0) + { + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + else + rt_error ("Divide by zero"); + } + break; + + case '%' : /* remainder */ + if (check_stack(2)) + { + if (is_zero (ex_stack->s_num)) + rt_error ("Modulo by zero"); + else + { + bc_modulo (ex_stack->s_next->s_num, + ex_stack->s_num, &temp_num, scale); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + } + break; + + case '^' : /* raise */ + if (check_stack(2)) + { + bc_raise (ex_stack->s_next->s_num, + ex_stack->s_num, &temp_num, scale); + if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num)) + rt_error ("divide by zero"); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '=' : /* compare equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) == 0; + pop (); + assign (c_code); + } + break; + + case '#' : /* compare not equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) != 0; + pop (); + assign (c_code); + } + break; + + case '<' : /* compare less than */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) == -1; + pop (); + assign (c_code); + } + break; + + case '{' : /* compare less than or equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) <= 0; + pop (); + assign (c_code); + } + break; + + case '>' : /* compare greater than */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) == 1; + pop (); + assign (c_code); + } + break; + + case '}' : /* compare greater than or equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) >= 0; + pop (); + assign (c_code); + } + break; + + default : /* error! */ + rt_error ("bad instruction: inst=%c", inst); + } + } + + /* Clean up the interrupt stuff. */ + if (interactive) + signal (SIGINT, use_quit); + + /* Clean up the function stack and pop all autos/parameters. */ + while (pc.pc_func != 0) + { + pop_vars(functions[pc.pc_func].f_autos); + pop_vars(functions[pc.pc_func].f_params); + fpop (); + pc.pc_addr = fpop (); + pc.pc_func = fpop (); + } + + /* Clean up the execution stack. */ + while (ex_stack != NULL) pop(); + +} + + +/* Prog_char gets another byte from the program. It is used for + conversion of text constants in the code to numbers. */ + +char +prog_char () +{ + return byte(&pc); +} + + +/* Read a character from the standard input. This function is used + by the "read" function. */ + +char +input_char () +{ + char in_ch; + + /* Get a character from the standard input for the read function. */ + in_ch = getchar(); + + /* Check for a \ quoted newline. */ + if (in_ch == '\\') + { + in_ch = getchar(); + if (in_ch == '\n') + in_ch = getchar(); + } + + /* Classify and preprocess the input character. */ + if (isdigit(in_ch)) + return (in_ch - '0'); + if (in_ch >= 'A' && in_ch <= 'F') + return (in_ch + 10 - 'A'); + if (in_ch >= 'a' && in_ch <= 'f') + return (in_ch + 10 - 'a'); + if (in_ch == '.' || in_ch == '+' || in_ch == '-') + return (in_ch); + if (in_ch <= ' ') + return (' '); + + return (':'); +} + + +/* Push_constant converts a sequence of input characters as returned + by IN_CHAR into a number. The number is pushed onto the execution + stack. The number is converted as a number in base CONV_BASE. */ + +void +push_constant (in_char, conv_base) + char (*in_char)(VOID); + int conv_base; +{ + int digits; + bc_num build, temp, result, mult, divisor; + char in_ch, first_ch; + char negative; + + /* Initialize all bc numbers */ + init_num (&temp); + init_num (&result); + init_num (&mult); + build = copy_num (_zero_); + negative = FALSE; + + /* The conversion base. */ + int2num (&mult, conv_base); + + /* Get things ready. */ + in_ch = in_char(); + while (in_ch == ' ') + in_ch = in_char(); + + if (in_ch == '+') + in_ch = in_char(); + else + if (in_ch == '-') + { + negative = TRUE; + in_ch = in_char(); + } + + /* Check for the special case of a single digit. */ + if (in_ch < 16) + { + first_ch = in_ch; + in_ch = in_char(); + if (in_ch < 16 && first_ch >= conv_base) + first_ch = conv_base - 1; + int2num (&build, (int) first_ch); + } + + /* Convert the integer part. */ + while (in_ch < 16) + { + if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; + bc_multiply (build, mult, &result, 0); + int2num (&temp, (int) in_ch); + bc_add (result, temp, &build); + in_ch = in_char(); + } + if (in_ch == '.') + { + in_ch = in_char(); + if (in_ch >= conv_base) in_ch = conv_base-1; + free_num (&result); + free_num (&temp); + divisor = copy_num (_one_); + result = copy_num (_zero_); + digits = 0; + while (in_ch < 16) + { + bc_multiply (result, mult, &result, 0); + int2num (&temp, (int) in_ch); + bc_add (result, temp, &result); + bc_multiply (divisor, mult, &divisor, 0); + digits++; + in_ch = in_char(); + if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; + } + bc_divide (result, divisor, &result, digits); + bc_add (build, result, &build); + } + + /* Final work. */ + if (negative) + bc_sub (_zero_, build, &build); + + push_num (build); + free_num (&temp); + free_num (&result); + free_num (&mult); +} + + +/* When converting base 10 constants from the program, we use this + more efficient way to convert them to numbers. PC tells where + the constant starts and is expected to be advanced to after + the constant. */ + +void +push_b10_const (pc) + program_counter *pc; +{ + bc_num build; + program_counter look_pc; + int kdigits, kscale; + char inchar; + char *ptr; + + /* Count the digits and get things ready. */ + look_pc = *pc; + kdigits = 0; + kscale = 0; + inchar = byte (&look_pc); + while (inchar != '.' && inchar != ':') + { + kdigits++; + inchar = byte(&look_pc); + } + if (inchar == '.' ) + { + inchar = byte(&look_pc); + while (inchar != ':') + { + kscale++; + inchar = byte(&look_pc); + } + } + + /* Get the first character again and move the pc. */ + inchar = byte(pc); + + /* Secial cases of 0, 1, and A-F single inputs. */ + if (kdigits == 1 && kscale == 0) + { + if (inchar == 0) + { + push_copy (_zero_); + inchar = byte(pc); + return; + } + if (inchar == 1) { + push_copy (_one_); + inchar = byte(pc); + return; + } + if (inchar > 9) + { + init_num (&build); + int2num (&build, inchar); + push_num (build); + inchar = byte(pc); + return; + } + } + + /* Build the new number. */ + if (kdigits == 0) + { + build = new_num (1,kscale); + ptr = build->n_value; + *ptr++ = 0; + } + else + { + build = new_num (kdigits,kscale); + ptr = build->n_value; + } + + while (inchar != ':') + { + if (inchar != '.') + if (inchar > 9) + *ptr++ = 9; + else + *ptr++ = inchar; + inchar = byte(pc); + } + push_num (build); +} + + +/* Put the correct value on the stack for C_CODE. Frees TOS num. */ + +void +assign (c_code) + char c_code; +{ + free_num (&ex_stack->s_num); + if (c_code) + ex_stack->s_num = copy_num (_one_); + else + ex_stack->s_num = copy_num (_zero_); +} diff --git a/usr/othersrc/public/bc-1.01/fix_math.h b/usr/othersrc/public/bc-1.01/fix_math.h new file mode 100755 index 0000000000..020e4112df --- /dev/null +++ b/usr/othersrc/public/bc-1.01/fix_math.h @@ -0,0 +1,8 @@ +ed math.h < 1) { + f += 1; + x /= 2; + } + + /* Initialize the variables. */ + v = 1+x + a = x + d = 1 + + for (i=2; 1; i++) { + e = (a *= x) / (d *= i) + if (e == 0) { + if (f>0) while (f--) v = v*v; + scale = z + if (m) return (1/v); + return (v/1); + } + v += e + } +} + +/* Natural log. Uses the fact that ln(x^2) = 2*ln(x) + The series used is: + ln(x) = 2(a+a^3/3+a^5/5+...) where a=(x-1)/(x+1) +*/ + +define l(x) { + auto e, f, i, m, n, v, z + + /* return something for the special case. */ + if (x <= 0) return (1 - 10^scale) + + /* Precondition x to make .5 < x < 2.0. */ + z = scale; + scale += 4; + f = 2; + i=0 + while (x >= 2) { /* for large numbers */ + f *= 2; + x = sqrt(x); + } + while (x <= .5) { /* for small numbers */ + f *= 2; + x = sqrt(x); + } + + /* Set up the loop. */ + v = n = (x-1)/(x+1) + m = n*n + + /* Sum the series. */ + for (i=3; 1; i+=2) { + e = (n *= m) / i + if (e == 0) { + v = f*v + scale = z + return (v/1) + } + v += e + } +} + +/* Sin(x) uses the standard series: + sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... */ + +define s(x) { + auto e, i, m, n, s, v, z + + /* precondition x. */ + z = scale + scale = 1.1*z + 1; + v = a(1) + if (x < 0) { + m = 1; + x = -x; + } + scale = 0 + n = (x / v + 2 )/4 + x = x - 4*n*v + if (n%2) x = -x + + /* Do the loop. */ + scale = z + 2; + v = e = x + s = -x*x + for (i=3; 1; i+=2) { + e *= s/(i*(i-1)) + if (e == 0) { + scale = z + if (m) return (-v/1); + return (v/1); + } + v += e + } +} + +/* Cosine : cos(x) = sin(x+pi/2) */ +define c(x) { + auto v; + scale += 1; + v = s(x+a(1)*2); + scale -= 1; + return (v/1); +} + +/* Arctan: Using the formula: + atan(x) = atan(c) + atan((x-c)/(1+xc)) for a small c (.2 here) + For under .2, use the series: + atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ... */ + +define a(x) { + auto a, e, f, i, m, n, s, v, z + + /* Special case and for fast answers */ + if (x==1) { + if (scale <= 25) return (.7853981633974483096156608/1) + if (scale <= 40) return (.7853981633974483096156608458198757210492/1) + if (scale <= 60) \ + return (.785398163397448309615660845819875721049292349843776455243736/1) + } + if (x==.2) { + if (scale <= 25) return (.1973955598498807583700497/1) + if (scale <= 40) return (.1973955598498807583700497651947902934475/1) + if (scale <= 60) \ + return (.197395559849880758370049765194790293447585103787852101517688/1) + } + + /* Negative x? */ + if (x<0) { + m = 1; + x = -x; + } + + /* Save the scale. */ + z = scale; + + /* Note: a and f are known to be zero due to being auto vars. */ + /* Calculate atan of a known number. */ + if (x > .2) { + scale = z+4; + a = a(.2); + } + + /* Precondition x. */ + scale = z+2; + while (x > .2) { + f += 1; + x = (x-.2) / (1+x*.2); + } + + /* Initialize the series. */ + v = n = x; + s = -x*x; + + /* Calculate the series. */ + for (i=3; 1; i+=2) { + e = (n *= s) / i; + if (e == 0) { + scale = z; + if (m) return ((f*a+v)/-1); + return ((f*a+v)/1); + } + v += e + } +} + + +/* Bessel function of integer order. Uses the following: + j(-n,x) = (-1)^n*j(n,x) + j(n,x) = x^n/(2^n*n!) * (1 - x^2/(2^2*1!*(n+1)) + x^4/(2^4*2!*(n+1)*(n+2)) + - x^6/(2^6*3!*(n+1)*(n+2)*(n+3)) .... ) +*/ +define j(n,x) { + auto a, d, e, f, i, m, s, v, z + + /* Make n an integer and check for negative n. */ + z = scale; + scale = 0; + n = n/1; + if (n<0) { + n = -n; + if (n%2 == 1) m = 1; + } + + /* Compute the factor of x^n/(2^n*n!) */ + f = 1; + for (i=2; i<=n; i++) f = f*i; + scale = 1.5*z; + f = x^n / 2^n / f; + + /* Initialize the loop .*/ + v = e = 1; + s = -x*x/4 + scale = 1.5*z + + /* The Loop.... */ + for (i=1; 1; i++) { + e = e * s / i / (n+i); + if (e == 0) { + scale = z + if (m) return (-f*v/1); + return (f*v/1); + } + v += e; + } +} diff --git a/usr/othersrc/public/bc-1.01/load.c b/usr/othersrc/public/bc-1.01/load.c new file mode 100644 index 0000000000..2d61a7529c --- /dev/null +++ b/usr/othersrc/public/bc-1.01/load.c @@ -0,0 +1,322 @@ +/* load.c: This code "loads" code into the code segments. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" +#include "proto.h" + +/* Load variables. */ + +program_counter load_adr; +char load_str; +char load_const; + +/* Initialize the load sequence. */ +void +init_load () +{ + clear_func(0); + load_adr.pc_func = 0; + load_adr.pc_addr = 0; + load_str = FALSE; + load_const = FALSE; +} + +/* addbyte adds one BYTE to the current code segment. */ +void +addbyte (byte) + char byte; +{ + int seg, offset, func; + + /* Calculate the segment and offset. */ + seg = load_adr.pc_addr >> BC_SEG_LOG; + offset = load_adr.pc_addr++ % BC_SEG_SIZE; + func = load_adr.pc_func; + + if (functions[func].f_body[seg] == NULL) + functions[func].f_body[seg] = (char *) bc_malloc (BC_SEG_SIZE); + + /* Store the byte. */ + functions[func].f_body[seg][offset] = byte; + functions[func].f_code_size++; +} + + +/* Define a label LAB to be the current program counter. */ + +void +def_label (lab) + long lab; +{ + bc_label_group *temp; + int group, offset, func; + + /* Get things ready. */ + group = lab >> BC_LABEL_LOG; + offset = lab % BC_LABEL_GROUP; + func = load_adr.pc_func; + + /* Make sure there is at least one label group. */ + if (functions[func].f_label == NULL) + { + functions[func].f_label = + (bc_label_group *) bc_malloc (sizeof(bc_label_group)); + functions[func].f_label->l_next = NULL; + } + + /* Add the label group. */ + temp = functions[func].f_label; + while (group > 0) + { + if (temp->l_next == NULL) + { + temp->l_next = (bc_label_group *) bc_malloc (sizeof(bc_label_group)); + temp->l_next->l_next = NULL; + } + temp = temp->l_next; + group --; + } + + /* Define it! */ + temp->l_adrs [offset] = load_adr.pc_addr; +} + +/* Several instructions have integers in the code. They + are all known to be legal longs. So, no error code + is added. STR is the pointer to the load string and + must be moved to the last non-digit character. */ + +long +long_val (str) + char **str; +{ int val = 0; + char neg = FALSE; + + if (**str == '-') + { + neg = TRUE; + (*str)++; + } + while (isdigit(**str)) + val = val*10 + *(*str)++ - '0'; + + if (neg) + return -val; + else + return val; +} + + +/* load_code loads the CODE into the machine. */ + +void +load_code (code) + char *code; +{ + char *str; + long ap_name; /* auto or parameter name. */ + long label_no; + long vaf_name; /* variable, array or function number. */ + long func; + program_counter save_adr; + + /* Initialize. */ + str = code; + + /* Scan the code. */ + while (*str != 0) + { + if (load_str) + { + if (*str == '"') load_str = FALSE; + addbyte (*str++); + } + else + if (load_const) + { + if (*str == '\n') + str++; + else + { + if (*str == ':') + { + load_const = FALSE; + addbyte (*str++); + } + else + if (*str == '.') + addbyte (*str++); + else + if (*str >= 'A') + addbyte (*str++ + 10 - 'A'); + else + addbyte (*str++ - '0'); + } + } + else + { + switch (*str) + { + + case '"': /* Starts a string. */ + load_str = TRUE; + break; + + case 'N': /* A label */ + str++; + label_no = long_val (&str); + def_label (label_no); + break; + + case 'B': /* Branch to label. */ + case 'J': /* Jump to label. */ + case 'Z': /* Branch Zero to label. */ + addbyte(*str++); + label_no = long_val (&str); + if (label_no > 65535L) + { /* Better message? */ + fprintf (stderr,"Program too big.\n"); + exit(1); + } + addbyte ( (char) label_no & 0xFF); + addbyte ( (char) label_no >> 8); + break; + + case 'F': /* A function, get the name and initialize it. */ + str++; + func = long_val (&str); + clear_func (func); +#if DEBUG > 2 + printf ("Loading function number %d\n", func); +#endif + /* get the parameters */ + while (*str++ != '.') + { + if (*str == '.') + { + str++; + break; + } + ap_name = long_val (&str); +#if DEBUG > 2 + printf ("parameter number %d\n", ap_name); +#endif + functions[(int)func].f_params = + nextarg (functions[(int)func].f_params, ap_name); + } + + /* get the auto vars */ + while (*str != '[') + { + if (*str == ',') str++; + ap_name = long_val (&str); +#if DEBUG > 2 + printf ("auto number %d\n", ap_name); +#endif + functions[(int)func].f_autos = + nextarg (functions[(int)func].f_autos, ap_name); + } + save_adr = load_adr; + load_adr.pc_func = func; + load_adr.pc_addr = 0; + break; + + case ']': /* A function end */ + if (!had_error) + functions[load_adr.pc_func].f_defined = TRUE; + load_adr = save_adr; + break; + + case 'C': /* Call a function. */ + addbyte (*str++); + func = long_val (&str); + if (func < 128) + addbyte ( (char) func); + else + { + addbyte ((func >> 8) & 0xff | 0x80); + addbyte (func & 0xff); + } + if (*str == ',') str++; + while (*str != ':') + addbyte (*str++); + addbyte (':'); + break; + + case 'c': /* Call a special function. */ + addbyte (*str++); + addbyte (*str); + break; + + case 'K': /* A constant.... may have an "F" in it. */ + addbyte (*str); + load_const = TRUE; + break; + + case 'd': /* Decrement. */ + case 'i': /* Increment. */ + case 'l': /* Load. */ + case 's': /* Store. */ + case 'A': /* Array Increment */ + case 'M': /* Array Decrement */ + case 'L': /* Array Load */ + case 'S': /* Array Store */ + addbyte (*str++); + vaf_name = long_val (&str); + if (vaf_name < 128) + addbyte (vaf_name); + else + { + addbyte ((vaf_name >> 8) & 0xff | 0x80); + addbyte (vaf_name & 0xff); + } + break; + + case '@': /* A command! */ + switch (*(++str)) + { + case 'i': + init_load (); + break; + case 'r': + execute (); + break; + } + break; + + case '\n': /* Ignore the newlines */ + break; + + default: /* Anything else */ + addbyte (*str); + } + str++; + } + } +} diff --git a/usr/othersrc/public/bc-1.01/main.c b/usr/othersrc/public/bc-1.01/main.c new file mode 100644 index 0000000000..1907fdba81 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/main.c @@ -0,0 +1,204 @@ +/* main.c: The main program for bc. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include +#include "global.h" +#include "proto.h" + +/* Variables for processing multiple files. */ +char first_file; +extern FILE *yyin; + + +/* The main program for bc. */ +int +main (argc, argv) + int argc; + char *argv[]; +{ + int ch; + + /* Initialize many variables. */ + compile_only = FALSE; + use_math = FALSE; + warn_not_std = FALSE; + std_only = FALSE; + if (isatty(0) && isatty(1)) + interactive = TRUE; + else + interactive = FALSE; + + /* Parse the command line */ + ch = getopt (argc, argv, "lcisvw"); + while (ch != EOF) + { + switch (ch) + { + case 'c': /* compile only */ + compile_only = TRUE; + break; + case 'l': /* math lib */ + use_math = TRUE; + break; + case 'i': /* force interactive */ + interactive = TRUE; + break; + case 'w': /* Non standard features give warnings. */ + warn_not_std = TRUE; + break; + case 's': /* Non standard features give errors. */ + std_only = TRUE; + break; + case 'v': /* Print the version. */ + printf ("%s\n", BC_VERSION); + break; + } + ch = getopt (argc, argv, "lcisvw"); + } + + /* Initialize the machine. */ + init_storage(); + init_load(); + + /* Set up interrupts to print a message. */ + if (interactive) + signal (SIGINT, use_quit); + + /* Initialize the front end. */ + init_tree(); + init_gen (); + g_argv = argv; + g_argc = argc; + is_std_in = FALSE; + first_file = TRUE; + if (!open_new_file ()) + exit (1); + + /* Do the parse. */ + yyparse (); + + /* End the compile only output with a newline. */ + if (compile_only) + printf ("\n"); + + exit (0); +} + + +/* This is the function that opens all the files. + It returns TRUE if the file was opened, otherwise + it returns FALSE. */ + +int +open_new_file () +{ + FILE *new_file; + + /* Set the line number. */ + line_no = 1; + + /* Check to see if we are done. */ + if (is_std_in) return (FALSE); + + /* Open the other files. */ + if (use_math) + { +#ifdef BC_MATH_FILE + /* Make the first file be the math library. */ + new_file = fopen (BC_MATH_FILE, "r"); + use_math = FALSE; + if (new_file != NULL) + { + new_yy_file (new_file); + return TRUE; + } + else + { + fprintf (stderr, "Math Library unavailable.\n"); + exit (1); + } +#else + /* Load the code from a precompiled version of the math libarary. */ + extern char libmath[]; + char tmp; + /* These MUST be in the order of first mention of each function. + That is why "a" comes before "c" even though "a" is defined after + after "c". "a" is used in "s"! */ + tmp = lookup ("e", FUNCT); + tmp = lookup ("l", FUNCT); + tmp = lookup ("s", FUNCT); + tmp = lookup ("a", FUNCT); + tmp = lookup ("c", FUNCT); + tmp = lookup ("j", FUNCT); + load_code (libmath); +#endif + } + + /* One of the argv values. */ + while (optind < g_argc) + { + new_file = fopen (g_argv[optind], "r"); + if (new_file != NULL) + { + new_yy_file (new_file); + optind++; + return TRUE; + } + fprintf (stderr, "File %s is unavailable.\n", g_argv[optind++]); + exit (1); + } + + /* If we fall through to here, we should return stdin. */ + new_yy_file (stdin); + is_std_in = TRUE; + return TRUE; +} + + +/* Set yyin to the new file. */ + +void +new_yy_file (file) + FILE *file; +{ + if (!first_file) fclose (yyin); + yyin = file; + first_file = FALSE; +} + + +/* Message to use quit. */ + +void +use_quit (sig) + int sig; +{ + printf ("\n(interrupt) use quit to exit.\n"); + signal (SIGINT, use_quit); +} diff --git a/usr/othersrc/public/bc-1.01/number.c b/usr/othersrc/public/bc-1.01/number.c new file mode 100644 index 0000000000..8a0f025a50 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/number.c @@ -0,0 +1,1402 @@ +/* number.c: Implements arbitrary precision numbers. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "proto.h" + +/* Storage used for special numbers. */ +bc_num _zero_; +bc_num _one_; +bc_num _two_; + + +/* "Frees" a bc_num NUM. Actually decreases reference count and only + frees the storage if reference count is zero. */ + +void +free_num (num) + bc_num *num; +{ + if (*num == NULL) return; + (*num)->n_refs--; + if ((*num)->n_refs == 0) free(*num); + *num = NULL; +} + + +/* new_num allocates a number and sets fields to known values. */ + +bc_num +new_num (length, scale) + int length, scale; +{ + bc_num temp; + + temp = (bc_num) malloc (sizeof(bc_struct)+length+scale); + if (temp == NULL) out_of_memory (); + temp->n_sign = PLUS; + temp->n_len = length; + temp->n_scale = scale; + temp->n_refs = 1; + temp->n_value[0] = 0; + return temp; +} + + +/* Intitialize the number package! */ + +void +init_numbers () +{ + _zero_ = new_num (1,0); + _one_ = new_num (1,0); + _one_->n_value[0] = 1; + _two_ = new_num (1,0); + _two_->n_value[0] = 2; +} + + +/* Make a copy of a number! Just increments the reference count! */ + +bc_num +copy_num (num) + bc_num num; +{ + num->n_refs++; + return num; +} + + +/* Initialize a number NUM by making it a copy of zero. */ + +void +init_num (num) + bc_num *num; +{ + *num = copy_num (_zero_); +} + + +/* Convert an integer VAL to a bc number NUM. */ + +void +int2num (num, val) + bc_num *num; + int val; +{ + char buffer[30]; + char *bptr, *vptr; + int ix = 1; + char neg = 0; + + /* Sign. */ + if (val < 0) + { + neg = 1; + val = -val; + } + + /* Get things going. */ + bptr = buffer; + *bptr++ = val % 10; + val = val / 10; + + /* Extract remaining digits. */ + while (val != 0) + { + *bptr++ = val % 10; + val = val / 10; + ix++; /* Count the digits. */ + } + + /* Make the number. */ + free_num (num); + *num = new_num (ix, 0); + if (neg) (*num)->n_sign = MINUS; + + /* Assign the digits. */ + vptr = (*num)->n_value; + while (ix-- > 0) + *vptr++ = *--bptr; +} + + +/* Convert a number NUM to a long. The function returns only the integer + part of the number. For numbers that are too large to represent as + a long, this function returns a zero. This can be detected by checking + the NUM for zero after having a zero returned. */ + +long +num2long (num) + bc_num num; +{ + long val; + char *nptr; + int index; + + /* Extract the int value, ignore the fraction. */ + val = 0; + nptr = num->n_value; + for (index=num->n_len; (index>0) && (val<=(LONG_MAX/10)); index--) + val = val*10 + *nptr++; + + /* Check for overflow. If overflow, return zero. */ + if (index>0) val = 0; + if (val < 0) val = 0; + + /* Return the value. */ + if (num->n_sign == PLUS) + return (val); + else + return (-val); +} + + +/* The following are some math routines for numbers. */ +_PROTOTYPE(static int _do_compare, (bc_num n1, bc_num n2, int use_sign, + int ignore_last)); +_PROTOTYPE(static void _rm_leading_zeros, (bc_num num)); +_PROTOTYPE(static bc_num _do_add, (bc_num n1, bc_num n2)); +_PROTOTYPE(static bc_num _do_sub, (bc_num n1, bc_num n2)); +_PROTOTYPE(static void _one_mult, (unsigned char *num, int size, int digit, + unsigned char *result)); + + + +/* Compare two bc numbers. Return value is 0 if equal, -1 if N1 is less + than N2 and +1 if N1 is greater than N2. If USE_SIGN is false, just + compare the magnitudes. */ + +static int +_do_compare (n1, n2, use_sign, ignore_last) + bc_num n1, n2; + int use_sign; + int ignore_last; +{ + char *n1ptr, *n2ptr; + int count; + + /* First, compare signs. */ + if (use_sign && n1->n_sign != n2->n_sign) + { + if (n1->n_sign == PLUS) + return (1); /* Positive N1 > Negative N2 */ + else + return (-1); /* Negative N1 < Positive N1 */ + } + + /* Now compare the magnitude. */ + if (n1->n_len != n2->n_len) + { + if (n1->n_len > n2->n_len) + { + /* Magnitude of n1 > n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (1); + else + return (-1); + } + else + { + /* Magnitude of n1 < n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (-1); + else + return (1); + } + } + + /* If we get here, they have the same number of integer digits. + check the integer part and the equal length part of the fraction. */ + count = n1->n_len + MIN (n1->n_scale, n2->n_scale); + n1ptr = n1->n_value; + n2ptr = n2->n_value; + + while ((count > 0) && (*n1ptr == *n2ptr)) + { + n1ptr++; + n2ptr++; + count--; + } + if (ignore_last && count == 1 && n1->n_scale == n2->n_scale) + return (0); + if (count != 0) + { + if (*n1ptr > *n2ptr) + { + /* Magnitude of n1 > n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (1); + else + return (-1); + } + else + { + /* Magnitude of n1 < n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (-1); + else + return (1); + } + } + + /* They are equal up to the last part of the equal part of the fraction. */ + if (n1->n_scale != n2->n_scale) + if (n1->n_scale > n2->n_scale) + { + for (count = n1->n_scale-n2->n_scale; count>0; count--) + if (*n1ptr++ != 0) + { + /* Magnitude of n1 > n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (1); + else + return (-1); + } + } + else + { + for (count = n2->n_scale-n1->n_scale; count>0; count--) + if (*n2ptr++ != 0) + { + /* Magnitude of n1 < n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (-1); + else + return (1); + } + } + + /* They must be equal! */ + return (0); +} + + +/* This is the "user callable" routine to compare numbers N1 and N2. */ + +int +bc_compare (n1, n2) + bc_num n1, n2; +{ + return _do_compare (n1, n2, TRUE, FALSE); +} + + +/* In some places we need to check if the number NUM is zero. */ + +char +is_zero (num) + bc_num num; +{ + int count; + char *nptr; + + /* Quick check. */ + if (num == _zero_) return TRUE; + + /* Initialize */ + count = num->n_len + num->n_scale; + nptr = num->n_value; + + /* The check */ + while ((count > 0) && (*nptr++ == 0)) count--; + + if (count != 0) + return FALSE; + else + return TRUE; +} + + +/* In some places we need to check if the number is negative. */ + +char +is_neg (num) + bc_num num; +{ + return num->n_sign == MINUS; +} + + +/* For many things, we may have leading zeros in a number NUM. + _rm_leading_zeros just moves the data to the correct + place and adjusts the length. */ + +static void +_rm_leading_zeros (num) + bc_num num; +{ + int bytes; + char *dst, *src; + + /* Do a quick check to see if we need to do it. */ + if (*num->n_value != 0) return; + + /* The first digit is 0, find the first non-zero digit in the 10's or + greater place. */ + bytes = num->n_len; + src = num->n_value; + while (bytes > 1 && *src == 0) src++, bytes--; + num->n_len = bytes; + bytes += num->n_scale; + dst = num->n_value; + while (bytes-- > 0) *dst++ = *src++; + +} + + +/* Perform addition: N1 is added to N2 and the value is + returned. The signs of N1 and N2 are ignored. */ + +static bc_num +_do_add (n1, n2) + bc_num n1, n2; +{ + bc_num sum; + int sum_scale, sum_digits; + char *n1ptr, *n2ptr, *sumptr; + int carry, n1bytes, n2bytes; + + /* Prepare sum. */ + sum_scale = MAX (n1->n_scale, n2->n_scale); + sum_digits = MAX (n1->n_len, n2->n_len) + 1; + sum = new_num (sum_digits,sum_scale); + + /* Start with the fraction part. Initialize the pointers. */ + n1bytes = n1->n_scale; + n2bytes = n2->n_scale; + n1ptr = (char *) (n1->n_value + n1->n_len + n1bytes - 1); + n2ptr = (char *) (n2->n_value + n2->n_len + n2bytes - 1); + sumptr = (char *) (sum->n_value + sum_scale + sum_digits - 1); + + /* Add the fraction part. First copy the longer fraction.*/ + if (n1bytes != n2bytes) + { + if (n1bytes > n2bytes) + while (n1bytes>n2bytes) + { *sumptr-- = *n1ptr--; n1bytes--;} + else + while (n2bytes>n1bytes) + { *sumptr-- = *n2ptr--; n2bytes--;} + } + + /* Now add the remaining fraction part and equal size integer parts. */ + n1bytes += n1->n_len; + n2bytes += n2->n_len; + carry = 0; + while ((n1bytes > 0) && (n2bytes > 0)) + { + *sumptr = *n1ptr-- + *n2ptr-- + carry; + if (*sumptr > 9) + { + carry = 1; + *sumptr -= 10; + } + else + carry = 0; + sumptr--; + n1bytes--; + n2bytes--; + } + + /* Now add carry the longer integer part. */ + if (n1bytes == 0) + { n1bytes = n2bytes; n1ptr = n2ptr; } + while (n1bytes-- > 0) + { + *sumptr = *n1ptr-- + carry; + if (*sumptr > 9) + { + carry = 1; + *sumptr -= 10; + } + else + carry = 0; + sumptr--; + } + + /* Set final carry. */ + if (carry == 1) + *sumptr += 1; + + /* Adjust sum and return. */ + _rm_leading_zeros (sum); + return sum; +} + + +/* Perform subtraction: N2 is subtracted from N1 and the value is + returned. The signs of N1 and N2 are ignored. Also, N1 is + assumed to be larger than N2. */ + +static bc_num +_do_sub (n1, n2) + bc_num n1, n2; +{ + bc_num diff; + int diff_scale, diff_len; + int min_scale, min_len; + char *n1ptr, *n2ptr, *diffptr; + int borrow, count, val; + + /* Allocate temporary storage. */ + diff_len = MAX (n1->n_len, n2->n_len); + diff_scale = MAX (n1->n_scale, n2->n_scale); + min_len = MIN (n1->n_len, n2->n_len); + min_scale = MIN (n1->n_scale, n2->n_scale); + diff = new_num (diff_len, diff_scale); + + /* Initialize the subtract. */ + n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale -1); + n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale -1); + diffptr = (char *) (diff->n_value + diff_len + diff_scale -1); + + /* Subtract the numbers. */ + borrow = 0; + + /* Take care of the longer scaled number. */ + if (n1->n_scale != min_scale) + { + /* n1 has the longer scale */ + for (count = n1->n_scale - min_scale; count > 0; count--) + *diffptr-- = *n1ptr--; + } + else + { + /* n2 has the longer scale */ + for (count = n2->n_scale - min_scale; count > 0; count--) + { + val = - *n2ptr-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *diffptr-- = val; + } + } + + /* Now do the equal length scale and integer parts. */ + + for (count = 0; count < min_len + min_scale; count++) + { + val = *n1ptr-- - *n2ptr-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *diffptr-- = val; + } + + /* If n1 has more digits then n2, we now do that subtract. */ + if (diff_len != min_len) + { + for (count = diff_len - min_len; count > 0; count--) + { + val = *n1ptr-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *diffptr-- = val; + } + } + + /* Clean up and return. */ + _rm_leading_zeros (diff); + return diff; +} + + +/* Here is the full add routine that takes care of negative numbers. + N1 is added to N2 and the result placed into RESULT. */ + +void +bc_add ( n1, n2, result) + bc_num n1, n2, *result; +{ + bc_num sum; + int cmp_res; + + if (n1->n_sign == n2->n_sign) + { + sum = _do_add (n1, n2); + sum->n_sign = n1->n_sign; + } + else + { + /* subtraction must be done. */ + cmp_res = _do_compare (n1, n2, FALSE, FALSE); /* Compare magnitudes. */ + switch (cmp_res) + { + case -1: + /* n1 is less than n2, subtract n1 from n2. */ + sum = _do_sub (n2, n1); + sum->n_sign = n2->n_sign; + break; + case 0: + /* They are equal! return zero! */ + sum = copy_num (_zero_); + break; + case 1: + /* n2 is less than n1, subtract n2 from n1. */ + sum = _do_sub (n1, n2); + sum->n_sign = n1->n_sign; + } + } + + /* Clean up and return. */ + free_num (result); + *result = sum; +} + + +/* Here is the full subtract routine that takes care of negative numbers. + N2 is subtracted from N1 and the result placed in RESULT. */ + +void +bc_sub ( n1, n2, result) + bc_num n1, n2, *result; +{ + bc_num diff; + int cmp_res; + + if (n1->n_sign != n2->n_sign) + { + diff = _do_add (n1, n2); + diff->n_sign = n1->n_sign; + } + else + { + /* subtraction must be done. */ + cmp_res = _do_compare (n1, n2, FALSE, FALSE); /* Compare magnitudes. */ + switch (cmp_res) + { + case -1: + /* n1 is less than n2, subtract n1 from n2. */ + diff = _do_sub (n2, n1); + diff->n_sign = (n2->n_sign == PLUS ? MINUS : PLUS); + break; + case 0: + /* They are equal! return zero! */ + diff = copy_num (_zero_); + break; + case 1: + /* n2 is less than n1, subtract n2 from n1. */ + diff = _do_sub (n1, n2); + diff->n_sign = n1->n_sign; + break; + } + } + + /* Clean up and return. */ + free_num (result); + *result = diff; +} + + +/* The multiply routine. N2 time N1 is put int PROD with the scale of + the result being MIN(N2 scale+N1 scale, MAX (SCALE, N2 scale, N1 scale)). + */ + +void +bc_multiply (n1, n2, prod, scale) + bc_num n1, n2, *prod; + int scale; +{ + bc_num pval; /* For the working storage. */ + char *n1ptr, *n2ptr, *pvptr; /* Work pointers. */ + char *n1end, *n2end; /* To the end of n1 and n2. */ + + int indx; + int len1, len2, total_digits; + long sum; + int full_scale, prod_scale; + int toss; + + /* Initialize things. */ + len1 = n1->n_len + n1->n_scale; + len2 = n2->n_len + n2->n_scale; + total_digits = len1 + len2; + full_scale = n1->n_scale + n2->n_scale; + prod_scale = MIN(full_scale,MAX(scale,MAX(n1->n_scale,n2->n_scale))); + toss = full_scale - prod_scale; + pval = new_num (total_digits-full_scale, prod_scale); + pval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS ); + n1end = (char *) (n1->n_value + len1 - 1); + n2end = (char *) (n2->n_value + len2 - 1); + pvptr = (char *) (pval->n_value + total_digits - toss - 1); + sum = 0; + + /* Here are the loops... */ + for (indx = 0; indx < toss; indx++) + { + n1ptr = (char *) (n1end - MAX(0, indx-len2+1)); + n2ptr = (char *) (n2end - MIN(indx, len2-1)); + while ((n1ptr >= n1->n_value) && (n2ptr <= n2end)) + sum += *n1ptr-- * *n2ptr++; + sum = sum / 10; + } + for ( ; indx < total_digits-1; indx++) + { + n1ptr = (char *) (n1end - MAX(0, indx-len2+1)); + n2ptr = (char *) (n2end - MIN(indx, len2-1)); + while ((n1ptr >= n1->n_value) && (n2ptr <= n2end)) + sum += *n1ptr-- * *n2ptr++; + *pvptr-- = sum % 10; + sum = sum / 10; + } + *pvptr-- = sum; + + /* Assign to prod and clean up the number. */ + free_num (prod); + *prod = pval; + _rm_leading_zeros (*prod); + if (is_zero (*prod)) + (*prod)->n_sign = PLUS; +} + + +/* Some utility routines for the divide: First a one digit multiply. + NUM (with SIZE digits) is multiplied by DIGIT and the result is + placed into RESULT. It is written so that NUM and RESULT can be + the same pointers. */ + +static void +_one_mult (num, size, digit, result) + unsigned char *num; + int size, digit; + unsigned char *result; +{ + int carry, value; + unsigned char *nptr, *rptr; + + if (digit == 0) + memset (result, 0, size); + else + { + if (digit == 1) + memcpy (result, num, size); + else + { + /* Initialize */ + nptr = (unsigned char *) (num+size-1); + rptr = (unsigned char *) (result+size-1); + carry = 0; + + while (size-- > 0) + { + value = *nptr-- * digit + carry; + *rptr-- = value % 10; + carry = value / 10; + } + + if (carry != 0) *rptr = carry; + } + } +} + + +/* The full division routine. This computes N1 / N2. It returns + 0 if the division is ok and the result is in QUOT. The number of + digits after the decimal point is SCALE. It returns -1 if division + by zero is tried. The algorithm is found in Knuth Vol 2. p237. */ + +int +bc_divide (n1, n2, quot, scale) + bc_num n1, n2, *quot; + int scale; +{ + bc_num qval; + unsigned char *num1, *num2; + unsigned char *ptr1, *ptr2, *n2ptr, *qptr; + int scale1, val; + unsigned int len1, len2, scale2, qdigits, extra, count; + unsigned int qdig, qguess, borrow, carry; + unsigned char *mval; + char zero; + unsigned int norm; + + /* Test for divide by zero. */ + if (is_zero (n2)) return -1; + + /* Test for divide by 1. If it is we must truncate. */ + if (n2->n_scale == 0) + { + if (n2->n_len == 1 && *n2->n_value == 1) + { + qval = new_num (n1->n_len, scale); + qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS); + memset (&qval->n_value[n1->n_len],0,scale); + memcpy (qval->n_value, n1->n_value, + n1->n_len + MIN(n1->n_scale,scale)); + free_num (quot); + *quot = qval; + } + } + + /* Set up the divide. Move the decimal point on n1 by n2's scale. + Remember, zeros on the end of num2 are wasted effort for dividing. */ + scale2 = n2->n_scale; + n2ptr = (unsigned char *) n2->n_value+n2->n_len+scale2-1; + while ((scale2 > 0) && (*n2ptr-- == 0)) scale2--; + + len1 = n1->n_len + scale2; + scale1 = n1->n_scale - scale2; + if (scale1 < scale) + extra = scale - scale1; + else + extra = 0; + num1 = (unsigned char *) malloc (n1->n_len+n1->n_scale+extra+2); + if (num1 == NULL) out_of_memory(); + memset (num1, 0, n1->n_len+n1->n_scale+extra+2); + memcpy (num1+1, n1->n_value, n1->n_len+n1->n_scale); + + len2 = n2->n_len + scale2; + num2 = (unsigned char *) malloc (len2+1); + if (num2 == NULL) out_of_memory(); + memcpy (num2, n2->n_value, len2); + *(num2+len2) = 0; + n2ptr = num2; + while (*n2ptr == 0) + { + n2ptr++; + len2--; + } + + /* Calculate the number of quotient digits. */ + if (len2 > len1+scale) + { + qdigits = scale+1; + zero = TRUE; + } + else + { + zero = FALSE; + if (len2>len1) + qdigits = scale+1; /* One for the zero integer part. */ + else + qdigits = len1-len2+scale+1; + } + + /* Allocate and zero the storage for the quotient. */ + qval = new_num (qdigits-scale,scale); + memset (qval->n_value, 0, qdigits); + + /* Allocate storage for the temporary storage mval. */ + mval = (unsigned char *) malloc (len2+1); + if (mval == NULL) out_of_memory (); + + /* Now for the full divide algorithm. */ + if (!zero) + { + /* Normalize */ + norm = 10 / ((int)*n2ptr + 1); + if (norm != 1) + { + _one_mult (num1, len1+scale1+extra+1, norm, num1); + _one_mult (n2ptr, len2, norm, n2ptr); + } + + /* Initialize divide loop. */ + qdig = 0; + if (len2 > len1) + qptr = (unsigned char *) qval->n_value+len2-len1; + else + qptr = (unsigned char *) qval->n_value; + + /* Loop */ + while (qdig <= len1+scale-len2) + { + /* Calculate the quotient digit guess. */ + if (*n2ptr == num1[qdig]) + qguess = 9; + else + qguess = (num1[qdig]*10 + num1[qdig+1]) / *n2ptr; + + /* Test qguess. */ + if (n2ptr[1]*qguess > + (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10 + + num1[qdig+2]) + { + qguess--; + /* And again. */ + if (n2ptr[1]*qguess > + (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10 + + num1[qdig+2]) + qguess--; + } + + /* Multiply and subtract. */ + borrow = 0; + if (qguess != 0) + { + *mval = 0; + _one_mult (n2ptr, len2, qguess, mval+1); + ptr1 = (unsigned char *) num1+qdig+len2; + ptr2 = (unsigned char *) mval+len2; + for (count = 0; count < len2+1; count++) + { + val = (int) *ptr1 - (int) *ptr2-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *ptr1-- = val; + } + } + + /* Test for negative result. */ + if (borrow == 1) + { + qguess--; + ptr1 = (unsigned char *) num1+qdig+len2; + ptr2 = (unsigned char *) n2ptr+len2-1; + carry = 0; + for (count = 0; count < len2; count++) + { + val = (int) *ptr1 + (int) *ptr2-- + carry; + if (val > 9) + { + val -= 10; + carry = 1; + } + else + carry = 0; + *ptr1-- = val; + } + if (carry == 1) *ptr1 = (*ptr1 + 1) % 10; + } + + /* We now know the quotient digit. */ + *qptr++ = qguess; + qdig++; + } + } + + /* Clean up and return the number. */ + qval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS ); + if (is_zero (qval)) qval->n_sign = PLUS; + _rm_leading_zeros (qval); + free_num (quot); + *quot = qval; + + /* Clean up temporary storage. */ + free (mval); + free (num1); + free (num2); + + return 0; /* Everything is OK. */ +} + + +/* Modulo for numbers. This computes NUM1 % NUM2 and puts the + result in RESULT. */ + +int +bc_modulo (num1, num2, result, scale) + bc_num num1, num2, *result; + int scale; +{ + bc_num temp; + int rscale; + + /* Check for correct numbers. */ + if (is_zero (num2)) return -1; + + /* Calculate final scale. */ + rscale = MAX (num1->n_scale, num2->n_scale+scale); + init_num (&temp); + + /* Calculate it. */ + bc_divide (num1, num2, &temp, scale); + bc_multiply (temp, num2, &temp, rscale); + bc_sub (num1, temp, result); + free_num (&temp); + + return 0; /* Everything is OK. */ +} + + +/* Raise NUM1 to the NUM2 power. The result is placed in RESULT. + Maximum exponent is LONG_MAX. If a NUM2 is not an integer, + only the integer part is used. */ + +void +bc_raise (num1, num2, result, scale) + bc_num num1, num2, *result; + int scale; +{ + bc_num temp, power; + long exponent; + int rscale; + char neg; + + /* Check the exponent for scale digits and convert to a long. */ + if (num2->n_scale != 0) + rt_warn ("non-zero scale in exponent"); + exponent = num2long (num2); + if (exponent == 0 && (num2->n_len > 1 || num2->n_value[0] != 0)) + rt_error ("exponent too large in raise"); + + /* Special case if exponent is a zero. */ + if (exponent == 0) + { + free_num (result); + *result = copy_num (_one_); + return; + } + + /* Other initializations. */ + if (exponent < 0) + { + neg = TRUE; + exponent = -exponent; + rscale = scale; + } + else + { + neg = FALSE; + rscale = MIN (num1->n_scale*exponent, MAX(scale, num1->n_scale)); + } + temp = copy_num (_one_); + power = copy_num (num1); + + /* Do the calculation. */ + while (exponent != 0) + { + if (exponent & 1 != 0) + bc_multiply (temp, power, &temp, rscale); + bc_multiply (power, power, &power, rscale); + exponent = exponent >> 1; + } + + /* Assign the value. */ + if (neg) + { + bc_divide (_one_, temp, result, rscale); + free_num (&temp); + } + else + { + free_num (result); + *result = temp; + } + free_num (&power); +} + + +/* Take the square root NUM and return it in NUM with SCALE digits + after the decimal place. */ + +int +bc_sqrt (num, scale) + bc_num *num; + int scale; +{ + int rscale, cmp_res, done; + int cscale; + bc_num guess, guess1, point5; + + /* Initial checks. */ + cmp_res = bc_compare (*num, _zero_); + if (cmp_res < 0) + return 0; /* error */ + else + { + if (cmp_res == 0) + { + free_num (num); + *num = copy_num (_zero_); + return 1; + } + } + cmp_res = bc_compare (*num, _one_); + if (cmp_res == 0) + { + free_num (num); + *num = copy_num (_one_); + return 1; + } + + /* Initialize the variables. */ + rscale = MAX (scale, (*num)->n_scale); + cscale = rscale + 2; + init_num (&guess); + init_num (&guess1); + point5 = new_num (1,1); + point5->n_value[1] = 5; + + + /* Calculate the initial guess. */ + if (cmp_res < 0) + /* The number is between 0 and 1. Guess should start at 1. */ + guess = copy_num (_one_); + else + { + /* The number is greater than 1. Guess should start at 10^(exp/2). */ + int2num (&guess,10); + int2num (&guess1,(*num)->n_len); + bc_multiply (guess1, point5, &guess1, rscale); + guess1->n_scale = 0; + bc_raise (guess, guess1, &guess, rscale); + free_num (&guess1); + } + + /* Find the square root using Newton's algorithm. */ + done = FALSE; + while (!done) + { + free_num (&guess1); + guess1 = copy_num (guess); + bc_divide (*num,guess,&guess,cscale); + bc_add (guess,guess1,&guess); + bc_multiply (guess,point5,&guess,cscale); + cmp_res = _do_compare (guess,guess1,FALSE,TRUE); + if (cmp_res == 0) done = TRUE; + } + + /* Assign the number and clean up. */ + free_num (num); + bc_divide (guess,_one_,num,rscale); + free_num (&guess); + free_num (&guess1); + free_num (&point5); + return 1; +} + + +/* The following routines provide output for bcd numbers package + using the rules of POSIX bc for output. */ + +/* This structure is used for saving digits in the conversion process. */ +typedef struct stk_rec { + long digit; + struct stk_rec *next; +} stk_rec; + +/* The reference string for digits. */ +char ref_str[] = "0123456789ABCDEF"; + + +/* A special output routine for "multi-character digits." Exactly + SIZE characters must be output for the value VAL. If SPACE is + non-zero, we must output one space before the number. OUT_CHAR + is the actual routine for writing the characters. */ + +void +out_long (val, size, space, out_char) + long val; + int size, space; +#ifdef __STDC__ + void (*out_char)(int); +#else + void (*out_char)(); +#endif +{ + char digits[40]; + int len, ix; + + if (space) (*out_char) (' '); + sprintf (digits, "%ld", val); + len = strlen (digits); + while (size > len) + { + (*out_char) ('0'); + size--; + } + for (ix=0; ix < len; ix++) + (*out_char) (digits[ix]); +} + +/* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR + as the routine to do the actual output of the characters. */ + +void +out_num (num, o_base, out_char) + bc_num num; + int o_base; +#ifdef __STDC__ + void (*out_char)(int); +#else + void (*out_char)(); +#endif +{ + char *nptr; + int index, fdigit, pre_space; + stk_rec *digits, *temp; + bc_num int_part, frac_part, base, cur_dig, t_num; + + /* The negative sign if needed. */ + if (num->n_sign == MINUS) (*out_char) ('-'); + + /* Output the number. */ + if (is_zero (num)) + (*out_char) ('0'); + else + if (o_base == 10) + { + /* The number is in base 10, do it the fast way. */ + nptr = num->n_value; + if (num->n_len > 1 || *nptr != 0) + for (index=num->n_len; index>0; index--) + (*out_char) (BCD_CHAR(*nptr++)); + else + nptr++; + + /* Now the fraction. */ + if (num->n_scale > 0) + { + (*out_char) ('.'); + for (index=0; indexn_scale; index++) + (*out_char) (BCD_CHAR(*nptr++)); + } + } + else + { + /* The number is some other base. */ + digits = NULL; + init_num (&int_part); + bc_divide (num, _one_, &int_part, 0); + init_num (&frac_part); + init_num (&cur_dig); + init_num (&base); + bc_sub (num, int_part, &frac_part); + int2num (&base, o_base); + + /* Get the digits of the integer part and push them on a stack. */ + while (!is_zero (int_part)) + { + bc_modulo (int_part, base, &cur_dig, 0); + temp = (stk_rec *) malloc (sizeof(stk_rec)); + if (temp == NULL) out_of_memory(); + temp->digit = num2long (cur_dig); + temp->next = digits; + digits = temp; + bc_divide (int_part, base, &int_part, 0); + } + + /* Print the digits on the stack. */ + if (digits != NULL) + { + /* Output the digits. */ + while (digits != NULL) + { + temp = digits; + digits = digits->next; + if (o_base <= 16) + (*out_char) (ref_str[ (int) temp->digit]); + else + out_long (temp->digit, base->n_len, 1, out_char); + free (temp); + } + } + + /* Get and print the digits of the fraction part. */ + if (num->n_scale > 0) + { + (*out_char) ('.'); + pre_space = 0; + t_num = copy_num (_one_); + while (t_num->n_len <= num->n_scale) { + bc_multiply (frac_part, base, &frac_part, num->n_scale); + fdigit = num2long (frac_part); + int2num (&int_part, fdigit); + bc_sub (frac_part, int_part, &frac_part); + if (o_base <= 16) + (*out_char) (ref_str[fdigit]); + else { + out_long (fdigit, base->n_len, pre_space, out_char); + pre_space = 1; + } + bc_multiply (t_num, base, &t_num, 0); + } + } + + /* Clean up. */ + free_num (&int_part); + free_num (&frac_part); + free_num (&base); + free_num (&cur_dig); + } +} + + +#if DEBUG > 0 + +/* Debugging procedures. Some are just so one can call them from the + debugger. */ + +/* p_n prints the number NUM in base 10. */ + +void +p_n (num) + bc_num num; +{ + out_num (num, 10, out_char); + return 0; +} + + +/* p_b prints a character array as if it was a string of bcd digits. */ +void +p_v (name, num, len) + char *name; + unsigned char *num; + int len; +{ + int i; + printf ("%s=", name); + for (i=0; in_sign = MINUS; + ptr++; + } + else + { + (*num)->n_sign = PLUS; + if (*ptr == '+') ptr++; + } + while (*ptr == '0') ptr++; /* Skip leading zeros. */ + nptr = (*num)->n_value; + if (zero_int) + { + *nptr++ = 0; + digits = 0; + } + for (;digits > 0; digits--) + *nptr++ = CH_VAL(*ptr++); + + + /* Build the fractional part. */ + if (strscale > 0) + { + ptr++; /* skip the decimal point! */ + for (;strscale > 0; strscale--) + *nptr++ = CH_VAL(*ptr++); + } +} + +/* Convert a numbers to a string. Base 10 only.*/ + +char +*num2str (num) + bc_num num; +{ + char *str, *sptr; + char *nptr; + int index, signch; + + /* Allocate the string memory. */ + signch = ( num->n_sign == PLUS ? 0 : 1 ); /* Number of sign chars. */ + if (num->n_scale > 0) + str = (char *) malloc (num->n_len + num->n_scale + 2 + signch); + else + str = (char *) malloc (num->n_len + 1 + signch); + if (str == NULL) out_of_memory(); + + /* The negative sign if needed. */ + sptr = str; + if (signch) *sptr++ = '-'; + + /* Load the whole number. */ + nptr = num->n_value; + for (index=num->n_len; index>0; index--) + *sptr++ = BCD_CHAR(*nptr++); + + /* Now the fraction. */ + if (num->n_scale > 0) + { + *sptr++ = '.'; + for (index=0; indexn_scale; index++) + *sptr++ = BCD_CHAR(*nptr++); + } + + /* Terminate the string and return it! */ + *sptr = '\0'; + return (str); +} +#endif diff --git a/usr/othersrc/public/bc-1.01/number.h b/usr/othersrc/public/bc-1.01/number.h new file mode 100644 index 0000000000..c686133dbe --- /dev/null +++ b/usr/othersrc/public/bc-1.01/number.h @@ -0,0 +1,60 @@ +/* number.h: Arbitrary precision numbers header file. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + + +typedef enum {PLUS, MINUS} sign; + +typedef struct + { + sign n_sign; + int n_len; /* The number of digits before the decimal point. */ + int n_scale; /* The number of digits after the decimal point. */ + int n_refs; /* The number of pointers to this number. */ + char n_value[1]; /* The storage. Not zero char terminated. It is + allocated with all other fields. */ + } bc_struct; + +typedef bc_struct *bc_num; + +/* Some useful macros and constants. */ + +#define CH_VAL(c) (c - '0') +#define BCD_CHAR(d) (d + '0') + +#ifdef MIN +#undef MIN +#undef MAX +#endif +#define MAX(a,b) (a>b?a:b) +#define MIN(a,b) (a>b?b:a) +#define ODD(a) (a&1) + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif diff --git a/usr/othersrc/public/bc-1.01/proto.h b/usr/othersrc/public/bc-1.01/proto.h new file mode 100644 index 0000000000..d2b85ababa --- /dev/null +++ b/usr/othersrc/public/bc-1.01/proto.h @@ -0,0 +1,165 @@ +/* proto.h: Prototype function definitions for "external" functions. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +/* For the pc version using k&r ACK. (minix1.5 and earlier.) */ +#ifdef SHORTNAMES +#define init_numbers i_numbers +#define push_constant push__constant +#define load_const in_load_const +#define yy_get_next_buffer yyget_next_buffer +#define yy_init_buffer yyinit_buffer +#define yy_last_accepting_state yylast_accepting_state +#define arglist1 arg1list +#endif + +/* Include the standard library header files. */ +#ifndef NO_UNISTD +#include +#endif +#ifndef NO_STDLIB +#ifdef __STDC__ +#include +#endif +#endif + +/* Define the _PROTOTYPE macro if it is needed. */ + +#ifndef _PROTOTYPE +#ifdef __STDC__ +#define _PROTOTYPE(func, args) func args +#else +#define _PROTOTYPE(func, args) func() +#endif +#endif + +/* From execute.c */ +_PROTOTYPE(void stop_execution, (int)); +_PROTOTYPE(unsigned char byte, (program_counter *pc)); +_PROTOTYPE(void execute, (void)); +_PROTOTYPE(char prog_char, (void)); +_PROTOTYPE(char input_char, (void)); +_PROTOTYPE(void push_constant, (char (*in_char)(void), int conv_base)); +_PROTOTYPE(void push_b10_const, (program_counter *pc)); +_PROTOTYPE(void assign, (int c_code)); + +/* From util.c */ +_PROTOTYPE(char *strcopyof, (char *str)); +_PROTOTYPE(arg_list *nextarg, (arg_list *args, int val)); +_PROTOTYPE(char *arg_str, (arg_list *args, int)); +_PROTOTYPE(void free_args, (arg_list *args)); +_PROTOTYPE(void check_params, (arg_list *params, arg_list *autos)); +_PROTOTYPE(void init_gen, (void)); +_PROTOTYPE(void generate, (char *str)); +_PROTOTYPE(void run_code, (void)); +_PROTOTYPE(void out_char, (int ch)); +_PROTOTYPE(id_rec *find_id, (id_rec *tree, char *id)); +_PROTOTYPE(int insert_id_rec, (id_rec **root, id_rec *new_id)); +_PROTOTYPE(void init_tree, (void)); +_PROTOTYPE(int lookup, (char *name, int namekind)); +_PROTOTYPE(char *bc_malloc, (int)); +_PROTOTYPE(void out_of_memory, (void)); +_PROTOTYPE(void welcome, (void)); +_PROTOTYPE(void warranty, (char *)); +_PROTOTYPE(void limits, (void)); +_PROTOTYPE(void yyerror, (char *str ,...)); +_PROTOTYPE(void warn, (char *mesg ,...)); +_PROTOTYPE(void rt_error, (char *mesg ,...)); +_PROTOTYPE(void rt_warn, (char *mesg ,...)); + +/* From load.c */ +_PROTOTYPE(void init_load, (void)); +_PROTOTYPE(void addbyte, (int byte)); +_PROTOTYPE(void def_label, (long lab)); +_PROTOTYPE(long long_val, (char **str)); +_PROTOTYPE(void load_code, (char *code)); + +/* From main.c */ +_PROTOTYPE(int main, (int argc , char *argv [])); +_PROTOTYPE(int open_new_file, (void)); +_PROTOTYPE(void new_yy_file, (FILE *file)); +_PROTOTYPE(void use_quit, (int)); + +/* From number.c */ +_PROTOTYPE(void free_num, (bc_num *num)); +_PROTOTYPE(bc_num new_num, (int length, int scale)); +_PROTOTYPE(void init_numbers, (void)); +_PROTOTYPE(bc_num copy_num, (bc_num num)); +_PROTOTYPE(void init_num, (bc_num *num)); +_PROTOTYPE(void str2num, (bc_num *num, char *str, int scale)); +_PROTOTYPE(char *num2str, (bc_num num)); +_PROTOTYPE(void int2num, (bc_num *num, int val)); +_PROTOTYPE(long num2long, (bc_num num)); +_PROTOTYPE(int bc_compare, (bc_num n1, bc_num n2)); +_PROTOTYPE(char is_zero, (bc_num num)); +_PROTOTYPE(char is_neg, (bc_num num)); +_PROTOTYPE(void bc_add, (bc_num n1, bc_num n2, bc_num *result)); +_PROTOTYPE(void bc_sub, (bc_num n1, bc_num n2, bc_num *result)); +_PROTOTYPE(void bc_multiply, (bc_num n1, bc_num n2, bc_num *prod, int scale)); +_PROTOTYPE(int bc_divide, (bc_num n1, bc_num n2, bc_num *quot, int scale)); +_PROTOTYPE(int bc_modulo, (bc_num num1, bc_num num2, bc_num *result, int scale)); +_PROTOTYPE(void bc_raise, (bc_num num1, bc_num num2, bc_num *result, int scale)); +_PROTOTYPE(int bc_sqrt, (bc_num *num, int scale)); +_PROTOTYPE(void out_long, (long val, int size, int space, + void (*out_char)(int))); +_PROTOTYPE(void out_num, (bc_num num, int o_base, void (* out_char)(int))); + + +/* From storage.c */ +_PROTOTYPE(void init_storage, (void)); +_PROTOTYPE(void more_functions, (void)); +_PROTOTYPE(void more_variables, (void)); +_PROTOTYPE(void more_arrays, (void)); +_PROTOTYPE(void clear_func, (int func )); +_PROTOTYPE(int fpop, (void)); +_PROTOTYPE(void fpush, (int val )); +_PROTOTYPE(void pop, (void)); +_PROTOTYPE(void push_copy, (bc_num num )); +_PROTOTYPE(void push_num, (bc_num num )); +_PROTOTYPE(char check_stack, (int depth )); +_PROTOTYPE(bc_var *get_var, (int var_name )); +_PROTOTYPE(bc_num *get_array_num, (int var_index, long index )); +_PROTOTYPE(void store_var, (int var_name )); +_PROTOTYPE(void store_array, (int var_name )); +_PROTOTYPE(void load_var, (int var_name )); +_PROTOTYPE(void load_array, (int var_name )); +_PROTOTYPE(void decr_var, (int var_name )); +_PROTOTYPE(void decr_array, (int var_name )); +_PROTOTYPE(void incr_var, (int var_name )); +_PROTOTYPE(void incr_array, (int var_name )); +_PROTOTYPE(void auto_var, (int name )); +_PROTOTYPE(void free_a_tree, (bc_array_node *root, int depth )); +_PROTOTYPE(void pop_vars, (arg_list *list )); +_PROTOTYPE(void process_params, (program_counter *pc, int func )); + +/* For the scanner and parser.... */ +_PROTOTYPE(int yyparse, (void)); +_PROTOTYPE(int yylex, (void)); + +/* Other things... */ +_PROTOTYPE (int getopt, (int, char *[], CONST char *)); + diff --git a/usr/othersrc/public/bc-1.01/sbc.y b/usr/othersrc/public/bc-1.01/sbc.y new file mode 100644 index 0000000000..e4054cee6d --- /dev/null +++ b/usr/othersrc/public/bc-1.01/sbc.y @@ -0,0 +1,448 @@ +, %{ +/* sbc.y: A POSIX bc processor written for minix with no extensions. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" /* To get the global variables. */ +#include "proto.h" +%} + +%start program + +%union { + char *s_value; + char c_value; + int i_value; + arg_list *a_value; + } + +%token NEWLINE AND OR NOT +%token STRING NAME NUMBER +/* '-', '+' are tokens themselves */ +%token MUL_OP +/* '*', '/', '%' */ +%token ASSIGN_OP +/* '=', '+=', '-=', '*=', '/=', '%=', '^=' */ +%token REL_OP +/* '==', '<=', '>=', '!=', '<', '>' */ +%token INCR_DECR +/* '++', '--' */ +%token Define Break Quit Length +/* 'define', 'break', 'quit', 'length' */ +%token Return For If While Sqrt Else +/* 'return', 'for', 'if', 'while', 'sqrt', 'else' */ +%token Scale Ibase Obase Auto Read +/* 'scale', 'ibase', 'obase', 'auto', 'read' */ +%token Warranty, Halt, Last, Continue, Print, Limits +/* 'warranty', 'halt', 'last', 'continue', 'print', 'limits' */ + +/* The types of all other non-terminals. */ +%type expression named_expression return_expression +%type opt_parameter_list parameter_list opt_auto_define_list +%type define_list opt_argument_list argument_list +%type program input_item semicolon_list statement_list +%type statement_or_error statement function relational_expression + +/* precedence */ +%nonassoc REL_OP +%right ASSIGN_OP +%left '+' '-' +%left MUL_OP +%right '^' +%nonassoc UNARY_MINUS +%nonassoc INCR_DECR + +%% +program : /* empty */ + { + $$ = 0; + std_only = TRUE; + if (interactive) + { + printf ("s%s\n", BC_VERSION); + welcome(); + } + } + | program input_item + ; +input_item : semicolon_list NEWLINE + { run_code(); } + | function + { run_code(); } + | error NEWLINE + { + yyerrok; + init_gen() ; + } + ; +semicolon_list : /* empty */ + { $$ = 0; } + | statement_or_error + | semicolon_list ';' statement_or_error + | semicolon_list ';' + ; +statement_list : /* empty */ + { $$ = 0; } + | statement + | statement_list NEWLINE + | statement_list NEWLINE statement + | statement_list ';' + | statement_list ';' statement + ; +statement_or_error : statement + | error statement + { $$ = $2; } + ; +statement : Warranty + { warranty("s"); } + | expression + { + if ($1 & 1) + generate ("W"); + else + generate ("p"); + } + | STRING + { + $$ = 0; + generate ("w"); + generate ($1); + free ($1); + } + | Break + { + if (break_label == 0) + yyerror ("Break outside a for/while"); + else + { + sprintf (genstr, "J%1d:", break_label); + generate (genstr); + } + } + | Quit + { exit(0); } + | Return + { generate ("0R"); } + | Return '(' return_expression ')' + { generate ("R"); } + | For + { + $1 = break_label; + break_label = next_label++; + } + '(' expression ';' + { + $4 = next_label++; + sprintf (genstr, "pN%1d:", $4); + generate (genstr); + } + relational_expression ';' + { + $7 = next_label++; + sprintf (genstr, "B%1d:J%1d:", $7, break_label); + generate (genstr); + $$ = next_label++; + sprintf (genstr, "N%1d:", $$); + generate (genstr); + } + expression ')' + { + sprintf (genstr, "pJ%1d:N%1d:", $4, $7); + generate (genstr); + } + statement + { + sprintf (genstr, "J%1d:N%1d:", $9, + break_label); + generate (genstr); + break_label = $1; + } + | If '(' relational_expression ')' + { + $3 = next_label++; + sprintf (genstr, "Z%1d:", $3); + generate (genstr); + } + statement + { + sprintf (genstr, "N%1d:", $3); + generate (genstr); + } + | While + { + $1 = next_label++; + sprintf (genstr, "N%1d:", $1); + generate (genstr); + } + '(' relational_expression + { + $4 = break_label; + break_label = next_label++; + sprintf (genstr, "Z%1d:", break_label); + generate (genstr); + } + ')' statement + { + sprintf (genstr, "J%1d:N%1d:", $1, break_label); + generate (genstr); + break_label = $4; + } + | '{' statement_list '}' + { $$ = 0; } + ; +function : Define NAME '(' opt_parameter_list ')' '{' + NEWLINE opt_auto_define_list + { + check_params ($4,$8); + sprintf (genstr, "F%d,%s.%s[", lookup($2,FUNCT), + arg_str ($4,TRUE), arg_str ($8,TRUE)); + generate (genstr); + free_args ($4); + free_args ($8); + $1 = next_label; + next_label = 0; + } + statement_list NEWLINE '}' + { + generate ("0R]"); + next_label = $1; + } + ; +opt_parameter_list : /* empty */ + { $$ = NULL; } + | parameter_list + ; +parameter_list : NAME + { $$ = nextarg (NULL, lookup($1,SIMPLE)); } + | define_list ',' NAME + { $$ = nextarg ($1, lookup($3,SIMPLE)); } + ; +opt_auto_define_list : /* empty */ + { $$ = NULL; } + | Auto define_list NEWLINE + { $$ = $2; } + | Auto define_list ';' + { $$ = $2; } + ; +define_list : NAME + { $$ = nextarg (NULL, lookup($1,SIMPLE)); } + | NAME '[' ']' + { $$ = nextarg (NULL, lookup($1,ARRAY)); } + | define_list ',' NAME + { $$ = nextarg ($1, lookup($3,SIMPLE)); } + | define_list ',' NAME '[' ']' + { $$ = nextarg ($1, lookup($3,ARRAY)); } + ; +opt_argument_list : /* empty */ + { $$ = NULL; } + | argument_list + ; +argument_list : expression + { $$ = nextarg (NULL,0); } + | argument_list ',' expression + { $$ = nextarg ($1,0); } + ; +relational_expression : expression + { $$ = 0; } + | expression REL_OP expression + { + $$ = 0; + switch (*($2)) + { + case '=': + generate ("="); + break; + case '!': + generate ("#"); + break; + case '<': + if ($2[1] == '=') + generate ("{"); + else + generate ("<"); + break; + case '>': + if ($2[1] == '=') + generate ("}"); + else + generate (">"); + break; + } + } + ; +return_expression : /* empty */ + { + $$ = 0; + generate ("0"); + } + | expression + ; +expression : named_expression ASSIGN_OP + { + if ($2 != '=') + { + if ($1 < 0) + sprintf (genstr, "DL%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + } + expression + { + $$ = 0; + if ($2 != '=') + { + sprintf (genstr, "%c", $2); + generate (genstr); + } + if ($1 < 0) + sprintf (genstr, "S%d:", -$1); + else + sprintf (genstr, "s%d:", $1); + generate (genstr); + } + | expression '+' expression + { generate ("+"); } + | expression '-' expression + { generate ("-"); } + | expression MUL_OP expression + { + genstr[0] = $2; + genstr[1] = 0; + generate (genstr); + } + | expression '^' expression + { generate ("^"); } + | '-' expression %prec UNARY_MINUS + { generate ("n"); $$ = 1;} + | named_expression + { + $$ = 1; + if ($1 < 0) + sprintf (genstr, "L%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + | NUMBER + { + int len = strlen($1); + $$ = 1; + if (len == 1 && *$1 == '0') + generate ("0"); + else + { + if (len == 1 && *$1 == '1') + generate ("1"); + else + { + generate ("K"); + generate ($1); + generate (":"); + } + free ($1); + } + } + | '(' expression ')' + { $$ = 1; } + | NAME '(' opt_argument_list ')' + { + $$ = 1; + if ($3 != NULL) + { + sprintf (genstr, "C%d,%s:", lookup($1,FUNCT), + arg_str ($3,FALSE)); + free_args ($3); + } + else + sprintf (genstr, "C%d:", lookup($1,FUNCT)); + generate (genstr); + } + | INCR_DECR named_expression + { + $$ = 1; + if ($2 < 0) + { + if ($1 == '+') + sprintf (genstr, "DA%d:L%d:", -$2, -$2); + else + sprintf (genstr, "DM%d:L%d:", -$2, -$2); + } + else + { + if ($1 == '+') + sprintf (genstr, "i%d:l%d:", $2, $2); + else + sprintf (genstr, "d%d:l%d:", $2, $2); + } + generate (genstr); + } + | named_expression INCR_DECR + { + $$ = 1; + if ($1 < 0) + { + sprintf (genstr, "DL%d:x", -$1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "A%d:", -$1); + else + sprintf (genstr, "M%d:", -$1); + } + else + { + sprintf (genstr, "l%d:", $1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "i%d:", $1); + else + sprintf (genstr, "d%d:", $1); + } + generate (genstr); + } + | Length '(' expression ')' + { generate ("cL"); $$ = 1;} + | Sqrt '(' expression ')' + { generate ("cR"); $$ = 1;} + | Scale '(' expression ')' + { generate ("cS"); $$ = 1;} + ; +named_expression : NAME + { $$ = lookup($1,SIMPLE); } + | NAME '[' expression ']' + { $$ = lookup($1,ARRAY); } + | Ibase + { $$ = 0; } + | Obase + { $$ = 1; } + | Scale + { $$ = 2; } + ; + +%% diff --git a/usr/othersrc/public/bc-1.01/scan.c b/usr/othersrc/public/bc-1.01/scan.c new file mode 100644 index 0000000000..e9ffb52212 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/scan.c @@ -0,0 +1,1313 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton version: + * + * $Header: flex/RCS/flex.skel,v 2.13 90/05/26 17:24:13 vern Exp $ + * + * @(#)lex.skel 5.5 (Berkeley) 5/6/91 + */ + +#define FLEX_SCANNER + +#include +#include + +#ifdef __STDC__ + +#define YY_USE_PROTOS +#endif + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include + +/* use prototypes in function declarations */ +#define YY_USE_PROTOS + +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#define yyterminate() return ( YY_NULL ) + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters + */ +#define YY_DECL int yylex YY_PROTO(( void )) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +#define YY_CHAR char +# line 1 "scan.l" +#define INITIAL 0 +# line 2 "scan.l" +/* scan.l: the (f)lex description file for the scanner. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "y.tab.h" +#include "global.h" +#include "proto.h" + +/* Using flex, we can ask for a smaller input buffer. With lex, this + does nothing! */ + +#ifdef SMALL_BUF +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 512 +#endif + +/* We want to define our own yywrap. */ +#undef yywrap +_PROTOTYPE(int yywrap, (void)); + +/* MINIX returns from read with < 0 if SIGINT is encountered. + In flex, we can redefine YY_INPUT to the following. In lex, this + does nothing! */ +#include +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + if (errno != EINTR) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); + +# line 60 "scan.l" + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ + yyleng = yy_cp - yy_bp; \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters*/ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; + +YY_CHAR *yytext; +int yyleng; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +#define YY_END_OF_BUFFER 40 +typedef int yy_state_type; +static const short int yy_accept[144] = + { 0, + 0, 0, 40, 38, 33, 31, 25, 38, 26, 38, + 22, 26, 22, 22, 38, 26, 37, 29, 27, 29, + 38, 22, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 38, 33, + 29, 0, 36, 27, 23, 30, 37, 0, 34, 37, + 37, 0, 28, 32, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 7, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 24, 37, 0, 0, 37, + 0, 35, 35, 35, 35, 35, 6, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + + 35, 13, 35, 35, 35, 14, 16, 35, 17, 35, + 35, 35, 35, 3, 15, 35, 35, 9, 35, 35, + 2, 35, 35, 11, 35, 35, 12, 20, 35, 10, + 35, 8, 35, 1, 4, 21, 5, 35, 35, 35, + 19, 18, 0 + } ; + +static const YY_CHAR yy_ec[128] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 1, 6, 7, 1, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 1, 17, 18, + 19, 20, 1, 1, 21, 21, 21, 21, 21, 21, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 22, 23, 24, 25, 26, 1, 27, 28, 29, 30, + + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 36, 48, 36, + 49, 36, 50, 51, 52, 1, 1 + } ; + +static const YY_CHAR yy_meta[53] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 1, 1 + } ; + +static const short int yy_base[146] = + { 0, + 0, 0, 193, 194, 190, 194, 172, 185, 170, 181, + 194, 168, 42, 41, 41, 46, 52, 167, 61, 166, + 181, 164, 135, 137, 139, 148, 140, 136, 0, 149, + 27, 50, 147, 130, 126, 141, 40, 36, 120, 168, + 194, 164, 194, 194, 194, 194, 66, 165, 194, 72, + 76, 164, 194, 194, 0, 120, 134, 124, 131, 117, + 117, 122, 132, 0, 113, 117, 117, 128, 119, 118, + 52, 125, 107, 106, 114, 194, 80, 145, 84, 88, + 144, 105, 118, 98, 108, 111, 0, 95, 95, 93, + 105, 102, 91, 95, 88, 103, 85, 93, 84, 85, + + 90, 0, 90, 91, 85, 0, 0, 93, 0, 77, + 76, 90, 74, 0, 0, 75, 87, 0, 90, 85, + 0, 75, 83, 0, 76, 63, 0, 0, 66, 0, + 62, 0, 47, 0, 0, 0, 0, 45, 53, 29, + 0, 0, 194, 111, 56 + } ; + +static const short int yy_def[146] = + { 0, + 143, 1, 143, 143, 143, 143, 143, 144, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 143, 143, + 143, 144, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 143, 143, 143, 143, 143, + 143, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 0, 143, 143 + } ; + +static const short int yy_nxt[247] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, + 13, 11, 14, 15, 16, 17, 11, 18, 19, 20, + 17, 11, 21, 11, 22, 4, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 29, 29, 32, 29, 29, + 33, 34, 35, 36, 37, 29, 29, 38, 29, 11, + 39, 11, 46, 46, 63, 49, 47, 55, 64, 44, + 44, 47, 74, 48, 44, 50, 53, 51, 72, 75, + 53, 53, 51, 53, 52, 53, 65, 142, 96, 41, + 66, 77, 73, 141, 67, 53, 77, 80, 78, 50, + 140, 51, 80, 139, 81, 77, 51, 97, 52, 47, + + 77, 138, 78, 80, 47, 137, 48, 136, 80, 135, + 81, 42, 42, 134, 133, 132, 131, 130, 129, 128, + 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, + 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, + 107, 106, 105, 104, 103, 102, 80, 77, 101, 100, + 99, 98, 95, 94, 93, 92, 91, 90, 89, 88, + 87, 86, 85, 84, 83, 82, 51, 79, 43, 40, + 76, 71, 70, 69, 68, 62, 61, 60, 59, 58, + 57, 56, 44, 54, 41, 41, 44, 45, 44, 43, + 41, 40, 143, 3, 143, 143, 143, 143, 143, 143, + + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143 + } ; + +static const short int yy_chk[247] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 13, 14, 31, 16, 15, 145, 31, 14, + 13, 15, 38, 15, 16, 17, 19, 17, 37, 38, + 19, 19, 17, 19, 17, 19, 32, 140, 71, 19, + 32, 47, 37, 139, 32, 19, 47, 50, 47, 51, + 138, 51, 50, 133, 50, 77, 51, 71, 51, 79, + + 77, 131, 77, 80, 79, 129, 79, 126, 80, 125, + 80, 144, 144, 123, 122, 120, 119, 117, 116, 113, + 112, 111, 110, 108, 105, 104, 103, 101, 100, 99, + 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, + 88, 86, 85, 84, 83, 82, 81, 78, 75, 74, + 73, 72, 70, 69, 68, 67, 66, 65, 63, 62, + 61, 60, 59, 58, 57, 56, 52, 48, 42, 40, + 39, 36, 35, 34, 33, 30, 28, 27, 26, 25, + 24, 23, 22, 21, 20, 18, 12, 10, 9, 8, + 7, 5, 3, 143, 143, 143, 143, 143, 143, 143, + + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143 + } ; + +static yy_state_type yy_last_accepting_state; +static YY_CHAR *yy_last_accepting_cpos; + +/* the intent behind this definition is that it'll catch + * any uses of REJECT which flex missed + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yyunput YY_PROTO(( YY_CHAR c, YY_CHAR *buf_ptr )); +void yyrestart YY_PROTO(( FILE *input_file )); +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); + +#define yy_new_buffer yy_create_buffer + +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif + +YY_DECL + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp, *yy_bp; + register int yy_act; + + + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[*yy_cp]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 144 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 194 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + + + switch ( yy_act ) + { + case 0: /* must backtrack */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +# line 61 "scan.l" +return(Define); + YY_BREAK +case 2: +# line 62 "scan.l" +return(Break); + YY_BREAK +case 3: +# line 63 "scan.l" +return(Quit); + YY_BREAK +case 4: +# line 64 "scan.l" +return(Length); + YY_BREAK +case 5: +# line 65 "scan.l" +return(Return); + YY_BREAK +case 6: +# line 66 "scan.l" +return(For); + YY_BREAK +case 7: +# line 67 "scan.l" +return(If); + YY_BREAK +case 8: +# line 68 "scan.l" +return(While); + YY_BREAK +case 9: +# line 69 "scan.l" +return(Sqrt); + YY_BREAK +case 10: +# line 70 "scan.l" +return(Scale); + YY_BREAK +case 11: +# line 71 "scan.l" +return(Ibase); + YY_BREAK +case 12: +# line 72 "scan.l" +return(Obase); + YY_BREAK +case 13: +# line 73 "scan.l" +return(Auto); + YY_BREAK +case 14: +# line 74 "scan.l" +return(Else); + YY_BREAK +case 15: +# line 75 "scan.l" +return(Read); + YY_BREAK +case 16: +# line 76 "scan.l" +return(Halt); + YY_BREAK +case 17: +# line 77 "scan.l" +return(Last); + YY_BREAK +case 18: +# line 78 "scan.l" +return(Warranty); + YY_BREAK +case 19: +# line 79 "scan.l" +return(Continue); + YY_BREAK +case 20: +# line 80 "scan.l" +return(Print); + YY_BREAK +case 21: +# line 81 "scan.l" +return(Limits); + YY_BREAK +case 22: +# line 82 "scan.l" +{ yylval.c_value = yytext[0]; + return((int)yytext[0]); } + YY_BREAK +case 23: +# line 84 "scan.l" +{ return(AND); } + YY_BREAK +case 24: +# line 85 "scan.l" +{ return(OR); } + YY_BREAK +case 25: +# line 86 "scan.l" +{ return(NOT); } + YY_BREAK +case 26: +# line 87 "scan.l" +{ yylval.c_value = yytext[0]; return(MUL_OP); } + YY_BREAK +case 27: +# line 88 "scan.l" +{ yylval.c_value = yytext[0]; return(ASSIGN_OP); } + YY_BREAK +case 28: +# line 89 "scan.l" +{ +#ifdef OLD_EQ_OP + char warn_save; + warn_save = warn_not_std; + warn_not_std = TRUE; + warn ("Old fashioned ="); + warn_not_std = warn_save; + yylval.c_value = yytext[1]; +#else + yylval.c_value = '='; + yyless (1); +#endif + return(ASSIGN_OP); + } + YY_BREAK +case 29: +# line 103 "scan.l" +{ yylval.s_value = strcopyof(yytext); return(REL_OP); } + YY_BREAK +case 30: +# line 104 "scan.l" +{ yylval.c_value = yytext[0]; return(INCR_DECR); } + YY_BREAK +case 31: +# line 105 "scan.l" +{ line_no++; return(NEWLINE); } + YY_BREAK +case 32: +# line 106 "scan.l" +{ line_no++; /* ignore a "quoted" newline */ } + YY_BREAK +case 33: +# line 107 "scan.l" +{ /* ignore spaces and tabs */ } + YY_BREAK +case 34: +# line 108 "scan.l" +{ + int c; + + for (;;) + { + while ( ((c=input()) != '*') && (c != EOF)) + /* eat it */ + if (c == '\n') line_no++; + if (c == '*') + { + while ( (c=input()) == '*') /* eat it*/; + if (c == '/') break; /* at end of comment */ + if (c == '\n') line_no++; + } + if (c == EOF) + { + fprintf (stderr,"EOF encountered in a comment.\n"); + break; + } + } + } + YY_BREAK +case 35: +# line 129 "scan.l" +{ yylval.s_value = strcopyof(yytext); return(NAME); } + YY_BREAK +case 36: +# line 130 "scan.l" +{ + char *look; + yylval.s_value = strcopyof(yytext); + for (look = yytext; *look != 0; look++) + if (*look == '\n') line_no++; + return(STRING); + } + YY_BREAK +case 37: +# line 137 "scan.l" +{ + char *src, *dst; + int len; + /* remove a trailing decimal point. */ + len = strlen(yytext); + if (yytext[len-1] == '.') + yytext[len-1] = 0; + /* remove leading zeros. */ + src = yytext; + dst = yytext; + while (*src == '0') src++; + if (*src == 0) src--; + /* Copy strings removing the newlines. */ + while (*src != 0) + { + if (*src == '\\') + { + src++; src++; + line_no++; + } + else + *dst++ = *src++; + } + *dst = 0; + yylval.s_value = strcopyof(yytext); + return(NUMBER); + } + YY_BREAK +case 38: +# line 164 "scan.l" +yyerror ("illegal character: %s",yytext); + YY_BREAK +case 39: +# line 165 "scan.l" +ECHO; + YY_BREAK +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + + { + register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state() + + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[*yy_cp] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 144 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +register yy_state_type yy_current_state; +#endif + + { + register int yy_is_jam; + register YY_CHAR *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 144 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_base[yy_current_state] == 194); + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +#ifdef YY_USE_PROTOS +static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) +#else +static void yyunput( c, yy_bp ) +YY_CHAR c; +register YY_CHAR *yy_bp; +#endif + + { + register YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + register YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + register YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + + { + int c; + YY_CHAR *yy_cp = yy_c_buf_p; + + *yy_cp = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = yytext + YY_MORE_ADJ; + return ( EOF ); + } + + YY_NEW_FILE; + +#ifdef __cplusplus + return ( yyinput() ); +#else + return ( input() ); +#endif + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( "unexpected last match in input()" ); +#endif + } + } + } + + c = *yy_c_buf_p; + yy_hold_char = *++yy_c_buf_p; + + return ( c ); + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + + { + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } +# line 165 "scan.l" + + + + +/* This is the way to get multiple files input into lex. */ + +int +yywrap() +{ + if (!open_new_file ()) return (1); /* EOF on standard in. */ + return (0); /* We have more input. */ +} diff --git a/usr/othersrc/public/bc-1.01/scan.l b/usr/othersrc/public/bc-1.01/scan.l new file mode 100644 index 0000000000..1d3f9a0198 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/scan.l @@ -0,0 +1,176 @@ +%{ +/* scan.l: the (f)lex description file for the scanner. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "y.tab.h" +#include "global.h" +#include "proto.h" + +/* Using flex, we can ask for a smaller input buffer. With lex, this + does nothing! */ + +#ifdef SMALL_BUF +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 512 +#endif + +/* We want to define our own yywrap. */ +#undef yywrap +_PROTOTYPE(int yywrap, (void)); + +/* MINIX returns from read with < 0 if SIGINT is encountered. + In flex, we can redefine YY_INPUT to the following. In lex, this + does nothing! */ +#include +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + if (errno != EINTR) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); + +%} +DIGIT [0-9A-F] +LETTER [a-z] +%% +define return(Define); +break return(Break); +quit return(Quit); +length return(Length); +return return(Return); +for return(For); +if return(If); +while return(While); +sqrt return(Sqrt); +scale return(Scale); +ibase return(Ibase); +obase return(Obase); +auto return(Auto); +else return(Else); +read return(Read); +halt return(Halt); +last return(Last); +warranty return(Warranty); +continue return(Continue); +print return(Print); +limits return(Limits); +"+"|"-"|";"|"("|")"|"{"|"}"|"["|"]"|","|"^" { yylval.c_value = yytext[0]; + return((int)yytext[0]); } +&& { return(AND); } +\|\| { return(OR); } +"!" { return(NOT); } +"*"|"/"|"%" { yylval.c_value = yytext[0]; return(MUL_OP); } +"="|\+=|-=|\*=|\/=|%=|\^= { yylval.c_value = yytext[0]; return(ASSIGN_OP); } +=\+|=-|=\*|=\/|=%|=\^ { +#ifdef OLD_EQ_OP + char warn_save; + warn_save = warn_not_std; + warn_not_std = TRUE; + warn ("Old fashioned ="); + warn_not_std = warn_save; + yylval.c_value = yytext[1]; +#else + yylval.c_value = '='; + yyless (1); +#endif + return(ASSIGN_OP); + } +==|\<=|\>=|\!=|"<"|">" { yylval.s_value = strcopyof(yytext); return(REL_OP); } +\+\+|-- { yylval.c_value = yytext[0]; return(INCR_DECR); } +"\n" { line_no++; return(NEWLINE); } +\\\n { line_no++; /* ignore a "quoted" newline */ } +[ \t]+ { /* ignore spaces and tabs */ } +"/*" { + int c; + + for (;;) + { + while ( ((c=input()) != '*') && (c != EOF)) + /* eat it */ + if (c == '\n') line_no++; + if (c == '*') + { + while ( (c=input()) == '*') /* eat it*/; + if (c == '/') break; /* at end of comment */ + if (c == '\n') line_no++; + } + if (c == EOF) + { + fprintf (stderr,"EOF encountered in a comment.\n"); + break; + } + } + } +[a-z][a-z0-9_]* { yylval.s_value = strcopyof(yytext); return(NAME); } +\"[^\"]*\" { + char *look; + yylval.s_value = strcopyof(yytext); + for (look = yytext; *look != 0; look++) + if (*look == '\n') line_no++; + return(STRING); + } +{DIGIT}({DIGIT}|\\\n)*("."({DIGIT}|\\\n)*)?|"."(\\\n)*{DIGIT}({DIGIT}|\\\n)* { + char *src, *dst; + int len; + /* remove a trailing decimal point. */ + len = strlen(yytext); + if (yytext[len-1] == '.') + yytext[len-1] = 0; + /* remove leading zeros. */ + src = yytext; + dst = yytext; + while (*src == '0') src++; + if (*src == 0) src--; + /* Copy strings removing the newlines. */ + while (*src != 0) + { + if (*src == '\\') + { + src++; src++; + line_no++; + } + else + *dst++ = *src++; + } + *dst = 0; + yylval.s_value = strcopyof(yytext); + return(NUMBER); + } +. yyerror ("illegal character: %s",yytext); +%% + + + +/* This is the way to get multiple files input into lex. */ + +int +yywrap() +{ + if (!open_new_file ()) return (1); /* EOF on standard in. */ + return (0); /* We have more input. */ +} diff --git a/usr/othersrc/public/bc-1.01/storage.c b/usr/othersrc/public/bc-1.01/storage.c new file mode 100644 index 0000000000..1e815236d2 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/storage.c @@ -0,0 +1,967 @@ +/* storage.c: Code and data storage manipulations. This includes labels. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" +#include "proto.h" + + +/* Initialize the storage at the beginning of the run. */ + +void +init_storage () +{ + + /* Functions: we start with none and ask for more. */ + f_count = 0; + more_functions (); + f_names[0] = "(main)"; + + /* Variables. */ + v_count = 0; + more_variables (); + + /* Arrays. */ + a_count = 0; + more_arrays (); + + /* Other things... */ + ex_stack = NULL; + fn_stack = NULL; + i_base = 10; + o_base = 10; + scale = 0; + c_code = FALSE; + init_numbers(); +} + +/* Three functions for increasing the number of functions, variables, or + arrays that are needed. This adds another 32 of the requested object. */ + +void +more_functions (VOID) +{ + int old_count; + int indx1, indx2; + bc_function *old_f; + bc_function *f; + char **old_names; + + /* Save old information. */ + old_count = f_count; + old_f = functions; + old_names = f_names; + + /* Add a fixed amount and allocate new space. */ + f_count += STORE_INCR; + functions = (bc_function *) bc_malloc (f_count*sizeof (bc_function)); + f_names = (char **) bc_malloc (f_count*sizeof (char *)); + + /* Copy old ones. */ + for (indx1 = 0; indx1 < old_count; indx1++) + { + functions[indx1] = old_f[indx1]; + f_names[indx1] = old_names[indx1]; + } + + /* Initialize the new ones. */ + for (; indx1 < f_count; indx1++) + { + f = &functions[indx1]; + f->f_defined = FALSE; + for (indx2 = 0; indx2 < BC_MAX_SEGS; indx2++) + f->f_body [indx2] = NULL; + f->f_code_size = 0; + f->f_label = NULL; + f->f_autos = NULL; + f->f_params = NULL; + } + + /* Free the old elements. */ + if (old_count != 0) + { + free (old_f); + free (old_names); + } +} + +void +more_variables () +{ + int indx; + int old_count; + bc_var **old_var; + char **old_names; + + /* Save the old values. */ + old_count = v_count; + old_var = variables; + old_names = v_names; + + /* Increment by a fixed amount and allocate. */ + v_count += STORE_INCR; + variables = (bc_var **) bc_malloc (v_count*sizeof(bc_var *)); + v_names = (char **) bc_malloc (v_count*sizeof(char *)); + + /* Copy the old variables. */ + for (indx = 3; indx < old_count; indx++) + variables[indx] = old_var[indx]; + + /* Initialize the new elements. */ + for (; indx < v_count; indx++) + variables[indx] = NULL; + + /* Free the old elements. */ + if (old_count != 0) + { + free (old_var); + free (old_names); + } +} + +void +more_arrays () +{ + int indx; + int old_count; + bc_var_array **old_ary; + char **old_names; + + /* Save the old values. */ + old_count = a_count; + old_ary = arrays; + old_names = a_names; + + /* Increment by a fixed amount and allocate. */ + a_count += STORE_INCR; + arrays = (bc_var_array **) bc_malloc (a_count*sizeof(bc_var_array *)); + a_names = (char **) bc_malloc (a_count*sizeof(char *)); + + /* Copy the old arrays. */ + for (indx = 1; indx < old_count; indx++) + arrays[indx] = old_ary[indx]; + + + /* Initialize the new elements. */ + for (; indx < v_count; indx++) + arrays[indx] = NULL; + + /* Free the old elements. */ + if (old_count != 0) + { + free (old_ary); + free (old_names); + } +} + + +/* clear_func clears out function FUNC and makes it ready to redefine. */ + +void +clear_func (func) + char func; +{ + bc_function *f; + int indx; + bc_label_group *lg; + + /* Set the pointer to the function. */ + f = &functions[func]; + f->f_defined = FALSE; + + /* Clear the code segments. */ + for (indx = 0; indx < BC_MAX_SEGS; indx++) + { + if (f->f_body[indx] != NULL) + { + free (f->f_body[indx]); + f->f_body[indx] = NULL; + } + } + + f->f_code_size = 0; + if (f->f_autos != NULL) + { + free_args (f->f_autos); + f->f_autos = NULL; + } + if (f->f_params != NULL) + { + free_args (f->f_params); + f->f_params = NULL; + } + while (f->f_label != NULL) + { + lg = f->f_label->l_next; + free (f->f_label); + f->f_label = lg; + } +} + + +/* Pop the function execution stack and return the top. */ + +int +fpop() +{ + fstack_rec *temp; + int retval; + + if (fn_stack != NULL) + { + temp = fn_stack; + fn_stack = temp->s_next; + retval = temp->s_val; + free (temp); + } + return (retval); +} + + +/* Push VAL on to the function stack. */ + +void +fpush (val) + int val; +{ + fstack_rec *temp; + + temp = (fstack_rec *) bc_malloc (sizeof (fstack_rec)); + temp->s_next = fn_stack; + temp->s_val = val; + fn_stack = temp; +} + + +/* Pop and discard the top element of the regular execution stack. */ + +void +pop () +{ + estack_rec *temp; + + if (ex_stack != NULL) + { + temp = ex_stack; + ex_stack = temp->s_next; + free_num (&temp->s_num); + free (temp); + } +} + + +/* Push a copy of NUM on to the regular execution stack. */ + +void +push_copy (num) + bc_num num; +{ + estack_rec *temp; + + temp = (estack_rec *) bc_malloc (sizeof (estack_rec)); + temp->s_num = copy_num (num); + temp->s_next = ex_stack; + ex_stack = temp; +} + + +/* Push NUM on to the regular execution stack. Do NOT push a copy. */ + +void +push_num (num) + bc_num num; +{ + estack_rec *temp; + + temp = (estack_rec *) bc_malloc (sizeof (estack_rec)); + temp->s_num = num; + temp->s_next = ex_stack; + ex_stack = temp; +} + + +/* Make sure the ex_stack has at least DEPTH elements on it. + Return TRUE if it has at least DEPTH elements, otherwise + return FALSE. */ + +char +check_stack (depth) + int depth; +{ + estack_rec *temp; + + temp = ex_stack; + while ((temp != NULL) && (depth > 0)) + { + temp = temp->s_next; + depth--; + } + if (depth > 0) + { + rt_error ("Stack error."); + return FALSE; + } + return TRUE; +} + + +/* The following routines manipulate simple variables and + array variables. */ + +/* get_var returns a pointer to the variable VAR_NAME. If one does not + exist, one is created. */ + +bc_var * +get_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + var_ptr = variables[var_name]; + if (var_ptr == NULL) + { + var_ptr = variables[var_name] = (bc_var *) bc_malloc (sizeof (bc_var)); + init_num (&var_ptr->v_value); + } + return var_ptr; +} + + +/* get_array_num returns the address of the bc_num in the array + structure. If more structure is requried to get to the index, + this routine does the work to create that structure. VAR_INDEX + is a zero based index into the arrays storage array. INDEX is + the index into the bc array. */ + +bc_num * +get_array_num (var_index, index) + int var_index; + long index; +{ + bc_var_array *ary_ptr; + bc_array *a_var; + bc_array_node *temp; + int log, ix, ix1; + int sub [NODE_DEPTH]; + + /* Get the array entry. */ + ary_ptr = arrays[var_index]; + if (ary_ptr == NULL) + { + ary_ptr = arrays[var_index] = + (bc_var_array *) bc_malloc (sizeof (bc_var_array)); + ary_ptr->a_value = NULL; + ary_ptr->a_next = NULL; + ary_ptr->a_param = FALSE; + } + + a_var = ary_ptr->a_value; + if (a_var == NULL) { + a_var = ary_ptr->a_value = (bc_array *) bc_malloc (sizeof (bc_array)); + a_var->a_tree = NULL; + a_var->a_depth = 0; + } + + /* Get the index variable. */ + sub[0] = index & NODE_MASK; + ix = index >> NODE_SHIFT; + log = 1; + while (ix > 0 || log < a_var->a_depth) + { + sub[log] = ix & NODE_MASK; + ix >>= NODE_SHIFT; + log++; + } + + /* Build any tree that is necessary. */ + while (log > a_var->a_depth) + { + temp = (bc_array_node *) bc_malloc (sizeof(bc_array_node)); + if (a_var->a_depth != 0) + { + temp->n_items.n_down[0] = a_var->a_tree; + for (ix=1; ix < NODE_SIZE; ix++) + temp->n_items.n_down[ix] = NULL; + } + else + { + for (ix=0; ix < NODE_SIZE; ix++) + temp->n_items.n_num[ix] = copy_num(_zero_); + } + a_var->a_tree = temp; + a_var->a_depth++; + } + + /* Find the indexed variable. */ + temp = a_var->a_tree; + while ( log-- > 1) + { + ix1 = sub[log]; + if (temp->n_items.n_down[ix1] == NULL) + { + temp->n_items.n_down[ix1] = + (bc_array_node *) bc_malloc (sizeof(bc_array_node)); + temp = temp->n_items.n_down[ix1]; + if (log > 1) + for (ix=0; ix < NODE_SIZE; ix++) + temp->n_items.n_down[ix] = NULL; + else + for (ix=0; ix < NODE_SIZE; ix++) + temp->n_items.n_num[ix] = copy_num(_zero_); + } + else + temp = temp->n_items.n_down[ix1]; + } + + /* Return the address of the indexed variable. */ + return &(temp->n_items.n_num[sub[0]]); +} + + +/* Store the top of the execution stack into VAR_NAME. + This includes the special variables ibase, obase, and scale. */ + +void +store_var (var_name) + int var_name; +{ + bc_var *var_ptr; + long temp; + char toobig; + + if (var_name > 2) + { + /* It is a simple variable. */ + var_ptr = get_var (var_name); + if (var_ptr != NULL) + { + free_num(&var_ptr->v_value); + var_ptr->v_value = copy_num (ex_stack->s_num); + } + } + else + { + /* It is a special variable... */ + toobig = FALSE; + if (is_neg (ex_stack->s_num)) + { + switch (var_name) + { + case 0: + rt_warn ("negative ibase, set to 2"); + temp = 2; + break; + case 1: + rt_warn ("negative obase, set to 2"); + temp = 2; + break; + case 2: + rt_warn ("negative scale, set to 0"); + temp = 0; + break; + } + } + else + { + temp = num2long (ex_stack->s_num); + if (!is_zero (ex_stack->s_num) && temp == 0) + toobig = TRUE; + } + switch (var_name) + { + case 0: + if (temp < 2 && !toobig) + { + i_base = 2; + rt_warn ("ibase too small, set to 2"); + } + else + if (temp > 16 || toobig) + { + i_base = 16; + rt_warn ("ibase too large, set to 16"); + } + else + i_base = (int) temp; + break; + + case 1: + if (temp < 2 && !toobig) + { + o_base = 2; + rt_warn ("obase too small, set to 2"); + } + else + if (temp > BC_BASE_MAX || toobig) + { + o_base = BC_BASE_MAX; + rt_warn ("obase too large, set to %d", BC_BASE_MAX); + } + else + o_base = (int) temp; + break; + + case 2: + /* WARNING: The following if statement may generate a compiler + warning if INT_MAX == LONG_MAX. This is NOT a problem. */ + if (temp > BC_SCALE_MAX || toobig ) + { + scale = BC_SCALE_MAX; + rt_warn ("scale too large, set to %d", BC_SCALE_MAX); + } + else + scale = (int) temp; + } + } +} + + +/* Store the top of the execution stack into array VAR_NAME. + VAR_NAME is the name of an array, and the next to the top + of stack for the index into the array. */ + +void +store_array (var_name) + int var_name; +{ + bc_num *num_ptr; + long index; + + if (!check_stack(2)) return; + index = num2long (ex_stack->s_next->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero(ex_stack->s_next->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + free_num (num_ptr); + *num_ptr = copy_num (ex_stack->s_num); + free_num (&ex_stack->s_next->s_num); + ex_stack->s_next->s_num = ex_stack->s_num; + init_num (&ex_stack->s_num); + pop(); + } + } +} + + +/* Load a copy of VAR_NAME on to the execution stack. This includes + the special variables ibase, obase and scale. */ + +void +load_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + switch (var_name) + { + + case 0: + /* Special variable ibase. */ + push_copy (_zero_); + int2num (&ex_stack->s_num, i_base); + break; + + case 1: + /* Special variable obase. */ + push_copy (_zero_); + int2num (&ex_stack->s_num, o_base); + break; + + case 2: + /* Special variable scale. */ + push_copy (_zero_); + int2num (&ex_stack->s_num, scale); + break; + + default: + /* It is a simple variable. */ + var_ptr = variables[var_name]; + if (var_ptr != NULL) + push_copy (var_ptr->v_value); + else + push_copy (_zero_); + } +} + + +/* Load a copy of VAR_NAME on to the execution stack. This includes + the special variables ibase, obase and scale. */ + +void +load_array (var_name) + int var_name; +{ + bc_num *num_ptr; + long index; + + if (!check_stack(1)) return; + index = num2long (ex_stack->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero(ex_stack->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + pop(); + push_copy (*num_ptr); + } + } +} + + +/* Decrement VAR_NAME by one. This includes the special variables + ibase, obase, and scale. */ + +void +decr_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + switch (var_name) + { + + case 0: /* ibase */ + if (i_base > 2) + i_base--; + else + rt_warn ("ibase too small in --"); + break; + + case 1: /* obase */ + if (o_base > 2) + o_base--; + else + rt_warn ("obase too small in --"); + break; + + case 2: /* scale */ + if (scale > 0) + scale--; + else + rt_warn ("scale can not be negative in -- "); + break; + + default: /* It is a simple variable. */ + var_ptr = get_var (var_name); + if (var_ptr != NULL) + bc_sub (var_ptr->v_value,_one_,&var_ptr->v_value); + } +} + + +/* Decrement VAR_NAME by one. VAR_NAME is an array, and the top of + the execution stack is the index and it is popped off the stack. */ + +void +decr_array (var_name) + char var_name; +{ + bc_num *num_ptr; + long index; + + /* It is an array variable. */ + if (!check_stack (1)) return; + index = num2long (ex_stack->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero (ex_stack->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + pop (); + bc_sub (*num_ptr, _one_, num_ptr); + } + } +} + + +/* Increment VAR_NAME by one. This includes the special variables + ibase, obase, and scale. */ + +void +incr_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + switch (var_name) + { + + case 0: /* ibase */ + if (i_base < 16) + i_base++; + else + rt_warn ("ibase too big in ++"); + break; + + case 1: /* obase */ + if (o_base < BC_BASE_MAX) + o_base++; + else + rt_warn ("obase too big in ++"); + break; + + case 2: + if (scale < BC_SCALE_MAX) + scale++; + else + rt_warn ("Scale too big in ++"); + break; + + default: /* It is a simple variable. */ + var_ptr = get_var (var_name); + if (var_ptr != NULL) + bc_add (var_ptr->v_value, _one_, &var_ptr->v_value); + + } +} + + +/* Increment VAR_NAME by one. VAR_NAME is an array and top of + execution stack is the index and is popped off the stack. */ + +void +incr_array (var_name) + int var_name; +{ + bc_num *num_ptr; + long index; + + if (!check_stack (1)) return; + index = num2long (ex_stack->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero (ex_stack->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + pop (); + bc_add (*num_ptr, _one_, num_ptr); + } + } +} + + +/* Routines for processing autos variables and parameters. */ + +/* NAME is an auto variable that needs to be pushed on its stack. */ + +void +auto_var (name) + int name; +{ + bc_var *v_temp; + bc_var_array *a_temp; + int ix; + + if (name > 0) + { + /* A simple variable. */ + ix = name; + v_temp = (bc_var *) bc_malloc (sizeof (bc_var)); + v_temp->v_next = variables[ix]; + init_num (&v_temp->v_value); + variables[ix] = v_temp; + } + else + { + /* An array variable. */ + ix = -name; + a_temp = (bc_var_array *) bc_malloc (sizeof (bc_var_array)); + a_temp->a_next = arrays[ix]; + a_temp->a_value = NULL; + a_temp->a_param = FALSE; + arrays[ix] = a_temp; + } +} + + +/* Free_a_tree frees everything associated with an array variable tree. + This is used when popping an array variable off its auto stack. */ + +void +free_a_tree ( root, depth ) + bc_array_node *root; + int depth; +{ + int ix; + + if (root != NULL) + { + if (depth > 1) + for (ix = 0; ix < NODE_SIZE; ix++) + free_a_tree (root->n_items.n_down[ix], depth-1); + else + for (ix = 0; ix < NODE_SIZE; ix++) + free_num ( &(root->n_items.n_num[ix])); + free (root); + } +} + + +/* LIST is an NULL terminated list of varible names that need to be + popped off their auto stacks. */ + +void +pop_vars (list) + arg_list *list; +{ + bc_var *v_temp; + bc_var_array *a_temp; + int ix; + + while (list != NULL) + { + ix = list->av_name; + if (ix > 0) + { + /* A simple variable. */ + v_temp = variables[ix]; + if (v_temp != NULL) + { + variables[ix] = v_temp->v_next; + free_num (&v_temp->v_value); + free (v_temp); + } + } + else + { + /* An array variable. */ + ix = -ix; + a_temp = arrays[ix]; + if (a_temp != NULL) + { + arrays[ix] = a_temp->a_next; + if (!a_temp->a_param && a_temp->a_value != NULL) + { + free_a_tree (a_temp->a_value->a_tree, + a_temp->a_value->a_depth); + free (a_temp->a_value); + } + free (a_temp); + } + } + list = list->next; + } +} + + +/* A call is being made to FUNC. The call types are at PC. Process + the parameters by doing an auto on the parameter variable and then + store the value at the new variable or put a pointer the the array + variable. */ + +void +process_params (pc, func) + program_counter *pc; + int func; +{ + char ch; + arg_list *params; + char warned = FALSE; + int ix, ix1; + bc_var *v_temp; + bc_var_array *a_src, *a_dest; + bc_num *n_temp; + + /* Get the parameter names from the function. */ + params = functions[func].f_params; + + while ((ch = byte(pc)) != ':') + { + if (params != NULL) + { + if ((ch == '0') && params->av_name > 0) + { + /* A simple variable. */ + ix = params->av_name; + v_temp = (bc_var *) bc_malloc (sizeof(bc_var)); + v_temp->v_next = variables[ix]; + v_temp->v_value = ex_stack->s_num; + init_num (&ex_stack->s_num); + variables[ix] = v_temp; + } + else + if ((ch == '1') && (params->av_name < 0)) + { + /* The variables is an array variable. */ + + /* Compute source index and make sure some structure exists. */ + ix = (int) num2long (ex_stack->s_num); + n_temp = get_array_num (ix, 0); + + /* Push a new array and Compute Destination index */ + auto_var (params->av_name); + ix1 = -params->av_name; + + /* Set up the correct pointers in the structure. */ + if (ix == ix1) + a_src = arrays[ix]->a_next; + else + a_src = arrays[ix]; + a_dest = arrays[ix1]; + a_dest->a_param = TRUE; + a_dest->a_value = a_src->a_value; + } + else + { + if (params->av_name < 0) + rt_error ("Parameter type mismatch parameter %s.", + a_names[-params->av_name]); + else + rt_error ("Parameter type mismatch, parameter %s.", + v_names[params->av_name]); + params++; + } + pop (); + } + else + { + if (!warned) + { + rt_error ("Parameter number mismatch"); + warned = TRUE; + } + } + params = params->next; + } + if (params != NULL) + rt_error ("Parameter number mismatch"); +} diff --git a/usr/othersrc/public/bc-1.01/util.c b/usr/othersrc/public/bc-1.01/util.c new file mode 100644 index 0000000000..80d578dd38 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/util.c @@ -0,0 +1,794 @@ +/* util.c: Utility routines for bc. */ + +/* This file is part of bc written for MINIX. + 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 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + + +#include "bcdefs.h" +#ifndef VARARGS +#include +#else +#include +#endif +#include "global.h" +#include "proto.h" + + +/* strcopyof mallocs new memory and copies a string to to the new + memory. */ + +char * +strcopyof (str) + char *str; +{ + char *temp; + + temp = (char *) bc_malloc (strlen (str)+1); + return (strcpy (temp,str)); +} + + +/* nextarg adds another value to the list of arguments. */ + +arg_list * +nextarg (args, val) + arg_list *args; + char val; +{ arg_list *temp; + + temp = (arg_list *) bc_malloc (sizeof (arg_list)); + temp->av_name = val; + temp->next = args; + + return (temp); +} + + +/* For generate, we must produce a string in the form + "val,val,...,val". We also need a couple of static variables + for retaining old generated strings. It also uses a recursive + function that builds the string. */ + +static char *arglist1 = NULL, *arglist2 = NULL; + + +/* make_arg_str does the actual construction of the argument string. + ARGS is the pointer to the list and LEN is the maximum number of + characters needed. 1 char is the minimum needed. COMMAS tells + if each number should be seperated by commas.*/ + +_PROTOTYPE (static char *make_arg_str, (arg_list *args, int len, int commas)); + +static char * +make_arg_str (args, len, commas) + arg_list *args; + int len; + int commas; +{ + char *temp; + char sval[20]; + + /* Recursive call. */ + if (args != NULL) + temp = make_arg_str (args->next, len+11, commas); + else + { + temp = (char *) bc_malloc (len); + *temp = 0; + return temp; + } + + /* Add the current number to the end of the string. */ + if (len != 1 && commas) + sprintf (sval, "%d,", args->av_name); + else + sprintf (sval, "%d", args->av_name); + temp = strcat (temp, sval); + return (temp); +} + +char * +arg_str (args, commas) + arg_list *args; + int commas; +{ + if (arglist2 != NULL) + free (arglist2); + arglist2 = arglist1; + arglist1 = make_arg_str (args, 1, commas); + return (arglist1); +} + + +/* free_args frees an argument list ARGS. */ + +void +free_args (args) + arg_list *args; +{ + arg_list *temp; + + temp = args; + while (temp != NULL) + { + args = args->next; + free (temp); + temp = args; + } +} + + +/* Check for valid parameter (PARAMS) and auto (AUTOS) lists. + There must be no duplicates any where. Also, this is where + warnings are generated for array parameters. */ + +void +check_params ( params, autos ) + arg_list *params, *autos; +{ + arg_list *tmp1, *tmp2; + + /* Check for duplicate parameters. */ + if (params != NULL) + { + tmp1 = params; + while (tmp1 != NULL) + { + tmp2 = tmp1->next; + while (tmp2 != NULL) + { + if (tmp2->av_name == tmp1->av_name) + yyerror ("duplicate parameter names"); + tmp2 = tmp2->next; + } + if (tmp1->av_name < 0) + warn ("Array parameter"); + tmp1 = tmp1->next; + } + } + + /* Check for duplicate autos. */ + if (autos != NULL) + { + tmp1 = autos; + while (tmp1 != NULL) + { + tmp2 = tmp1->next; + while (tmp2 != NULL) + { + if (tmp2->av_name == tmp1->av_name) + yyerror ("duplicate auto variable names"); + tmp2 = tmp2->next; + } + tmp1 = tmp1->next; + } + } + + /* Check for duplicate between parameters and autos. */ + if ((params != NULL) && (autos != NULL)) + { + tmp1 = params; + while (tmp1 != NULL) + { + tmp2 = autos; + while (tmp2 != NULL) + { + if (tmp2->av_name == tmp1->av_name) + yyerror ("variable in both parameter and auto lists"); + tmp2 = tmp2->next; + } + tmp1 = tmp1->next; + } + } +} + + +/* Initialize the code generator the parser. */ + +void +init_gen () +{ + /* Get things ready. */ + break_label = 0; + continue_label = 0; + next_label = 1; + out_count = 2; + if (compile_only) + printf ("@i"); + else + init_load (); + had_error = FALSE; + did_gen = FALSE; +} + + +/* generate code STR for the machine. */ + +void +generate (str) + char *str; +{ + did_gen = TRUE; + if (compile_only) + { + printf ("%s",str); + out_count += strlen(str); + if (out_count > 60) + { + printf ("\n"); + out_count = 0; + } + } + else + load_code (str); +} + + +/* Execute the current code as loaded. */ + +void +run_code() +{ + /* If no compile errors run the current code. */ + if (!had_error && did_gen) + { + if (compile_only) + { + printf ("@r\n"); + out_count = 0; + } + else + execute (); + } + + /* Reinitialize the code generation and machine. */ + if (did_gen) + init_gen(); + else + had_error = FALSE; +} + + +/* Output routines: Write a character CH to the standard output. + It keeps track of the number of characters output and may + break the output with a "\". */ + +void +out_char (ch) + char ch; +{ + if (ch == '\n') + { + out_col = 0; + putchar ('\n'); + } + else + { + out_col++; + if (out_col == 70) + { + putchar ('\\'); + putchar ('\n'); + out_col = 1; + } + putchar (ch); + } +} + + +/* The following are "Symbol Table" routines for the parser. */ + +/* find_id returns a pointer to node in TREE that has the correct + ID. If there is no node in TREE with ID, NULL is returned. */ + +id_rec * +find_id (tree, id) + id_rec *tree; + char *id; +{ + int cmp_result; + + /* Check for an empty tree. */ + if (tree == NULL) + return NULL; + + /* Recursively search the tree. */ + cmp_result = strcmp (id, tree->id); + if (cmp_result == 0) + return tree; /* This is the item. */ + else if (cmp_result < 0) + return find_id (tree->left, id); + else + return find_id (tree->right, id); +} + + +/* insert_id_rec inserts a NEW_ID rec into the tree whose ROOT is + provided. insert_id_rec returns TRUE if the tree height from + ROOT down is increased otherwise it returns FALSE. This is a + recursive balanced binary tree insertion algorithm. */ + +int insert_id_rec (root, new_id) + id_rec **root; + id_rec *new_id; +{ + id_rec *A, *B; + + /* If root is NULL, this where it is to be inserted. */ + if (*root == NULL) + { + *root = new_id; + new_id->left = NULL; + new_id->right = NULL; + new_id->balance = 0; + return (TRUE); + } + + /* We need to search for a leaf. */ + if (strcmp (new_id->id, (*root)->id) < 0) + { + /* Insert it on the left. */ + if (insert_id_rec (&((*root)->left), new_id)) + { + /* The height increased. */ + (*root)->balance --; + + switch ((*root)->balance) + { + case 0: /* no height increase. */ + return (FALSE); + case -1: /* height increase. */ + return (FALSE); + case -2: /* we need to do a rebalancing act. */ + A = *root; + B = (*root)->left; + if (B->balance <= 0) + { + /* Single Rotate. */ + A->left = B->right; + B->right = A; + *root = B; + A->balance = 0; + B->balance = 0; + } + else + { + /* Double Rotate. */ + *root = B->right; + B->right = (*root)->left; + A->left = (*root)->right; + (*root)->left = B; + (*root)->right = A; + switch ((*root)->balance) + { + case -1: + A->balance = 1; + B->balance = 0; + break; + case 0: + A->balance = 0; + B->balance = 0; + break; + case 1: + A->balance = 0; + B->balance = -1; + break; + } + (*root)->balance = 0; + } + } + } + } + else + { + /* Insert it on the right. */ + if (insert_id_rec (&((*root)->right), new_id)) + { + /* The height increased. */ + (*root)->balance ++; + switch ((*root)->balance) + { + case 0: /* no height increase. */ + return (FALSE); + case 1: /* height increase. */ + return (FALSE); + case 2: /* we need to do a rebalancing act. */ + A = *root; + B = (*root)->right; + if (B->balance >= 0) + { + /* Single Rotate. */ + A->right = B->left; + B->left = A; + *root = B; + A->balance = 0; + B->balance = 0; + } + else + { + /* Double Rotate. */ + *root = B->left; + B->left = (*root)->right; + A->right = (*root)->left; + (*root)->left = A; + (*root)->right = B; + switch ((*root)->balance) + { + case -1: + A->balance = 0; + B->balance = 1; + break; + case 0: + A->balance = 0; + B->balance = 0; + break; + case 1: + A->balance = -1; + B->balance = 0; + break; + } + (*root)->balance = 0; + } + } + } + } + + /* If we fall through to here, the tree did not grow in height. */ + return (FALSE); +} + + +/* Initialize variables for the symbol table tree. */ + +void +init_tree() +{ + name_tree = NULL; + next_array = 1; + next_func = 1; + next_var = 4; /* 0 => ibase, 1 => obase, 2 => scale, 3 => last. */ +} + + +/* Lookup routines for symbol table names. */ + +int +lookup (name, namekind) + char *name; + int namekind; +{ + id_rec *id; + + /* Warn about non-standard name. */ + if (strlen(name) != 1) + warn ("multiple letter name - %s", name); + + /* Look for the id. */ + id = find_id (name_tree, name); + if (id == NULL) + { + /* We need to make a new item. */ + id = (id_rec *) bc_malloc (sizeof (id_rec)); + id->id = strcopyof (name); + id->a_name = 0; + id->f_name = 0; + id->v_name = 0; + insert_id_rec (&name_tree, id); + } + + /* Return the correct value. */ + switch (namekind) + { + + case ARRAY: + /* ARRAY variable numbers are returned as negative numbers. */ + if (id->a_name != 0) + { + free (name); + return (-id->a_name); + } + id->a_name = next_array++; + a_names[id->a_name] = name; + if (id->a_name < MAX_STORE) + { + if (id->a_name >= a_count) + more_arrays (); + return (-id->a_name); + } + yyerror ("Too many array variables"); + exit (1); + + case FUNCT: + if (id->f_name != 0) + { + free(name); + return (id->f_name); + } + id->f_name = next_func++; + f_names[id->f_name] = name; + if (id->f_name < MAX_STORE) + { + if (id->f_name >= f_count) + more_functions (); + return (id->f_name); + } + yyerror ("Too many functions"); + exit (1); + + case SIMPLE: + if (id->v_name != 0) + { + free(name); + return (id->v_name); + } + id->v_name = next_var++; + v_names[id->v_name - 1] = name; + if (id->v_name <= MAX_STORE) + { + if (id->v_name >= v_count) + more_variables (); + return (id->v_name); + } + yyerror ("Too many variables"); + exit (1); + } +} + + +/* Print the welcome banner. */ + +void +welcome() +{ + printf ("This is free software with ABSOLUTELY NO WARRANTY.\n"); + printf ("For details type `warranty'. \n"); +} + + +/* Print out the warranty information. */ + +void +warranty(prefix) + char *prefix; +{ + printf ("\n%s%s\n\n", prefix, BC_VERSION); + printf ("%s%s%s%s%s%s%s%s%s%s%s", +" This program is free software; you can redistribute it and/or modify\n", +" it under the terms of the GNU General Public License as published by\n", +" the Free Software Foundation; either version 2 of the License , or\n", +" (at your option) any later version.\n\n", +" This program is distributed in the hope that it will be useful,\n", +" but WITHOUT ANY WARRANTY; without even the implied warranty of\n", +" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n", +" GNU General Public License for more details.\n\n", +" You should have received a copy of the GNU General Public License\n", +" along with this program. If not, write to the Free Software\n", +" Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n"); +} + +/* Print out the limits of this program. */ + +void +limits() +{ + printf ("BC_BASE_MAX = %d\n", BC_BASE_MAX); + printf ("BC_DIM_MAX = %ld\n", (long) BC_DIM_MAX); + printf ("BC_SCALE_MAX = %d\n", BC_SCALE_MAX); + printf ("BC_STRING_MAX = %d\n", BC_STRING_MAX); + printf ("MAX Exponent = %ld\n", (long) LONG_MAX); + printf ("MAX code = %ld\n", (long) BC_MAX_SEGS * (long) BC_SEG_SIZE); + printf ("multiply digits = %ld\n", (long) LONG_MAX / (long) 90); + printf ("Number of vars = %ld\n", (long) MAX_STORE); +#ifdef OLD_EQ_OP + printf ("Old assignment operatiors are valid. (=-, =+, ...)\n"); +#endif +} + +/* bc_malloc will check the return value so all other places do not + have to do it! SIZE is the number of types to allocate. */ + +char * +bc_malloc (size) + int size; +{ + char *ptr; + + ptr = (char *) malloc (size); + if (ptr == NULL) + out_of_memory (); + + return ptr; +} + + +/* The following routines are error routines for various problems. */ + +/* Malloc could not get enought memory. */ + +void +out_of_memory() +{ + fprintf (stderr, "Fatal error: Out of memory for malloc.\n"); + exit (1); +} + + + +/* The standard yyerror routine. Built with variable number of argumnets. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +yyerror (char *str, ...) +#else +void +yyerror (str) + char *str; +#endif +#else +void +yyerror (str, va_alist) + char *str; +#endif +{ + char *name; + va_list args; + +#ifndef VARARGS + va_start (args, str); +#else + va_start (args); +#endif + if (is_std_in) + name = "(standard_in)"; + else + name = g_argv[optind-1]; + fprintf (stderr,"%s %d: ",name,line_no); + vfprintf (stderr, str, args); + fprintf (stderr, "\n"); + had_error = TRUE; + va_end (args); +} + + +/* The routine to produce warnings about non-standard features + found during parsing. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +warn (char *mesg, ...) +#else +void +warn (mesg) + char *mesg; +#endif +#else +void +warn (mesg, va_alist) + char *mesg; +#endif +{ + char *name; + va_list args; + +#ifndef VARARGS + va_start (args, mesg); +#else + va_start (args); +#endif + if (std_only) + { + if (is_std_in) + name = "(standard_in)"; + else + name = g_argv[optind-1]; + fprintf (stderr,"%s %d: ",name,line_no); + vfprintf (stderr, mesg, args); + fprintf (stderr, "\n"); + had_error = TRUE; + } + else + if (warn_not_std) + { + if (is_std_in) + name = "(standard_in)"; + else + name = g_argv[optind-1]; + fprintf (stderr,"%s %d: (Warning) ",name,line_no); + vfprintf (stderr, mesg, args); + fprintf (stderr, "\n"); + } + va_end (args); +} + +/* Runtime error will print a message and stop the machine. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +rt_error (char *mesg, ...) +#else +void +rt_error (mesg) + char *mesg; +#endif +#else +void +rt_error (mesg, va_alist) + char *mesg; +#endif +{ + va_list args; + char error_mesg [255]; + +#ifndef VARARGS + va_start (args, mesg); +#else + va_start (args); +#endif + vsprintf (error_mesg, mesg, args); + va_end (args); + + fprintf (stderr, "Runtime error (func=%s, adr=%d): %s\n", + f_names[pc.pc_func], pc.pc_addr, error_mesg); + runtime_error = TRUE; +} + + +/* A runtime warning tells of some action taken by the processor that + may change the program execution but was not enough of a problem + to stop the execution. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +rt_warn (char *mesg, ...) +#else +void +rt_warn (mesg) + char *mesg; +#endif +#else +void +rt_warn (mesg, va_alist) + char *mesg; +#endif +{ + va_list args; + char error_mesg [255]; + +#ifndef VARARGS + va_start (args, mesg); +#else + va_start (args); +#endif + vsprintf (error_mesg, mesg, args); + va_end (args); + + fprintf (stderr, "Runtime warning (func=%s, adr=%d): %s\n", + f_names[pc.pc_func], pc.pc_addr, error_mesg); +} diff --git a/usr/othersrc/public/bc-1.01/version.h b/usr/othersrc/public/bc-1.01/version.h new file mode 100644 index 0000000000..72211ceda4 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/version.h @@ -0,0 +1,3 @@ +#define BC_VERSION \ + "bc 1.01 (Nov 25, 1991), Copyright (C) 1991 Free Software Foundation, Inc." + diff --git a/usr/othersrc/public/bc-1.01/vfprintf.c b/usr/othersrc/public/bc-1.01/vfprintf.c new file mode 100644 index 0000000000..4c00910329 --- /dev/null +++ b/usr/othersrc/public/bc-1.01/vfprintf.c @@ -0,0 +1,13 @@ +#include +#include +#include + +int vfprintf(file, format, argp) +FILE *file; +_CONST char *format; +va_list argp; +{ + _doprintf(file, format, argp); + if (testflag(file, PERPRINTF)) fflush(file); + return 0; +} diff --git a/usr/src/usr.bin/sed/ChangeLog b/usr/src/usr.bin/sed/ChangeLog new file mode 100644 index 0000000000..289fb4e570 --- /dev/null +++ b/usr/src/usr.bin/sed/ChangeLog @@ -0,0 +1,194 @@ +Thu Aug 8 00:15:33 1991 David J. MacKenzie (djm at bleen) + + * Version 1.08. + + * sed.c (compile_filename): If reading a file fails, read + /dev/null instead. It's what Unix and POSIX do, effectively. + + * sed.c (compile_regex): The 'slash' character doesn't + terminate the regex if it's in a character class. + + * sed.c (main): If given no args, or bad option, print usage + message. + (usage): New function. + + * sed.c (execute_program): Amount written for 'P' command was + wrong. From stephend@ksr.com (Stephen Davis). + +Wed Aug 7 16:51:14 1991 David J. MacKenzie (djm at apple-gunkies) + + * sed.c (append_pattern_space): Check for buffer full before + instead of after writing to buffer. Don't need to test for + EOF initially anymore, due to the next change. + (execute_program): For 'n' and 'N' commands, if eof is reached + in input, quit the script like Unix sed does. + Fix memory allocation problems for 'a' and 'r' commands. + (compile_program): Fix off by one error in processing comments. + All of the above are from Tapani Tarvainen, tarvaine@tukki.jyu.fi. + + * sed.c (setup_jump): Use isblank instead of testing for ' ' + or '\t', for POSIX locales. + + * utils.c (ck_strdup): Renamed from strdup. + * sed.c: Change callers. + + * sed.c, utils.c: Clean up declarations and includes to get + rid of compiler warnings. + + * sed.c (main): Add long-named options. Don't complain if -n + is given twice. + +Fri Aug 2 12:33:16 1991 David J. MacKenzie (djm at apple-gunkies) + + * configure: Support +srcdir arg. Create config.status and + remove it and Makefile if interrupted while creating them. + * Makefile.in: Change DESTDIR to prefix. + +Mon Jul 15 13:07:39 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * sed.c (main): Add -V option to print version number. + (USAGE): Mention -V. + +Mon Jul 8 01:42:22 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * sed.c: Define bcopy in terms of memcpy if STDC_HEADERS as + well as if USG. + (compile_filename): Don't glob filename (for 'r' and 'w' + commands). Unix sed doesn't do it and it's not very useful, + since it can only match 0 or 1 files. + (execute_program): Change '\a' to 007 since some compilers + don't recognize \a. + * utils.c: New file; code moved from sed.c. + * Replace Makefile with Makefile.in and configure. + Update README. + +Tue Mar 26 13:00:48 EST 1991 Jay Fenlason (hack@gnu.ai.mit.edu) + + * sed.c (match_address) Added a trivial cast for portability. + +Mon Feb 25 13:23:29 EST 1991 Jay Fenlason (hack@ai.mit.edu) + + * sed.c Changed 's' command to work with latest version of regex() + routines, which mysteriously changed somewhere in there. . . + A one-line patch from David Eckelkamp (eckelkamp@mcc.com). + + Initialize the fastmap in the hopes that it'll make sed faster. + +Thu Feb 21 13:42:27 EST 1991 Jay Fenlason (hack@ai.mti.edu) + + * sed.c Change panic to compile with other __STDC__ compilers. + +Wed Jan 30 10:46:38 EST 1991 Jay Fenlason (hack@ai.mit.edu) + + * sed.c Changed version number. Made new release. + +Tue Nov 27 15:34:51 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * sed.c (setup_jump) Don't blow chunks if there isn't a label + after a b or t command. + + (main) Don't panic if it a branch command doesn't have + a label to branch to. + + (main) Collect all the -e arguments together and parse them + all at once. This way, -e { -e mumble -e } will work. + + All these small patches from David Schmidt (davids@isc-br.isc-br.com) + +Tue Sep 11 12:51:37 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sed.c Changed some function forward declarations to use VOID * + instead of char * + +Mon Jul 16 11:12:54 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sed.c (ck_malloc) Use malloc(1) instead of malloc(0) if given + a request for zero bytes. + +Tue Jun 5 02:05:37 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * sed.c: Remove excess newlines from calls to panic. + Reformat some comments to fit in 79 columns. + Base whether to use void * on __STDC__, not __GNU__. + (main): Add missing arg when printing usage message. + Print usage if given invalid arg. + (panic) [__STDC__]: Add missing ", ...". + (compile_filename): Print correct error message if glob_filename + returns NULL. + +Thu Apr 5 21:41:12 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * sed.c (execute_program, case 'r'): When need to realloc append.text, + multiply append.alloc by 2 instead of adding + cur_cmd->x.cmd_txt.text_len. + +Tue Mar 6 15:55:35 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * sed.c (compile_regex) Allocate 10 bytes extra space needed by + re_compile_pattern. + +Sun Feb 25 16:32:10 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * sed.c (execute_program, case 'l'): Print \00 instead of \0. + Print backslash as \\ not \. + Print \xx instead of /xx. + +Thu Feb 1 14:02:28 EST 1990 hack@wookumz + + * sed.c (memchr) Use () inside inner loop so it will work correctly. + A two character patch from Robert A Bruce (rab@allspice.berkeley.edu) + +Wed Sep 27 18:47:39 EDT 1989 hack@ai.mit.edu + + * sed.c (compile_regex) New function. When compiling regex, + turn ^ into \` and $ into \' so that they won't match on embedded + newlines. UN*X pattern matching is a crock. + (compile_program, compile_address) call compile_regex. + +Mon Sep 18 10:15:32 EDT 1989 hack@ai.mit.edu + + * sed.c (compile_program): define translate as unsigned char * so + that y command will work on non-ascii characters. + + Changed version number to 1.06. + +Thu Sep 14 15:57:08 EDT 1989 hack@ai.mit.edu + + * sed.c (compile_program) Let programs use ; to terminate } as + well as newline. + + (read_file) Print an error msg to stderr if it can't open an + input file. + +Thu Mar 23 18:04:46 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * Makefile, sed.c: Added new copyright notice. + + * Makefile: Make distributions which follow the symlinks. + +hack@ai.mit.edu + + 1.05 Fixed error in 'r' (now does things in the right order) + + 1.04 Fixed s/re/rep/[number] + + 1.03 Fixes from Mike Haertel for regexps that match the + empty string, and for Ritchie stdio (non-sticky EOF) + + 1.02 Fixed 't', 'b', ':' to trim leading spaces and tabs + Fixed \\ in replacement of 's' command + Added comments + + 1.01 Added s/re/rep/[digits] + added #n as first line of script + added filename globbing + added 'l' command + All in the name of POSIX + + 1.00 Began (thinking about) distributing this file + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +End: diff --git a/usr/src/usr.bin/sed/Makefile.gnu b/usr/src/usr.bin/sed/Makefile.gnu new file mode 100644 index 0000000000..b4ad89dcce --- /dev/null +++ b/usr/src/usr.bin/sed/Makefile.gnu @@ -0,0 +1,84 @@ +# Generated automatically from Makefile.in by configure. +# Makefile for GNU SED, a batch editor. +# Copyright (C) 1987-1991 Free Software Foundation, Inc. +# +# This file is part of GNU SED. +# +# GNU SED 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. +# +# GNU SED 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 GNU SED; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +SHELL = /bin/sh + +#### Start of system configuration section. #### + +srcdir = . + + +CC = gcc -O +INSTALL = install -c + +# Things you might add to DEFS: +# -DSTDC_HEADERS If you have ANSI C headers and libraries. +# -DUSG If you have System V/ANSI C string +# and memory functions and headers. +# -DCHAR_UNSIGNED If type `char' is unsigned. +# -DNO_VFPRINTF If you lack vprintf function (but have _doprnt). + +DEFS = +LIBS = + +CFLAGS = -I$(srcdir) $(DEFS) +LDFLAGS = + +prefix = /usr/local + +# Where to install the executable. +bindir = $(prefix)/gnubin + +#### End of system configuration section. #### + +OBJS = sed.o utils.o regex.o getopt.o getopt1.o +SRCS = sed.c utils.c regex.c getopt.c getopt1.c +DISTFILES = COPYING ChangeLog README Makefile.in configure \ +regex.h getopt.h $(SRCS) + +all: sed + +sed: $(OBJS) + $(CC) -o $@ $(LDFLAGS) $(OBJS) $(LIBS) + +sed.o regex.o: regex.h +sed.o getopt1.o: getopt.h + +install: all + $(INSTALL) sed $(bindir) + +TAGS: $(SRCS) + etags $(SRCS) + +clean: + rm -f sed $(OBJS) core + +distclean: clean + rm -f TAGS Makefile config.status + +realclean: distclean + +dist: $(DISTFILES) + echo sed-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q sed.c` > .fname + rm -rf `cat .fname` + mkdir `cat .fname` + ln $(DISTFILES) `cat .fname` + tar chZf `cat .fname`.tar.Z `cat .fname` + rm -rf `cat .fname` .fname diff --git a/usr/src/usr.bin/sed/README b/usr/src/usr.bin/sed/README new file mode 100644 index 0000000000..85d1fa0f69 --- /dev/null +++ b/usr/src/usr.bin/sed/README @@ -0,0 +1,48 @@ +This directory contains GNU sed. Please report all bugs and comments +to bug-gnu-utils@prep.ai.mit.edu. + +This sed may run slower than some UN*X seds. This is because it uses +the regular-expression routines from Emacs, which are rather complete +and powerful, but not as fast as they could be. If you really care +about speed, use perl instead. + +To compile: + +1. Type `sh configure'. This shell script attempts to guess correct +values for various system-dependent variables used during compilation, +and creates the file `Makefile'. This takes a minute or so. + +If you want to compile in a different directory from the one +containing the source code, `cd' to that directory and run `configure' +with the option `+srcdir=DIR', where DIR is the directory that +contains the source code. The object files and executables will be +put in the current directory. This option only works with versions of +`make' that support the VPATH variable. `configure' ignores any other +arguments you give it. + +If your system requires unusual options for compilation or linking +that `configure' doesn't know about, you can give `configure' initial +values for variables by setting them in the environment; in +Bourne-compatible shells, you can do that on the command line like +this: +$ CC='gcc -traditional' LIBS=-lposix sh configure + +2. If you want to change the directories where the program will be +installed, or the optimization options, edit `Makefile' and change +those values. If you have an unusual system that needs special +compilation options that `configure' doesn't know about, and you +didn't pass them in the environment when running `configure', you +should add them to `Makefile' now. Alternately, teach `configure' how +to figure out that it is being run on a system where they are needed, +and mail the diffs to the address listed at the top of this file so we +can include them in the next release. + +3. Type `make'. + +4. If the program compiles successfully, type `make install' to +install it. + +5. After you have installed the program, you can remove the binary +from the source directory by typing `make clean'. Type `make +distclean' if you also want to remove `Makefile', for instance if you +are going to recompile sed next on another type of machine. diff --git a/usr/src/usr.bin/sed/getopt.c b/usr/src/usr.bin/sed/getopt.c new file mode 100644 index 0000000000..8c0ba2e29f --- /dev/null +++ b/usr/src/usr.bin/sed/getopt.c @@ -0,0 +1,594 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989, 1990, 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. */ + +/* AIX requires this to be the first thing in the file. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#ifdef sparc +#include +#else +#ifdef _AIX +#pragma alloca +#else +char *alloca (); +#endif +#endif /* sparc */ +#endif /* not __GNUC__ */ + +#ifndef __STDC__ +#define const +#endif + +/* 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 _POSIX_OPTION_ORDER 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 + +#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) +#include +#else /* STDC_HEADERS or __GNU_LIBRARY__ */ +char *getenv (); +char *malloc (); +#endif /* STDC_HEADERS or __GNU_LIBRARY__ */ + +#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) +#include +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#define index strchr +#else /* USG or STDC_HEADERS or __GNU_LIBRARY__ */ +#ifdef VMS +#include +#else /* VMS */ +#include +#endif /* VMS */ +/* Declaring bcopy causes errors on systems whose declarations are different. + If the declaration is omitted, everything works fine. */ +#endif /* USG or STDC_HEADERS or __GNU_LIBRARY__ */ + +/* 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 = 0; + +/* 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. */ + +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; + +/* 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 + _POSIX_OPTION_ORDER 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 _POSIX_OPTION_ORDER, 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; + +/* Describe the long-named options requested by the application. + _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an + element containing a name which is zero. + The field `has_arg' is 1 if the option takes an argument, + 2 if it takes an optional argument. */ + +struct option +{ + char *name; + int has_arg; + int *flag; + int val; +}; + +const struct option *_getopt_long_options; + +int _getopt_long_only = 0; + +/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found. + Only valid when a long-named option was found. */ + +int option_index; + +/* 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 nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) alloca (nonopts_size); + + /* Interchange the two blocks of data in ARGV. */ + + bcopy (&argv[first_nonopt], temp, nonopts_size); + bcopy (&argv[last_nonopt], &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* 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 + otherwise. */ + +int +getopt (argc, argv, optstring) + int argc; + char **argv; + const char *optstring; +{ + optarg = 0; + + /* Initialize the internal data when the first call is made. + 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. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = 0; + + /* 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 (getenv ("_POSIX_OPTION_ORDER") != 0) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == 0 || *nextchar == 0) + { + 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 (argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' + || argv[optind][1] == 0) + && (_getopt_long_options == 0 + || argv[optind][0] != '+' + || argv[optind][1] == 0)) + optind++; + last_nonopt = optind; + } + + /* 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 (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) + && (_getopt_long_options == 0 + || 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. + Start decoding its characters. */ + + nextchar = argv[optind] + 1; + } + + if (_getopt_long_options != 0 + && (argv[optind][0] == '+' + || (_getopt_long_only && argv[optind][0] == '-')) + ) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = 0; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = _getopt_long_options, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == 0) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != 0) + { + option_index = indfound; + optind++; + if (*s) + { + if (pfound->has_arg > 0) + optarg = s + 1; + else + { + 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 + { + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return '?'; + } + } + nextchar += strlen (nextchar); + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is getopt_long_only, + and the option starts with '-' and is a valid short + option, then interpret it as a short option. Otherwise it's + an error. */ + if (_getopt_long_only == 0 || argv[optind][0] == '+' || + index (optstring, *nextchar) == 0) + { + if (opterr != 0) + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == 0) + optind++; + + if (temp == 0 || c == ':') + { + if (opterr != 0) + { + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", + argv[0], 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 = 0; + nextchar = 0; + } + 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 != 0) + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = 0; + } + } + return c; + } +} + +#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/usr/src/usr.bin/sed/getopt.h b/usr/src/usr.bin/sed/getopt.h new file mode 100644 index 0000000000..151379fc69 --- /dev/null +++ b/usr/src/usr.bin/sed/getopt.h @@ -0,0 +1,102 @@ +/* declarations for getopt + Copyright (C) 1989, 1990, 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. */ + +/* 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; + +/* Describe the long-named options requested by the application. + _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an + element containing a name which is zero. + + The field `has_arg' is: + 0 if the option does not take an argument, + 1 if the option requires an argument, + 2 if the option takes an optional argument. + + If the field `flag' is nonzero, 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 +{ + char *name; + int has_arg; + int *flag; + int val; +}; + +#ifdef __STDC__ +extern const struct option *_getopt_long_options; +#else +extern struct option *_getopt_long_options; +#endif + +/* If nonzero, '-' can introduce long-named options. + Set by getopt_long_only. */ + +extern int _getopt_long_only; + +/* The index in GETOPT_LONG_OPTIONS of the long-named option found. + Only valid when a long-named option has been found by the most + recent call to `getopt'. */ + +extern int option_index; + +#ifdef __STDC__ +int getopt (int argc, char **argv, const char *shortopts); +int getopt_long (int argc, char **argv, const char *shortopts, + const struct option *longopts, int *longind); +int getopt_long_only (int argc, char **argv, const char *shortopts, + const struct option *longopts, int *longind); +void envopt(int *pargc, char ***pargv, char *optstr); +#else +int getopt (); +int getopt_long (); +int getopt_long_only (); +void envopt(); +#endif diff --git a/usr/src/usr.bin/sed/getopt1.c b/usr/src/usr.bin/sed/getopt1.c new file mode 100644 index 0000000000..781673c6f2 --- /dev/null +++ b/usr/src/usr.bin/sed/getopt1.c @@ -0,0 +1,158 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989, 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. */ + +#include "getopt.h" + +#ifndef __STDC__ +#define const +#endif + +#if !defined (NULL) +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char **argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + int val; + + _getopt_long_options = long_options; + val = getopt (argc, argv, options); + if (val == 0 && opt_index != NULL) + *opt_index = option_index; + return val; +} + +/* Like getopt_long, but '-' as well as '+' can indicate a long option. + If an option that starts with '-' 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 **argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + int val; + + _getopt_long_options = long_options; + _getopt_long_only = 1; + val = getopt (argc, argv, options); + if (val == 0 && opt_index != NULL) + *opt_index = option_index; + return val; +} + + +#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; + char *name = '\0'; + 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 '?': + 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/usr/src/usr.bin/sed/regex.c b/usr/src/usr.bin/sed/regex.c new file mode 100644 index 0000000000..a24d05cec8 --- /dev/null +++ b/usr/src/usr.bin/sed/regex.c @@ -0,0 +1,2781 @@ +/* Extended regular expression matching and search library. + Copyright (C) 1985, 1989-90 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. */ + + +/* To test, compile with -Dtest. This Dtestable feature turns this into + a self-contained program which reads a pattern, describes how it + compiles, then reads a string and searches for it. + + On the other hand, if you compile with both -Dtest and -Dcanned you + can run some tests we've already thought of. */ + +/* AIX requires the alloca decl to be the first thing in the file. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include +#else +#ifdef _AIX +#pragma alloca +#else +char *alloca (); +#endif +#endif +#endif + +#ifdef emacs + +/* The `emacs' switch turns on certain special matching commands + that make sense only in emacs. */ + +#include "config.h" +#include "lisp.h" +#include "buffer.h" +#include "syntax.h" + +#else /* not emacs */ + +#if defined (USG) || defined (STDC_HEADERS) +#ifndef BSTRING +#include +#define bcopy(s,d,n) memcpy((d),(s),(n)) +#define bcmp(s1,s2,n) memcmp((s1),(s2),(n)) +#define bzero(s,n) memset((s),0,(n)) +#endif +#endif + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +char *realloc (); +#endif + +/* Define the syntax stuff, so we can do the \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +#ifndef Sword +#define Sword 1 +#endif + +#define SYNTAX(c) re_syntax_table[c] + + +#ifdef SYNTAX_TABLE + +char *re_syntax_table; + +#else /* not SYNTAX_TABLE */ + +static char re_syntax_table[256]; + + +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; + + done = 1; +} + +#endif /* SYNTAX_TABLE */ +#endif /* emacs */ + +/* We write fatal error messages on standard error. */ +#include + +/* isalpha(3) etc. are used for the character classes. */ +#include +/* Sequents are missing isgraph. */ +#ifndef isgraph +#define isgraph(c) (isprint((c)) && !isspace((c))) +#endif + +/* Get the interface, including the syntax bits. */ +#include "regex.h" + + +/* These are the command codes that appear in compiled regular + expressions, one per byte. Some command codes 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. + + The value of `exactn' is needed in search.c (search_buffer) in emacs. + So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of + `exactn' we use here must also be 1. */ + +enum regexpcode + { + unused=0, + exactn=1, /* Followed by one byte giving n, then by n literal bytes. */ + begline, /* Fail unless at beginning of line. */ + endline, /* Fail unless at end of line. */ + jump, /* Followed by two bytes giving relative address to jump to. */ + on_failure_jump, /* Followed by two bytes giving relative address of + place to resume at in case of failure. */ + finalize_jump, /* Throw away latest failure point and then jump to + address. */ + maybe_finalize_jump, /* Like jump but finalize if safe to do so. + This is used to jump back to the beginning + of a repeat. If the command that follows + this jump is clearly incompatible with the + one at the beginning of the repeat, such that + we can be sure that there is no use backtracking + out of repetitions already completed, + then we finalize. */ + dummy_failure_jump, /* Jump, 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 + use it as an intermediary kind of jump when + compiling an or construct. */ + succeed_n, /* Used like on_failure_jump except has to succeed n times; + then gets turned into an on_failure_jump. The relative + address following it is useless until then. The + address is followed by two bytes containing n. */ + jump_n, /* Similar to jump, but jump n times only; also the relative + address following is in turn followed by yet two more bytes + containing n. */ + set_number_at, /* Set the following relative location to the + subsequent number. */ + anychar, /* Matches any (more or less) one character. */ + charset, /* 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_not, /* Same parameters as charset, but match any character + that is not one of those specified. */ + start_memory, /* Start remembering the text that is matched, for + storing in a memory register. Followed by one + byte containing the register number. Register numbers + must be in the range 0 through RE_NREGS. */ + stop_memory, /* Stop remembering the text that is matched + and store it in a memory register. Followed by + one byte containing the register number. Register + numbers must be in the range 0 through RE_NREGS. */ + duplicate, /* Match a duplicate of something remembered. + Followed by one byte containing the index of the memory + register. */ + before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ + begbuf, /* Succeeds if at beginning of buffer. */ + endbuf, /* Succeeds if at end of buffer. */ + 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. */ + syntaxspec, /* Matches any character whose syntax is specified. + followed by a byte which contains a syntax code, + e.g., Sword. */ + notsyntaxspec /* Matches any character whose syntax differs from + that specified. */ + }; + + +/* Number of failure points to allocate space for initially, + when matching. If this number is exceeded, more space is allocated, + so it is not a hard limit. */ + +#ifndef NFAILURES +#define NFAILURES 80 +#endif + +#ifdef CHAR_UNSIGNED +#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c)) /* for IBM RT */ +#endif +#ifndef SIGN_EXTEND_CHAR +#define SIGN_EXTEND_CHAR(x) (x) +#endif + + +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ +#define STORE_NUMBER(destination, number) \ + { (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; } + +/* Same as STORE_NUMBER, except increment the destination pointer to + the byte after where the number is stored. Watch out that values for + DESTINATION such as p + 1 won't work, whereas p will. */ +#define STORE_NUMBER_AND_INCR(destination, number) \ + { STORE_NUMBER(destination, number); \ + (destination) += 2; } + + +/* Put into DESTINATION a number stored in two contingous bytes starting + at SOURCE. */ +#define EXTRACT_NUMBER(destination, source) \ + { (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*(char *)((source) + 1)) << 8; } + +/* Same as EXTRACT_NUMBER, except increment the pointer for source to + point to second byte of SOURCE. Note that SOURCE has to be a value + such as p, not, e.g., p + 1. */ +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + { EXTRACT_NUMBER (destination, source); \ + (source) += 2; } + + +/* 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. */ + +int +re_set_syntax (syntax) + int syntax; +{ + int ret; + + ret = obscure_syntax; + obscure_syntax = syntax; + return ret; +} + +/* Set by re_set_syntax to the current regexp syntax to recognize. */ +int obscure_syntax = 0; + + + +/* Macros for re_compile_pattern, which is found below these definitions. */ + +#define CHAR_CLASS_MAX_LENGTH 6 + +/* Fetch the next character in the uncompiled pattern, translating it if + necessary. */ +#define PATFETCH(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (unsigned char *) p++; \ + if (translate) c = translate[c]; } + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (unsigned char *) p++; } + +#define PATUNFETCH p-- + + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 28 + +/* 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 CH to it. */ +#define BUFPUSH(ch) \ + { \ + GET_BUFFER_SPACE (1); \ + *b++ = (char) (ch); \ + } + +/* Extend the buffer by twice its current size via reallociation and + reset the pointers that pointed into the old allocation to point to + the correct places in the new allocation. If extending the buffer + results in it being larger than 1 << 16, then flag memory exhausted. */ +#define EXTEND_BUFFER \ + { char *old_buffer = bufp->buffer; \ + if (bufp->allocated == (1L<<16)) goto too_big; \ + bufp->allocated *= 2; \ + if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \ + bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated); \ + if (bufp->buffer == 0) \ + goto memory_exhausted; \ + b = (b - old_buffer) + bufp->buffer; \ + if (fixup_jump) \ + fixup_jump = (fixup_jump - old_buffer) + bufp->buffer; \ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } + +/* Set the bit for character C in a character set list. */ +#define SET_LIST_BIT(c) (b[(c) / BYTEWIDTH] |= 1 << ((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); \ + } \ + } \ + } + +/* Subroutines for re_compile_pattern. */ +static void store_jump (), insert_jump (), store_jump_n (), + insert_jump_n (), insert_op_2 (); + + +/* re_compile_pattern takes a regular-expression string + and converts it into a buffer full of byte commands for matching. + + PATTERN is the address of the pattern string + SIZE is the length of it. + BUFP is a struct re_pattern_buffer * which points to the info + on where to store the byte commands. + This structure contains a char * which points to the + actual space, which should have been obtained with malloc. + re_compile_pattern may use realloc to grow the buffer space. + + The number of bytes of commands can be found out by looking in + the `struct re_pattern_buffer' that bufp pointed to, after + re_compile_pattern returns. */ + +char * +re_compile_pattern (pattern, size, bufp) + char *pattern; + int size; + struct re_pattern_buffer *bufp; +{ + register char *b = bufp->buffer; + register char *p = pattern; + char *pend = pattern + size; + register unsigned c, c1; + char *p1; + unsigned char *translate = (unsigned char *) bufp->translate; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell whether a new exact-match + character can be added to that command or requires a new `exactn' + command. */ + + char *pending_exact = 0; + + /* 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. */ + + char *fixup_jump = 0; + + /* Address of start of the most recently finished expression. + This tells postfix * where to find the start of its operand. */ + + char *laststart = 0; + + /* In processing a repeat, 1 means zero matches is allowed. */ + + char zero_times_ok; + + /* In processing a repeat, 1 means many matches is allowed. */ + + char many_times_ok; + + /* Address of beginning of regexp, or inside of last \(. */ + + char *begalt = b; + + /* In processing an interval, at least this many matches must be made. */ + int lower_bound; + + /* In processing an interval, at most this many matches can be made. */ + int upper_bound; + + /* Place in pattern (i.e., the {) to which to go back if the interval + is invalid. */ + char *beg_interval = 0; + + /* Stack of information saved by \( and restored by \). + Four stack elements are pushed by each \(: + First, the value of b. + Second, the value of fixup_jump. + Third, the value of regnum. + Fourth, the value of begalt. */ + + int stackb[40]; + int *stackp = stackb; + int *stacke = stackb + 40; + int *stackt; + + /* Counts \('s as they are encountered. Remembered for the matching \), + where it becomes the register number to put in the stop_memory + command. */ + + int regnum = 1; + + bufp->fastmap_accurate = 0; + +#ifndef emacs +#ifndef SYNTAX_TABLE + /* Initialize the syntax table. */ + init_syntax_once(); +#endif +#endif + + if (bufp->allocated == 0) + { + bufp->allocated = INIT_BUF_SIZE; + if (bufp->buffer) + /* EXTEND_BUFFER loses when bufp->allocated is 0. */ + bufp->buffer = (char *) realloc (bufp->buffer, INIT_BUF_SIZE); + else + /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = (char *) malloc (INIT_BUF_SIZE); + if (!bufp->buffer) goto memory_exhausted; + begalt = b = bufp->buffer; + } + + while (p != pend) + { + PATFETCH (c); + + switch (c) + { + case '$': + { + char *p1 = p; + /* When testing what follows the $, + look past the \-constructs that don't consume anything. */ + if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + while (p1 != pend) + { + if (*p1 == '\\' && p1 + 1 != pend + && (p1[1] == '<' || p1[1] == '>' + || p1[1] == '`' || p1[1] == '\'' +#ifdef emacs + || p1[1] == '=' +#endif + || p1[1] == 'b' || p1[1] == 'B')) + p1 += 2; + else + break; + } + if (obscure_syntax & RE_TIGHT_VBAR) + { + if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p1 != pend) + goto normal_char; + /* Make operand of last vbar end before this `$'. */ + if (fixup_jump) + store_jump (fixup_jump, jump, b); + fixup_jump = 0; + BUFPUSH (endline); + break; + } + /* $ means succeed if at end of line, but only in special contexts. + If validly in the middle of a pattern, it is a normal character. */ + + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && p1 != pend) + goto invalid_pattern; + if (p1 == pend || *p1 == '\n' + || (obscure_syntax & RE_CONTEXT_INDEP_OPS) + || (obscure_syntax & RE_NO_BK_PARENS + ? *p1 == ')' + : *p1 == '\\' && p1[1] == ')') + || (obscure_syntax & RE_NO_BK_VBAR + ? *p1 == '|' + : *p1 == '\\' && p1[1] == '|')) + { + BUFPUSH (endline); + break; + } + goto normal_char; + } + case '^': + /* ^ means succeed if at beg of line, but only if no preceding + pattern. */ + + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && laststart) + goto invalid_pattern; + if (laststart && p - 2 >= pattern && p[-2] != '\n' + && !(obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + if (obscure_syntax & RE_TIGHT_VBAR) + { + if (p != pattern + 1 + && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + BUFPUSH (begline); + begalt = b; + } + else + BUFPUSH (begline); + break; + + case '+': + case '?': + if ((obscure_syntax & RE_BK_PLUS_QM) + || (obscure_syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern, char not special. */ + if (!laststart) + { + if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + /* If there is a sequence of repetition chars, + collapse it down to just one. */ + zero_times_ok = 0; + many_times_ok = 0; + while (1) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + if (p == pend) + break; + PATFETCH (c); + if (c == '*') + ; + else if (!(obscure_syntax & RE_BK_PLUS_QM) + && (c == '+' || c == '?')) + ; + else if ((obscure_syntax & RE_BK_PLUS_QM) + && c == '\\') + { + int c1; + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + c = c1; + } + else + { + PATUNFETCH; + break; + } + } + + /* 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) + { + /* If more than one repetition is allowed, 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). */ + GET_BUFFER_SPACE (3); + store_jump (b, maybe_finalize_jump, laststart - 3); + b += 3; /* Because store_jump put stuff here. */ + } + /* 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 (on_failure_jump, laststart, b + 3, b); + pending_exact = 0; + b += 3; + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + dummy-failure 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 (6); + insert_jump (dummy_failure_jump, laststart, laststart + 6, b); + b += 3; + } + break; + + case '.': + laststart = b; + BUFPUSH (anychar); + break; + + case '[': + if (p == pend) + goto invalid_pattern; + while (b - bufp->buffer + > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH) + EXTEND_BUFFER; + + laststart = b; + if (*p == '^') + { + BUFPUSH (charset_not); + p++; + } + else + BUFPUSH (charset); + p1 = p; + + BUFPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + /* Clear the whole map */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + + if ((obscure_syntax & RE_HAT_NOT_NEWLINE) && b[-2] == charset_not) + SET_LIST_BIT ('\n'); + + + /* Read in characters and ranges, setting map bits. */ + while (1) + { + /* Don't translate while fetching, in case it's a range bound. + When we set the bit for the character, we translate it. */ + PATFETCH_RAW (c); + + /* If set, \ escapes characters when inside [...]. */ + if ((obscure_syntax & RE_AWK_CLASS_HACK) && c == '\\') + { + PATFETCH(c1); + SET_LIST_BIT (c1); + continue; + } + if (c == ']') + { + if (p == p1 + 1) + { + /* If this is an empty bracket expression. */ + if ((obscure_syntax & RE_NO_EMPTY_BRACKETS) + && p == pend) + goto invalid_pattern; + } + else + /* Stop if this isn't merely a ] inside a bracket + expression, but rather the end of a bracket + expression. */ + break; + } + /* Get a range. */ + if (p[0] == '-' && p[1] != ']') + { + PATFETCH (c1); + /* Don't translate the range bounds while fetching them. */ + PATFETCH_RAW (c1); + + if ((obscure_syntax & RE_NO_EMPTY_RANGES) && c > c1) + goto invalid_pattern; + + if ((obscure_syntax & RE_NO_HYPHEN_RANGE_END) + && c1 == '-' && *p != ']') + goto invalid_pattern; + + while (c <= c1) + { + /* Translate each char that's in the range. */ + if (translate) + SET_LIST_BIT (translate[c]); + else + SET_LIST_BIT (c); + c++; + } + } + else if ((obscure_syntax & RE_CHAR_CLASSES) + && c == '[' && p[0] == ':') + { + /* Longest valid character class word has six characters. */ + char str[CHAR_CLASS_MAX_LENGTH]; + PATFETCH (c); + c1 = 0; + /* If no ] at end. */ + if (p == pend) + goto invalid_pattern; + while (1) + { + /* Don't translate the ``character class'' characters. */ + PATFETCH_RAW (c); + if (c == ':' || c == ']' || p == pend + || c1 == CHAR_CLASS_MAX_LENGTH) + break; + str[c1++] = c; + } + str[c1] = '\0'; + if (p == pend + || c == ']' /* End of the bracket expression. */ + || p[0] != ']' + || p + 1 == pend + || (strcmp (str, "alpha") != 0 + && strcmp (str, "upper") != 0 + && strcmp (str, "lower") != 0 + && strcmp (str, "digit") != 0 + && strcmp (str, "alnum") != 0 + && strcmp (str, "xdigit") != 0 + && strcmp (str, "space") != 0 + && strcmp (str, "print") != 0 + && strcmp (str, "punct") != 0 + && strcmp (str, "graph") != 0 + && strcmp (str, "cntrl") != 0)) + { + /* Undo the ending character, the letters, and leave + the leading : and [ (but set bits for them). */ + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + } + else + { + /* The ] at the end of the character class. */ + PATFETCH (c); + if (c != ']') + goto invalid_pattern; + for (c = 0; c < (1 << BYTEWIDTH); c++) + { + if ((strcmp (str, "alpha") == 0 && isalpha (c)) + || (strcmp (str, "upper") == 0 && isupper (c)) + || (strcmp (str, "lower") == 0 && islower (c)) + || (strcmp (str, "digit") == 0 && isdigit (c)) + || (strcmp (str, "alnum") == 0 && isalnum (c)) + || (strcmp (str, "xdigit") == 0 && isxdigit (c)) + || (strcmp (str, "space") == 0 && isspace (c)) + || (strcmp (str, "print") == 0 && isprint (c)) + || (strcmp (str, "punct") == 0 && ispunct (c)) + || (strcmp (str, "graph") == 0 && isgraph (c)) + || (strcmp (str, "cntrl") == 0 && iscntrl (c))) + SET_LIST_BIT (c); + } + } + } + else if (translate) + SET_LIST_BIT (translate[c]); + else + SET_LIST_BIT (c); + } + + /* Discard any character set/class bitmap bytes that are all + 0 at the end of the map. Decrement the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + break; + + case '(': + if (! (obscure_syntax & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_open; + + case ')': + if (! (obscure_syntax & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_close; + + case '\n': + if (! (obscure_syntax & RE_NEWLINE_OR)) + goto normal_char; + else + goto handle_bar; + + case '|': + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + && (! laststart || p == pend)) + goto invalid_pattern; + else if (! (obscure_syntax & RE_NO_BK_VBAR)) + goto normal_char; + else + goto handle_bar; + + case '{': + if (! ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + && (obscure_syntax & RE_INTERVALS))) + goto normal_char; + else + goto handle_interval; + + case '\\': + if (p == pend) goto invalid_pattern; + PATFETCH_RAW (c); + switch (c) + { + case '(': + if (obscure_syntax & RE_NO_BK_PARENS) + goto normal_backsl; + handle_open: + if (stackp == stacke) goto nesting_too_deep; + + /* Laststart should point to the start_memory that we are about + to push (unless the pattern has RE_NREGS or more ('s). */ + *stackp++ = b - bufp->buffer; + if (regnum < RE_NREGS) + { + BUFPUSH (start_memory); + BUFPUSH (regnum); + } + *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; + *stackp++ = regnum++; + *stackp++ = begalt - bufp->buffer; + fixup_jump = 0; + laststart = 0; + begalt = b; + break; + + case ')': + if (obscure_syntax & RE_NO_BK_PARENS) + goto normal_backsl; + handle_close: + if (stackp == stackb) goto unmatched_close; + begalt = *--stackp + bufp->buffer; + if (fixup_jump) + store_jump (fixup_jump, jump, b); + if (stackp[-1] < RE_NREGS) + { + BUFPUSH (stop_memory); + BUFPUSH (stackp[-1]); + } + stackp -= 2; + fixup_jump = *stackp ? *stackp + bufp->buffer - 1 : 0; + laststart = *--stackp + bufp->buffer; + break; + + case '|': + if ((obscure_syntax & RE_LIMITED_OPS) + || (obscure_syntax & RE_NO_BK_VBAR)) + goto normal_backsl; + handle_bar: + if (obscure_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 (6); + insert_jump (on_failure_jump, begalt, b + 6, b); + pending_exact = 0; + b += 3; + /* The alternative before the previous alternative has a + jump after it which gets executed if it gets matched. + Adjust that jump so it will jump to the previous + 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. */ + if (fixup_jump) + store_jump (fixup_jump, jump, b); + + /* Leave space for a jump after previous alternative---to be + filled in later. */ + fixup_jump = b; + b += 3; + + laststart = 0; + begalt = b; + break; + + case '{': + if (! (obscure_syntax & RE_INTERVALS) + /* Let \{ be a literal. */ + || ((obscure_syntax & RE_INTERVALS) + && (obscure_syntax & RE_NO_BK_CURLY_BRACES)) + /* If it's the string "\{". */ + || (p - 2 == pattern && p == pend)) + goto normal_backsl; + handle_interval: + beg_interval = p - 1; /* The {. */ + /* If there is no previous pattern, this isn't an interval. */ + if (!laststart) + { + if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else + goto normal_backsl; + } + /* It also isn't an interval if not preceded by an re + matching a single character or subexpression, or if + the current type of intervals can't handle back + references and the previous thing is a back reference. */ + if (! (*laststart == anychar + || *laststart == charset + || *laststart == charset_not + || *laststart == start_memory + || (*laststart == exactn && laststart[1] == 1) + || (! (obscure_syntax & RE_NO_BK_REFS) + && *laststart == duplicate))) + { + if (obscure_syntax & RE_NO_BK_CURLY_BRACES) + goto normal_char; + + /* Posix extended syntax is handled in previous + statement; this is for Posix basic syntax. */ + if (obscure_syntax & RE_INTERVALS) + goto invalid_pattern; + + goto normal_backsl; + } + lower_bound = -1; /* So can see if are set. */ + upper_bound = -1; + GET_UNSIGNED_NUMBER (lower_bound); + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) + upper_bound = RE_DUP_MAX; + } + if (upper_bound < 0) + upper_bound = lower_bound; + if (! (obscure_syntax & RE_NO_BK_CURLY_BRACES)) + { + if (c != '\\') + goto invalid_pattern; + PATFETCH (c); + } + if (c != '}' || lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound + || ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + && p != pend && *p == '{')) + { + if (obscure_syntax & RE_NO_BK_CURLY_BRACES) + goto unfetch_interval; + else + goto invalid_pattern; + } + + /* If 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 this jump is inserted. */ + + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + insert_jump (jump, laststart, b + 3, b); + b += 3; + } + + /* Otherwise, after lower_bound number of succeeds, jump + to after the jump_n which will be inserted at the end + of the buffer, and insert that jump_n. */ + else + { /* Set to 5 if only one repetition is allowed and + hence no jump_n is inserted at the current end of + the buffer; then only space for the succeed_n is + needed. Otherwise, need space for both the + succeed_n and the jump_n. */ + + unsigned slots_needed = upper_bound == 1 ? 5 : 10; + + GET_BUFFER_SPACE (slots_needed); + /* Initialize the succeed_n to n, even though it will + be set by its attendant set_number_at, because + re_compile_fastmap will need to know it. Jump to + what the end of buffer will be after inserting + this succeed_n and possibly appending a jump_n. */ + insert_jump_n (succeed_n, laststart, b + slots_needed, + b, lower_bound); + b += 5; /* Just increment for the succeed_n here. */ + + /* More than one repetition is allowed, so put in at + the end of the buffer a backward jump from b to the + succeed_n we put in above. By the time we've gotten + to this jump when matching, we'll have matched once + already, so jump back only upper_bound - 1 times. */ + + if (upper_bound > 1) + { + store_jump_n (b, jump_n, laststart, upper_bound - 1); + b += 5; + /* When hit this when matching, reset the + preceding jump_n's n to upper_bound - 1. */ + BUFPUSH (set_number_at); + GET_BUFFER_SPACE (2); + STORE_NUMBER_AND_INCR (b, -5); + STORE_NUMBER_AND_INCR (b, upper_bound - 1); + } + /* When hit this when matching, set the succeed_n's n. */ + GET_BUFFER_SPACE (5); + insert_op_2 (set_number_at, laststart, b, 5, lower_bound); + b += 5; + } + pending_exact = 0; + beg_interval = 0; + break; + + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + if (beg_interval) + p = beg_interval; + else + { + fprintf (stderr, + "regex: no interval beginning to which to backtrack.\n"); + exit (1); + } + + beg_interval = 0; + PATFETCH (c); /* normal_char expects char in `c'. */ + goto normal_char; + break; + +#ifdef emacs + case '=': + BUFPUSH (at_dot); + break; + + case 's': + laststart = b; + BUFPUSH (syntaxspec); + PATFETCH (c); + BUFPUSH (syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + BUFPUSH (notsyntaxspec); + PATFETCH (c); + BUFPUSH (syntax_spec_code[c]); + break; +#endif /* emacs */ + + case 'w': + laststart = b; + BUFPUSH (wordchar); + break; + + case 'W': + laststart = b; + BUFPUSH (notwordchar); + break; + + case '<': + BUFPUSH (wordbeg); + break; + + case '>': + BUFPUSH (wordend); + break; + + case 'b': + BUFPUSH (wordbound); + break; + + case 'B': + BUFPUSH (notwordbound); + break; + + case '`': + BUFPUSH (begbuf); + break; + + case '\'': + BUFPUSH (endbuf); + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (obscure_syntax & RE_NO_BK_REFS) + goto normal_char; + c1 = c - '0'; + if (c1 >= regnum) + { + if (obscure_syntax & RE_NO_EMPTY_BK_REF) + goto invalid_pattern; + else + goto normal_char; + } + /* Can't back reference to a subexpression if inside of it. */ + for (stackt = stackp - 2; stackt > stackb; stackt -= 4) + if (*stackt == c1) + goto normal_char; + laststart = b; + BUFPUSH (duplicate); + BUFPUSH (c1); + break; + + case '+': + case '?': + if (obscure_syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backsl; + break; + + default: + normal_backsl: + /* 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. */ + if (translate) c = translate[c]; + goto normal_char; + } + break; + + default: + normal_char: /* Expects the character in `c'. */ + if (!pending_exact || pending_exact + *pending_exact + 1 != b + || *pending_exact == 0177 || *p == '*' || *p == '^' + || ((obscure_syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((obscure_syntax & RE_INTERVALS) + && ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + laststart = b; + BUFPUSH (exactn); + pending_exact = b; + BUFPUSH (0); + } + BUFPUSH (c); + (*pending_exact)++; + } + } + + if (fixup_jump) + store_jump (fixup_jump, jump, b); + + if (stackp != stackb) goto unmatched_open; + + bufp->used = b - bufp->buffer; + return 0; + + invalid_pattern: + return "Invalid regular expression"; + + unmatched_open: + return "Unmatched \\("; + + unmatched_close: + return "Unmatched \\)"; + + end_of_pattern: + return "Premature end of regular expression"; + + nesting_too_deep: + return "Nesting too deep"; + + too_big: + return "Regular expression too big"; + + memory_exhausted: + return "Memory exhausted"; +} + + +/* Store a jump of the form . + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store. */ + +static void +store_jump (from, opcode, to) + char *from, *to; + char opcode; +{ + from[0] = opcode; + STORE_NUMBER(from + 1, to - (from + 3)); +} + + +/* Open up space before char FROM, and insert there a jump to TO. + CURRENT_END gives the end of the storage not in use, so we know + how much data to copy up. OP is the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_jump (op, from, to, current_end) + char op; + char *from, *to, *current_end; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 3; /* ...to here. */ + + while (pfrom != from) + *--pto = *--pfrom; + store_jump (from, op, to); +} + + +/* Store a jump of the form . + + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store, N is a number the + jump uses, say, to decide how many times to jump. + + If you call this function, you must zero out pending_exact. */ + +static void +store_jump_n (from, opcode, to, n) + char *from, *to; + char opcode; + unsigned n; +{ + from[0] = opcode; + STORE_NUMBER (from + 1, to - (from + 3)); + STORE_NUMBER (from + 3, n); +} + + +/* Similar to insert_jump, but handles a jump which needs an extra + number to handle minimum and maximum cases. Open up space at + location FROM, and insert there a jump to TO. CURRENT_END gives the + end of the storage in use, so we know how much data to copy up. OP is + the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_jump_n (op, from, to, current_end, n) + char op; + char *from, *to, *current_end; + unsigned n; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != from) + *--pto = *--pfrom; + store_jump_n (from, op, to, n); +} + + +/* Open up space at location THERE, and insert operation OP followed by + NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so + we know how much data to copy up. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_op_2 (op, there, current_end, num_1, num_2) + char op; + char *there, *current_end; + int num_1, num_2; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != there) + *--pto = *--pfrom; + + there[0] = op; + STORE_NUMBER (there + 1, num_1); + STORE_NUMBER (there + 3, num_2); +} + + + +/* Given a pattern, compute a fastmap from it. The 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 totally implausible text. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as bufp->fastmap. + The other components of bufp describe the pattern to be used. */ + +void +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *pattern = (unsigned char *) bufp->buffer; + int size = bufp->used; + register char *fastmap = bufp->fastmap; + register unsigned char *p = pattern; + register unsigned char *pend = pattern + size; + register int j, k; + unsigned char *translate = (unsigned char *) bufp->translate; + + unsigned char *stackb[NFAILURES]; + unsigned char **stackp = stackb; + + unsigned is_a_succeed_n; + + bzero (fastmap, (1 << BYTEWIDTH)); + bufp->fastmap_accurate = 1; + bufp->can_be_null = 0; + + while (p) + { + is_a_succeed_n = 0; + if (p == pend) + { + bufp->can_be_null = 1; + break; + } +#ifdef SWITCH_ENUM_BUG + switch ((int) ((enum regexpcode) *p++)) +#else + switch ((enum regexpcode) *p++) +#endif + { + case exactn: + if (translate) + fastmap[translate[p[1]]] = 1; + else + fastmap[p[1]] = 1; + break; + + case begline: + case before_dot: + case at_dot: + case after_dot: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + continue; + + case endline: + if (translate) + fastmap[translate['\n']] = 1; + else + fastmap['\n'] = 1; + + if (bufp->can_be_null != 1) + bufp->can_be_null = 2; + break; + + case jump_n: + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (j > 0) + continue; + /* Jump backward reached implies we just went through + the body of a loop and matched nothing. + Opcode jumped to should be an on_failure_jump. + Just treat it like an ordinary jump. + For a * loop, it has pushed its failure point already; + If so, discard that as redundant. */ + + if ((enum regexpcode) *p != on_failure_jump + && (enum regexpcode) *p != succeed_n) + continue; + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (stackp != stackb && *stackp == p) + stackp--; + continue; + + case on_failure_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + *++stackp = p + j; + if (is_a_succeed_n) + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + continue; + + case succeed_n: + is_a_succeed_n = 1; + /* 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; + goto handle_on_failure_jump; + } + continue; + + case set_number_at: + p += 4; + continue; + + case start_memory: + case stop_memory: + p++; + continue; + + case duplicate: + bufp->can_be_null = 1; + fastmap['\n'] = 1; + case anychar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (j != '\n') + fastmap[j] = 1; + if (bufp->can_be_null) + return; + /* Don't return; check the alternative paths + so we can set can_be_null if appropriate. */ + 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; + +#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; +#endif /* not emacs */ + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + { + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + } + break; + + case charset_not: + /* Chars beyond end of map must be allowed */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + { + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + } + break; + } + + /* Get here means we have successfully found the possible starting + characters of one path of the pattern. We need not follow this + path any farther. Instead, look at the next alternative + remembered in the stack. */ + if (stackp != stackb) + p = *stackp--; + else + break; + } +} + + + +/* Like re_search_2, below, but only one string is specified, and + doesn't let you say where to stop matching. */ + +int +re_search (pbufp, string, size, startpos, range, regs) + struct re_pattern_buffer *pbufp; + char *string; + int size, startpos, range; + struct re_registers *regs; +{ + return re_search_2 (pbufp, (char *) 0, 0, string, size, startpos, range, + regs, size); +} + + +/* Using the compiled pattern in PBUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. RANGE is the number of + places to try before giving up. If RANGE is negative, it searches + backwards, i.e., the starting positions tried are STARTPOS, STARTPOS + - 1, etc. STRING1 and STRING2 are of SIZE1 and SIZE2, respectively. + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire PBUFP->buffer and its contained + subexpressions. Do not consider matching one past the index MSTOP in + the virtual concatenation of STRING1 and STRING2. + + The value returned is the position in the strings at which the match + was found, or -1 if no match was found, or -2 if error (such as + failure stack overflow). */ + +int +re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, + regs, mstop) + struct re_pattern_buffer *pbufp; + char *string1, *string2; + int size1, size2; + int startpos; + register int range; + struct re_registers *regs; + int mstop; +{ + register char *fastmap = pbufp->fastmap; + register unsigned char *translate = (unsigned char *) pbufp->translate; + int total_size = size1 + size2; + int endpos = startpos + range; + int val; + + /* Check for out-of-range starting position. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up range if it would eventually take startpos outside of the + virtual concatenation of string1 and string2. */ + if (endpos < -1) + range = -1 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* Update the fastmap now if not correct already. */ + if (fastmap && !pbufp->fastmap_accurate) + re_compile_fastmap (pbufp); + + /* If the search isn't to be a backwards one, don't waste time in a + long search for a pattern that says it is anchored. */ + if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf + && range > 0) + { + if (startpos > 0) + return -1; + else + range = 1; + } + + while (1) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot possibly be the start of a match. Note, however, that + if the pattern can possibly match the null string, we must + test it at each starting point so that we take the first null + string we get. */ + + if (fastmap && startpos < total_size && pbufp->can_be_null != 1) + { + if (range > 0) /* Searching forwards. */ + { + register int lim = 0; + register unsigned char *p; + int irange = range; + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + + p = ((unsigned char *) + &(startpos >= size1 ? string2 - size1 : string1)[startpos]); + + while (range > lim && !fastmap[translate + ? translate[*p++] + : *p++]) + range--; + startpos += irange - range; + } + else /* Searching backwards. */ + { + register unsigned char c; + + if (string1 == 0 || startpos >= size1) + c = string2[startpos - size1]; + else + c = string1[startpos]; + + c &= 0xff; + if (translate ? !fastmap[translate[c]] : !fastmap[c]) + goto advance; + } + } + + if (range >= 0 && startpos == total_size + && fastmap && pbufp->can_be_null == 0) + return -1; + + val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, + regs, mstop); + if (val >= 0) + return startpos; + if (val == -2) + return -2; + +#ifdef C_ALLOCA + alloca (0); +#endif /* C_ALLOCA */ + + advance: + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } + } + return -1; +} + + + +#ifndef emacs /* emacs never uses this. */ +int +re_match (pbufp, string, size, pos, regs) + struct re_pattern_buffer *pbufp; + char *string; + int size, pos; + struct re_registers *regs; +{ + return re_match_2 (pbufp, (char *) 0, 0, string, size, pos, regs, size); +} +#endif /* not emacs */ + + +/* The following are used for re_match_2, defined below: */ + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always pushed MAX_NUM_FAILURE_ITEMS each time we failed. */ + +int re_max_failures = 2000; + +/* Routine used by re_match_2. */ +static int bcmp_translate (); + + +/* Structure and accessing macros used in re_match_2: */ + +struct register_info +{ + unsigned is_active : 1; + unsigned matched_something : 1; +}; + +#define IS_ACTIVE(R) ((R).is_active) +#define MATCHED_SOMETHING(R) ((R).matched_something) + + +/* Macros used by re_match_2: */ + + +/* I.e., regstart, regend, and reg_info. */ + +#define NUM_REG_ITEMS 3 + +/* We push at most this many things on the stack whenever we + fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are + arguments to the PUSH_FAILURE_POINT macro. */ + +#define MAX_NUM_FAILURE_ITEMS (RE_NREGS * NUM_REG_ITEMS + 2) + + +/* We push this many things on the stack whenever we fail. */ + +#define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + 2) + + +/* This pushes most of the information about the current state we will want + if we ever fail back to it. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place) \ + { \ + short last_used_reg, this_reg; \ + \ + /* Find out how many registers are active or have been matched. \ + (Aside from register zero, which is only set at the end.) */ \ + for (last_used_reg = RE_NREGS - 1; last_used_reg > 0; last_used_reg--)\ + if (regstart[last_used_reg] != (unsigned char *) -1) \ + break; \ + \ + if (stacke - stackp < NUM_FAILURE_ITEMS) \ + { \ + unsigned char **stackx; \ + unsigned int len = stacke - stackb; \ + if (len > re_max_failures * MAX_NUM_FAILURE_ITEMS) \ + return -2; \ + \ + /* Roughly double the size of the stack. */ \ + stackx = (unsigned char **) alloca (2 * len \ + * sizeof (unsigned char *));\ + /* Only copy what is in use. */ \ + bcopy (stackb, stackx, len * sizeof (char *)); \ + stackp = stackx + (stackp - stackb); \ + stackb = stackx; \ + stacke = stackb + 2 * len; \ + } \ + \ + /* Now push the info for each of those registers. */ \ + for (this_reg = 1; this_reg <= last_used_reg; this_reg++) \ + { \ + *stackp++ = regstart[this_reg]; \ + *stackp++ = regend[this_reg]; \ + *stackp++ = (unsigned char *) ®_info[this_reg]; \ + } \ + \ + /* Push how many registers we saved. */ \ + *stackp++ = (unsigned char *) last_used_reg; \ + \ + *stackp++ = pattern_place; \ + *stackp++ = string_place; \ + } + + +/* This pops what PUSH_FAILURE_POINT pushes. */ + +#define POP_FAILURE_POINT() \ + { \ + int temp; \ + stackp -= 2; /* Remove failure points. */ \ + temp = (int) *--stackp; /* How many regs pushed. */ \ + temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \ + stackp -= temp; /* Remove the register info. */ \ + } + + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Is true if there is a first string and if PTR is pointing anywhere + inside it or just past the end. */ + +#define IS_IN_FIRST_STRING(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* 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; \ + } + + +/* Call this when have matched something; it sets `matched' flags for the + registers corresponding to the subexpressions of which we currently + are inside. */ +#define SET_REGS_MATCHED \ + { unsigned this_reg; \ + for (this_reg = 0; this_reg < RE_NREGS; this_reg++) \ + { \ + if (IS_ACTIVE(reg_info[this_reg])) \ + MATCHED_SOMETHING(reg_info[this_reg]) = 1; \ + else \ + MATCHED_SOMETHING(reg_info[this_reg]) = 0; \ + } \ + } + +/* Test if at very beginning or at very end of the virtual concatenation + of string1 and string2. If there is only one string, we've put it in + string2. */ + +#define AT_STRINGS_BEG (d == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END (d == end2) + +#define AT_WORD_BOUNDARY \ + (AT_STRINGS_BEG || AT_STRINGS_END || IS_A_LETTER (d - 1) != IS_A_LETTER (d)) + +/* We have two special cases to check for: + 1) if we're past the end of string1, we have to look at the first + character in string2; + 2) if we're before the beginning of string2, we have to look at the + last character in string1; we assume there is a string1, so use + this in conjunction with AT_STRINGS_BEG. */ +#define IS_A_LETTER(d) \ + (SYNTAX ((d) == end1 ? *string2 : (d) == string2 - 1 ? *(end1 - 1) : *(d))\ + == Sword) + + +/* Match the pattern described by PBUFP against the virtual + concatenation of STRING1 and STRING2, which are of SIZE1 and SIZE2, + respectively. Start the match at index POS in the virtual + concatenation of STRING1 and STRING2. In REGS, return the indices of + the virtual concatenation of STRING1 and STRING2 that matched the + entire PBUFP->buffer and its contained subexpressions. Do not + consider matching one past the index MSTOP in the virtual + concatenation of STRING1 and STRING2. + + If pbufp->fastmap is nonzero, then it had better be up to date. + + The reason that the data to match are specified as two components + which are to be regarded as concatenated is so this function can be + used directly on the contents of an Emacs buffer. + + -1 is returned if there is no match. -2 is returned if there is an + error (such as match stack overflow). Otherwise the value is the + length of the substring which was matched. */ + +int +re_match_2 (pbufp, string1_arg, size1, string2_arg, size2, pos, regs, mstop) + struct re_pattern_buffer *pbufp; + char *string1_arg, *string2_arg; + int size1, size2; + int pos; + struct re_registers *regs; + int mstop; +{ + register unsigned char *p = (unsigned char *) pbufp->buffer; + + /* Pointer to beyond end of buffer. */ + register unsigned char *pend = p + pbufp->used; + + unsigned char *string1 = (unsigned char *) string1_arg; + unsigned char *string2 = (unsigned char *) string2_arg; + unsigned char *end1; /* Just past end of first string. */ + unsigned char *end2; /* Just past end of second string. */ + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ + unsigned char *end_match_1, *end_match_2; + + register unsigned char *d, *dend; + register int mcnt; /* Multipurpose. */ + unsigned char *translate = (unsigned char *) pbufp->translate; + unsigned is_a_jump_n = 0; + + /* 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. */ + + unsigned char *initial_stack[MAX_NUM_FAILURE_ITEMS * NFAILURES]; + unsigned char **stackb = initial_stack; + unsigned char **stackp = stackb; + unsigned char **stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES]; + + + /* 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.) */ + + unsigned char *regstart[RE_NREGS]; + unsigned char *regend[RE_NREGS]; + + /* 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. */ + + struct register_info reg_info[RE_NREGS]; + + + /* 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 = 0; + unsigned char *best_regstart[RE_NREGS]; + unsigned char *best_regend[RE_NREGS]; + + /* Initialize subexpression text positions to -1 to mark ones that no + \( or ( and \) or ) has been seen for. Also set all registers to + inactive and mark them as not having matched anything or ever + failed. */ + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + { + regstart[mcnt] = regend[mcnt] = (unsigned char *) -1; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + if (regs) + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; + + /* Set up pointers to ends of strings. + Don't allow the second string to be empty unless both are empty. */ + if (size2 == 0) + { + string2 = string1; + size2 = size1; + string1 = 0; + size1 = 0; + } + end1 = string1 + size1; + end2 = string2 + size2; + + /* Compute where to stop matching, within the two strings. */ + if (mstop <= size1) + { + end_match_1 = string1 + mstop; + end_match_2 = string2; + } + else + { + end_match_1 = end1; + end_match_2 = string2 + mstop - 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; + + + /* This loops over pattern commands. It exits by returning from the + function if match is complete, or it drops through if match fails + at this starting point in the input data. */ + + while (1) + { + is_a_jump_n = 0; + /* End of pattern means we might have succeeded. */ + if (p == pend) + { + /* If not end of string, try backtracking. Otherwise done. */ + if (d != end_match_2) + { + if (stackp != stackb) + { + /* More failure points to try. */ + + unsigned in_same_string = + IS_IN_FIRST_STRING (best_regend[0]) + == MATCHING_IN_FIRST_STRING; + + /* If exceeds best match so far, save it. */ + if (! best_regs_set + || (in_same_string && d > best_regend[0]) + || (! in_same_string && ! MATCHING_IN_FIRST_STRING)) + { + best_regs_set = 1; + best_regend[0] = d; /* Never use regstart[0]. */ + + for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + /* If no failure points, don't restore garbage. */ + else if (best_regs_set) + { + restore_best_regs: + /* Restore best match. */ + d = best_regend[0]; + + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } + + /* If caller wants register contents data back, convert it + to indices. */ + if (regs) + { + regs->start[0] = pos; + if (MATCHING_IN_FIRST_STRING) + regs->end[0] = d - string1; + else + regs->end[0] = d - string2 + size1; + for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + { + if (regend[mcnt] == (unsigned char *) -1) + { + regs->start[mcnt] = -1; + regs->end[mcnt] = -1; + continue; + } + if (IS_IN_FIRST_STRING (regstart[mcnt])) + regs->start[mcnt] = regstart[mcnt] - string1; + else + regs->start[mcnt] = regstart[mcnt] - string2 + size1; + + if (IS_IN_FIRST_STRING (regend[mcnt])) + regs->end[mcnt] = regend[mcnt] - string1; + else + regs->end[mcnt] = regend[mcnt] - string2 + size1; + } + } + return d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + } + + /* Otherwise match next pattern command. */ +#ifdef SWITCH_ENUM_BUG + switch ((int) ((enum regexpcode) *p++)) +#else + switch ((enum regexpcode) *p++) +#endif + { + + /* \( [or `(', as appropriate] is represented by start_memory, + \) by stop_memory. Both of those commands are followed by + a register number in the next byte. The text matched + within the \( and \) is recorded under that number. */ + case start_memory: + regstart[*p] = d; + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + p++; + break; + + case stop_memory: + regend[*p] = d; + IS_ACTIVE (reg_info[*p]) = 0; + + /* If just failed to match something this time around with a sub- + expression that's in a loop, try to force exit from the loop. */ + if ((! MATCHED_SOMETHING (reg_info[*p]) + || (enum regexpcode) p[-3] == start_memory) + && (p + 1) != pend) + { + register unsigned char *p2 = p + 1; + mcnt = 0; + switch (*p2++) + { + case jump_n: + is_a_jump_n = 1; + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p2); + if (is_a_jump_n) + p2 += 2; + break; + } + p2 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump, 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 && (enum regexpcode) *p2++ == on_failure_jump) + { + EXTRACT_NUMBER_AND_INCR (mcnt, p2); + PUSH_FAILURE_POINT (p2 + mcnt, d); + goto fail; + } + } + p++; + break; + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of as the register number. */ + case duplicate: + { + int regno = *p++; /* Get which register to match against */ + register unsigned char *d2, *dend2; + + /* 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 = ((IS_IN_FIRST_STRING (regstart[regno]) + == IS_IN_FIRST_STRING (regend[regno])) + ? regend[regno] : end_match_1); + while (1) + { + /* If necessary, advance to next segment in register + contents. */ + while (d2 == dend2) + { + if (dend2 == end_match_2) break; + if (dend2 == regend[regno]) break; + d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */ + } + /* 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; + + case anychar: + PREFETCH; /* Fetch a data character. */ + /* Match anything but a newline, maybe even a null. */ + if ((translate ? translate[*d] : *d) == '\n' + || ((obscure_syntax & RE_DOT_NOT_NULL) + && (translate ? translate[*d] : *d) == '\000')) + goto fail; + SET_REGS_MATCHED; + d++; + break; + + case charset: + case charset_not: + { + int not = 0; /* Nonzero for charset_not. */ + register int c; + if (*(p - 1) == (unsigned char) charset_not) + not = 1; + + PREFETCH; /* Fetch a data character. */ + + if (translate) + c = translate[*d]; + else + c = *d; + + if (c < *p * BYTEWIDTH + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + SET_REGS_MATCHED; + d++; + break; + } + + case begline: + if ((size1 != 0 && d == string1) + || (size1 == 0 && size2 != 0 && d == string2) + || (d && d[-1] == '\n') + || (size1 == 0 && size2 == 0)) + break; + else + goto fail; + + case endline: + if (d == end2 + || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n')) + break; + goto fail; + + /* `or' constructs are handled by starting each alternative with + an on_failure_jump that points to the start of the next + alternative. Each alternative except the last ends with a + jump to the joining point. (Actually, each jump except for + the last one really jumps to the following jump, because + tensioning the jumps is a hassle.) */ + + /* The start of a stupid repeat has an on_failure_jump that points + past the end of the repeat text. This makes a failure point so + that on failure to match a repetition, matching restarts past + as many repetitions have been found with no way to fail and + look for another one. */ + + /* A smart repeat is similar but loops back to the on_failure_jump + so that each repetition makes another failure point. */ + + case on_failure_jump: + on_failure: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + PUSH_FAILURE_POINT (p + mcnt, d); + break; + + /* The end of a smart repeat has a maybe_finalize_jump back. + Change it either to a finalize_jump or an ordinary jump. */ + case maybe_finalize_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + { + register unsigned char *p2 = p; + /* Compare what follows with the beginning of the repeat. + If we can establish that there is nothing that they would + both match, we can change to finalize_jump. */ + while (p2 + 1 != pend + && (*p2 == (unsigned char) stop_memory + || *p2 == (unsigned char) start_memory)) + p2 += 2; /* Skip over reg number. */ + if (p2 == pend) + p[-3] = (unsigned char) finalize_jump; + else if (*p2 == (unsigned char) exactn + || *p2 == (unsigned char) endline) + { + register int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; + register unsigned char *p1 = p + mcnt; + /* p1[0] ... p1[2] are an on_failure_jump. + Examine what follows that. */ + if (p1[3] == (unsigned char) exactn && p1[5] != c) + p[-3] = (unsigned char) finalize_jump; + else if (p1[3] == (unsigned char) charset + || p1[3] == (unsigned char) charset_not) + { + int not = p1[3] == (unsigned char) charset_not; + if (c < p1[4] * BYTEWIDTH + && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + /* `not' is 1 if c would match. */ + /* That means it is not safe to finalize. */ + if (!not) + p[-3] = (unsigned char) finalize_jump; + } + } + } + p -= 2; /* Point at relative address again. */ + if (p[-1] != (unsigned char) finalize_jump) + { + p[-1] = (unsigned char) jump; + goto nofinalize; + } + /* Note fall through. */ + + /* The end of a stupid repeat has a finalize_jump back to the + start, where another failure point will be made which will + point to after all the repetitions found so far. */ + + /* Take off failure points put on by matching on_failure_jump + because didn't fail. Also remove the register information + put on by the on_failure_jump. */ + case finalize_jump: + POP_FAILURE_POINT (); + /* Note fall through. */ + + /* Jump without taking off any failure points. */ + case jump: + nofinalize: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p += mcnt; + break; + + case dummy_failure_jump: + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at finalize_jump. We will end up at + finalize_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 finalize_jump to pop. */ + PUSH_FAILURE_POINT (0, 0); + goto nofinalize; + + + /* Have to succeed matching what follows at least n times. Then + just handle like an on_failure_jump. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); + } + else if (mcnt == 0) + { + p[2] = unused; + p[3] = unused; + goto on_failure; + } + else + { + fprintf (stderr, "regex: the succeed_n's n is not set.\n"); + exit (1); + } + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER(p + 2, mcnt); + goto nofinalize; /* Do the jump without taking off + any failure points. */ + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + register unsigned char *p1; + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); + STORE_NUMBER (p1, mcnt); + break; + } + + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case unused: + break; + + case wordbound: + if (AT_WORD_BOUNDARY) + break; + goto fail; + + case notwordbound: + if (AT_WORD_BOUNDARY) + goto fail; + break; + + case wordbeg: + /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ + if (IS_A_LETTER (d) && (AT_STRINGS_BEG || !IS_A_LETTER (d - 1))) + break; + goto fail; + + case wordend: + /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ + if (!AT_STRINGS_BEG && IS_A_LETTER (d - 1) + && (!IS_A_LETTER (d) || AT_STRINGS_END)) + break; + goto fail; + +#ifdef emacs + case before_dot: + if (PTR_CHAR_POS (d) >= point) + goto fail; + break; + + case at_dot: + if (PTR_CHAR_POS (d) != point) + goto fail; + break; + + case after_dot: + if (PTR_CHAR_POS (d) <= point) + goto fail; + break; + + case wordchar: + mcnt = (int) Sword; + goto matchsyntax; + + case syntaxspec: + mcnt = *p++; + matchsyntax: + PREFETCH; + if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail; + SET_REGS_MATCHED; + break; + + case notwordchar: + mcnt = (int) Sword; + goto matchnotsyntax; + + case notsyntaxspec: + mcnt = *p++; + matchnotsyntax: + PREFETCH; + if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail; + SET_REGS_MATCHED; + break; + +#else /* not emacs */ + + case wordchar: + PREFETCH; + if (!IS_A_LETTER (d)) + goto fail; + SET_REGS_MATCHED; + break; + + case notwordchar: + PREFETCH; + if (IS_A_LETTER (d)) + goto fail; + SET_REGS_MATCHED; + break; + +#endif /* not emacs */ + + case begbuf: + if (AT_STRINGS_BEG) + break; + goto fail; + + case endbuf: + if (AT_STRINGS_END) + break; + goto fail; + + case exactn: + /* Match the next few pattern characters exactly. + mcnt is how many characters to match. */ + mcnt = *p++; + /* 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[*d++] != *p++) goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH; + if (*d++ != *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED; + break; + } + continue; /* Successfully executed one pattern command; keep going. */ + + /* Jump here if any matching operation fails. */ + fail: + if (stackp != stackb) + /* A restart point is known. Restart there and pop it. */ + { + short last_used_reg, this_reg; + + /* If this failure point is from a dummy_failure_point, just + skip it. */ + if (!stackp[-2]) + { + POP_FAILURE_POINT (); + goto fail; + } + + d = *--stackp; + p = *--stackp; + if (d >= string1 && d <= end1) + dend = end_match_1; + /* Restore register info. */ + last_used_reg = (short) *--stackp; + + /* Make the ones that weren't saved -1 or 0 again. */ + for (this_reg = RE_NREGS - 1; this_reg > last_used_reg; this_reg--) + { + regend[this_reg] = (unsigned char *) -1; + regstart[this_reg] = (unsigned char *) -1; + IS_ACTIVE (reg_info[this_reg]) = 0; + MATCHED_SOMETHING (reg_info[this_reg]) = 0; + } + + /* And restore the rest from the stack. */ + for ( ; this_reg > 0; this_reg--) + { + reg_info[this_reg] = *(struct register_info *) *--stackp; + regend[this_reg] = *--stackp; + regstart[this_reg] = *--stackp; + } + } + else + break; /* Matching at this starting point really fails. */ + } + + if (best_regs_set) + goto restore_best_regs; + return -1; /* Failure to match. */ +} + + +static int +bcmp_translate (s1, s2, len, translate) + unsigned char *s1, *s2; + register int len; + unsigned char *translate; +{ + register unsigned char *p1 = s1, *p2 = s2; + while (len) + { + if (translate [*p1++] != translate [*p2++]) return 1; + len--; + } + return 0; +} + + + +/* Entry points compatible with 4.2 BSD regex library. */ + +#ifndef emacs + +static struct re_pattern_buffer re_comp_buf; + +char * +re_comp (s) + char *s; +{ + if (!s) + { + if (!re_comp_buf.buffer) + return "No previous regular expression"; + return 0; + } + + if (!re_comp_buf.buffer) + { + if (!(re_comp_buf.buffer = (char *) malloc (200))) + return "Memory exhausted"; + re_comp_buf.allocated = 200; + if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH))) + return "Memory exhausted"; + } + return re_compile_pattern (s, strlen (s), &re_comp_buf); +} + +int +re_exec (s) + char *s; +{ + int len = strlen (s); + return 0 <= re_search (&re_comp_buf, s, len, 0, len, + (struct re_registers *) 0); +} +#endif /* not emacs */ + + + +#ifdef test + +#include + +/* Indexed by a character, gives the upper case equivalent of the + character. */ + +char upcase[0400] = + { 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 012, 013, 014, 015, 016, 017, + 020, 021, 022, 023, 024, 025, 026, 027, + 030, 031, 032, 033, 034, 035, 036, 037, + 040, 041, 042, 043, 044, 045, 046, 047, + 050, 051, 052, 053, 054, 055, 056, 057, + 060, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 072, 073, 074, 075, 076, 077, + 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, + 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 + }; + +#ifdef canned + +#include "tests.h" + +typedef enum { extended_test, basic_test } test_type; + +/* Use this to run the tests we've thought of. */ + +void +main () +{ + test_type t = extended_test; + + if (t == basic_test) + { + printf ("Running basic tests:\n\n"); + test_posix_basic (); + } + else if (t == extended_test) + { + printf ("Running extended tests:\n\n"); + test_posix_extended (); + } +} + +#else /* not canned */ + +/* Use this to run interactive tests. */ + +void +main (argc, argv) + int argc; + char **argv; +{ + char pat[80]; + struct re_pattern_buffer buf; + int i; + char c; + char fastmap[(1 << BYTEWIDTH)]; + + /* Allow a command argument to specify the style of syntax. */ + if (argc > 1) + obscure_syntax = atoi (argv[1]); + + buf.allocated = 40; + buf.buffer = (char *) malloc (buf.allocated); + buf.fastmap = fastmap; + buf.translate = upcase; + + while (1) + { + gets (pat); + + if (*pat) + { + re_compile_pattern (pat, strlen(pat), &buf); + + for (i = 0; i < buf.used; i++) + printchar (buf.buffer[i]); + + putchar ('\n'); + + printf ("%d allocated, %d used.\n", buf.allocated, buf.used); + + re_compile_fastmap (&buf); + printf ("Allowed by fastmap: "); + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (fastmap[i]) printchar (i); + putchar ('\n'); + } + + gets (pat); /* Now read the string to match against */ + + i = re_match (&buf, pat, strlen (pat), 0, 0); + printf ("Match value %d.\n", i); + } +} + +#endif + + +#ifdef NOTDEF +print_buf (bufp) + struct re_pattern_buffer *bufp; +{ + int i; + + printf ("buf is :\n----------------\n"); + for (i = 0; i < bufp->used; i++) + printchar (bufp->buffer[i]); + + printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used); + + printf ("Allowed by fastmap: "); + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (bufp->fastmap[i]) + printchar (i); + printf ("\nAllowed by translate: "); + if (bufp->translate) + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (bufp->translate[i]) + printchar (i); + printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't"); + printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not"); +} +#endif /* NOTDEF */ + +printchar (c) + char c; +{ + if (c < 040 || c >= 0177) + { + putchar ('\\'); + putchar (((c >> 6) & 3) + '0'); + putchar (((c >> 3) & 7) + '0'); + putchar ((c & 7) + '0'); + } + else + putchar (c); +} + +error (string) + char *string; +{ + puts (string); + exit (1); +} +#endif /* test */ diff --git a/usr/src/usr.bin/sed/regex.h b/usr/src/usr.bin/sed/regex.h new file mode 100644 index 0000000000..b88abf9c35 --- /dev/null +++ b/usr/src/usr.bin/sed/regex.h @@ -0,0 +1,253 @@ +/* Definitions for data structures callers pass the regex library. + + Copyright (C) 1985, 1989-90 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 +#define __REGEXP_LIBRARY + +/* Define number of parens for which we record the beginnings and ends. + This affects how much space the `struct re_registers' type takes up. */ +#ifndef RE_NREGS +#define RE_NREGS 10 +#endif + +#define BYTEWIDTH 8 + + +/* Maximum number of duplicates an interval can allow. */ +#define RE_DUP_MAX ((1 << 15) - 1) + + +/* This defines the various regexp syntaxes. */ +extern int obscure_syntax; + + +/* The following bits are used in the obscure_syntax variable to choose among + alternative regexp syntaxes. */ + +/* If this bit is set, plain parentheses serve as grouping, and backslash + parentheses are needed for literal searching. + If not set, backslash-parentheses are grouping, and plain parentheses + are for literal searching. */ +#define RE_NO_BK_PARENS 1 + +/* If this bit is set, plain | serves as the `or'-operator, and \| is a + literal. + If not set, \| serves as the `or'-operator, and | is a literal. */ +#define RE_NO_BK_VBAR (1 << 1) + +/* If this bit is not set, plain + or ? serves as an operator, and \+, \? are + literals. + If set, \+, \? are operators and plain +, ? are literals. */ +#define RE_BK_PLUS_QM (1 << 2) + +/* If this bit is set, | binds tighter than ^ or $. + If not set, the contrary. */ +#define RE_TIGHT_VBAR (1 << 3) + +/* If this bit is set, then treat newline as an OR operator. + If not set, treat it as a normal character. */ +#define RE_NEWLINE_OR (1 << 4) + +/* If this bit is set, then special characters may act as normal + characters in some contexts. Specifically, this applies to: + ^ -- only special at the beginning, or after ( or |; + $ -- only special at the end, or before ) or |; + *, +, ? -- only special when not after the beginning, (, or |. + If this bit is not set, special characters (such as *, ^, and $) + always have their special meaning regardless of the surrounding + context. */ +#define RE_CONTEXT_INDEP_OPS (1 << 5) + +/* If this bit is not set, then \ before anything inside [ and ] is taken as + a real \. + If set, then such a \ escapes the following character. This is a + special case for awk. */ +#define RE_AWK_CLASS_HACK (1 << 6) + +/* If this bit is set, then \{ and \} or { and } serve as interval operators. + If not set, then \{ and \} and { and } are treated as literals. */ +#define RE_INTERVALS (1 << 7) + +/* If this bit is not set, then \{ and \} serve as interval operators and + { and } are literals. + If set, then { and } serve as interval operators and \{ and \} are + literals. */ +#define RE_NO_BK_CURLY_BRACES (1 << 8) + +/* 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 (1 << 9) + +/* If this bit is set, then the dot re doesn't match a null byte. + If not set, it does. */ +#define RE_DOT_NOT_NULL (1 << 10) + +/* If this bit is set, then [^...] doesn't match a newline. + If not set, it does. */ +#define RE_HAT_NOT_NEWLINE (1 << 11) + +/* If this bit is set, back references are recognized. + If not set, they aren't. */ +#define RE_NO_BK_REFS (1 << 12) + +/* If this bit is set, back references must refer to a preceding + subexpression. If not set, a back reference to a nonexistent + subexpression is treated as literal characters. */ +#define RE_NO_EMPTY_BK_REF (1 << 13) + +/* If this bit is set, bracket expressions can't be empty. + If it is set, they can be empty. */ +#define RE_NO_EMPTY_BRACKETS (1 << 14) + +/* If this bit is set, then *, +, ? and { cannot be first in an re or + immediately after a |, or a (. Furthermore, a | cannot be first or + last in an re, or immediately follow another | or a (. Also, a ^ + cannot appear in a nonleading position and a $ cannot appear in a + nontrailing position (outside of bracket expressions, that is). */ +#define RE_CONTEXTUAL_INVALID_OPS (1 << 15) + +/* If this bit is set, then +, ? and | aren't recognized as operators. + If it's not, they are. */ +#define RE_LIMITED_OPS (1 << 16) + +/* If this bit is set, then an ending range point has to collate higher + or equal to the starting range point. + If it's not set, then when the ending range point collates higher + than the starting range point, the range is just considered empty. */ +#define RE_NO_EMPTY_RANGES (1 << 17) + +/* If this bit is set, then a hyphen (-) can't be an ending range point. + If it isn't, then it can. */ +#define RE_NO_HYPHEN_RANGE_END (1 << 18) + + +/* Define combinations of bits for the standard possibilities. */ +#define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS) +#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS | RE_AWK_CLASS_HACK) +#define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR) +#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) +#define RE_SYNTAX_EMACS 0 +#define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \ + | RE_CHAR_CLASSES | RE_DOT_NOT_NULL \ + | RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \ + | RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \ + | RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END) + +#define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \ + | RE_NO_BK_VBAR | RE_NO_BK_PARENS \ + | RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \ + | RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \ + | RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \ + | RE_NO_HYPHEN_RANGE_END) + + +/* This data structure is used to represent a compiled pattern. */ + +struct re_pattern_buffer + { + char *buffer; /* Space holding the compiled pattern commands. */ + long allocated; /* Size of space that `buffer' points to. */ + long used; /* Length of portion of buffer actually occupied */ + char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ + /* re_search uses the fastmap, if there is one, + to skip over totally implausible characters. */ + char *translate; /* Translate table to apply to all characters before + comparing, or zero for no translation. + The translation is applied to a pattern when it is + compiled and to data when it is matched. */ + char fastmap_accurate; + /* Set to zero when a new pattern is stored, + set to one when the fastmap is updated from it. */ + char can_be_null; /* Set to one by compiling fastmap + if this pattern might match the null string. + It does not necessarily match the null string + in that case, but if this is zero, it cannot. + 2 as value means can match null string + but at end of range or before a character + listed in the fastmap. */ + }; + + +/* search.c (search_buffer) needs this one value. It is defined both in + regex.c and here. */ +#define RE_EXACTN_VALUE 1 + + +/* Structure to store register contents data in. + + Pass the address of such a structure as an argument to re_match, etc., + if you want this information back. + + For i from 1 to RE_NREGS - 1, start[i] records the starting index in + the string of where the ith subexpression matched, and end[i] records + one after the ending index. start[0] and end[0] are analogous, for + the entire pattern. */ + +struct re_registers + { + int start[RE_NREGS]; + int end[RE_NREGS]; + }; + + + +#ifdef __STDC__ + +extern char *re_compile_pattern (char *, int, struct re_pattern_buffer *); +/* Is this really advertised? */ +extern void re_compile_fastmap (struct re_pattern_buffer *); +extern int re_search (struct re_pattern_buffer *, char*, int, int, int, + struct re_registers *); +extern int re_search_2 (struct re_pattern_buffer *, char *, int, + char *, int, int, int, + struct re_registers *, int); +extern int re_match (struct re_pattern_buffer *, char *, int, int, + struct re_registers *); +extern int re_match_2 (struct re_pattern_buffer *, char *, int, + char *, int, int, struct re_registers *, int); + +/* 4.2 bsd compatibility. */ +extern char *re_comp (char *); +extern int re_exec (char *); + +#else /* !__STDC__ */ + +extern char *re_compile_pattern (); +/* Is this really advertised? */ +extern void re_compile_fastmap (); +extern int re_search (), re_search_2 (); +extern int re_match (), re_match_2 (); + +/* 4.2 bsd compatibility. */ +extern char *re_comp (); +extern int re_exec (); + +#endif /* __STDC__ */ + + +#ifdef SYNTAX_TABLE +extern char *re_syntax_table; +#endif + +#endif /* !__REGEXP_LIBRARY */ diff --git a/usr/src/usr.bin/sed/sed.c b/usr/src/usr.bin/sed/sed.c new file mode 100644 index 0000000000..e1cf70f326 --- /dev/null +++ b/usr/src/usr.bin/sed/sed.c @@ -0,0 +1,1554 @@ +/* GNU SED, a batch stream editor. + Copyright (C) 1989-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. */ + +#ifdef __STDC__ +#define VOID void +#else +#define VOID char +#endif + +#define _GNU_SOURCE +#include +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif +#include +#include +#include +#if defined(STDC_HEADERS) +#include +#endif +#if defined(USG) || defined(STDC_HEADERS) +#include +#include +#define bcopy(s, d, n) (memcpy((d), (s), (n))) +#else +#include +VOID *memchr(); +#endif + +char *version_string = "GNU sed version 1.08"; + +/* Struct vector is used to describe a chunk of a sed program. There is one + vector for the main program, and one for each { } pair. */ +struct vector { + struct sed_cmd *v; + int v_length; + int v_allocated; + struct vector *up_one; + struct vector *next_one; +}; + + +/* Goto structure is used to hold both GOTO's and labels. There are two + separate lists, one of goto's, called 'jumps', and one of labels, called + 'labels'. + the V element points to the descriptor for the program-chunk in which the + goto was encountered. + the v_index element counts which element of the vector actually IS the + goto/label. The first element of the vector is zero. + the NAME element is the null-terminated name of the label. + next is the next goto/label in the list. */ + +struct sed_label { + struct vector *v; + int v_index; + char *name; + struct sed_label *next; +}; + +/* ADDR_TYPE is zero for a null address, + one if addr_number is valid, or + two if addr_regex is valid, + three, if the address is '$' + + Other values are undefined. + */ + +#define ADDR_NULL 0 +#define ADDR_NUM 1 +#define ADDR_REGEX 2 +#define ADDR_LAST 3 + +struct addr { + int addr_type; + struct re_pattern_buffer *addr_regex; + int addr_number; +}; + + +/* Aflags: If the low order bit is set, a1 has been + matched; apply this command until a2 matches. + If the next bit is set, apply this command to all + lines that DON'T match the address(es). + */ + +#define A1_MATCHED_BIT 01 +#define ADDR_BANG_BIT 02 + + +struct sed_cmd { + struct addr a1,a2; + int aflags; + + char cmd; + + union { + /* This structure is used for a, i, and c commands */ + struct { + char *text; + int text_len; + } cmd_txt; + + /* This is used for b and t commands */ + struct sed_cmd *label; + + /* This for r and w commands */ + FILE *io_file; + + /* This for the hairy s command */ + /* For the flags var: + low order bit means the 'g' option was given, + next bit means the 'p' option was given, + and the next bit means a 'w' option was given, + and wio_file contains the file to write to. */ + +#define S_GLOBAL_BIT 01 +#define S_PRINT_BIT 02 +#define S_WRITE_BIT 04 +#define S_NUM_BIT 010 + + struct { + struct re_pattern_buffer *regx; + char *replacement; + int replace_length; + int flags; + int numb; + FILE *wio_file; + } cmd_regex; + + /* This for the y command */ + unsigned char *translate; + + /* For { and } */ + struct vector *sub; + struct sed_label *jump; + } x; +}; + +/* Sed operates a line at a time. */ +struct line { + char *text; /* Pointer to line allocated by malloc. */ + int length; /* Length of text. */ + int alloc; /* Allocated space for text. */ +}; + +/* This structure holds information about files opend by the 'r', 'w', + and 's///w' commands. In paticular, it holds the FILE pointer to + use, the file's name, a flag that is non-zero if the file is being + read instead of written. */ + +#define NUM_FPS 32 +struct { + FILE *phile; + char *name; + int readit; +} file_ptrs[NUM_FPS]; + + +#if defined(__STDC__) +# define P_(s) s +#else +# define P_(s) () +#endif + +void panic P_((char *str, ...)); +char *__fp_name P_((FILE *fp)); +FILE *ck_fopen P_((char *name, char *mode)); +void ck_fwrite P_((char *ptr, int size, int nmemb, FILE *stream)); +void ck_fclose P_((FILE *stream)); +VOID *ck_malloc P_((int size)); +VOID *ck_realloc P_((VOID *ptr, int size)); +char *ck_strdup P_((char *str)); +VOID *init_buffer P_((void)); +void flush_buffer P_((VOID *bb)); +int size_buffer P_((VOID *b)); +void add_buffer P_((VOID *bb, char *p, int n)); +void add1_buffer P_((VOID *bb, int ch)); +char *get_buffer P_((VOID *bb)); + +void compile_string P_((char *str)); +void compile_file P_((char *str)); +struct vector *compile_program P_((struct vector *vector)); +void bad_prog P_((char *why)); +int inchar P_((void)); +void savchar P_((int ch)); +int compile_address P_((struct addr *addr)); +void compile_regex P_((int slash)); +struct sed_label *setup_jump P_((struct sed_label *list, struct sed_cmd *cmd, struct vector *vec)); +FILE *compile_filename P_((int readit)); +void read_file P_((char *name)); +void execute_program P_((struct vector *vec)); +int match_address P_((struct addr *addr)); +int read_pattern_space P_((void)); +void append_pattern_space P_((void)); +void line_copy P_((struct line *from, struct line *to)); +void line_append P_((struct line *from, struct line *to)); +void str_append P_((struct line *to, char *string, int length)); +void usage P_((void)); + +extern char *myname; + +/* If set, don't write out the line unless explictly told to */ +int no_default_output = 0; + +/* Current input line # */ +int input_line_number = 0; + +/* Are we on the last input file? */ +int last_input_file = 0; + +/* Have we hit EOF on the last input file? This is used to decide if we + have hit the '$' address yet. */ +int input_EOF = 0; + +/* non-zero if a quit command has been executed. */ +int quit_cmd = 0; + +/* Have we done any replacements lately? This is used by the 't' command. */ +int replaced = 0; + +/* How many '{'s are we executing at the moment */ +int program_depth = 0; + +/* The complete compiled SED program that we are going to run */ +struct vector *the_program = 0; + +/* information about labels and jumps-to-labels. This is used to do + the required backpatching after we have compiled all the scripts. */ +struct sed_label *jumps = 0; +struct sed_label *labels = 0; + +/* The 'current' input line. */ +struct line line; + +/* An input line that's been stored by later use by the program */ +struct line hold; + +/* A 'line' to append to the current line when it comes time to write it out */ +struct line append; + + +/* When we're reading a script command from a string, 'prog_start' and + 'prog_end' point to the beginning and end of the string. This + would allow us to compile script strings that contain nulls, except + that script strings are only read from the command line, which is + null-terminated */ +char *prog_start; +char *prog_end; + +/* When we're reading a script command from a string, 'prog_cur' points + to the current character in the string */ +char *prog_cur; + +/* This is the name of the current script file. + It is used for error messages. */ +char *prog_name; + +/* This is the current script file. If it is zero, we are reading + from a string stored in 'prog_start' instead. If both 'prog_file' + and 'prog_start' are zero, we're in trouble! */ +FILE *prog_file; + +/* this is the number of the current script line that we're compiling. It is + used to give out useful and informative error messages. */ +int prog_line = 1; + +/* This is the file pointer that we're currently reading data from. It may + be stdin */ +FILE *input_file; + +/* If this variable is non-zero at exit, one or more of the input + files couldn't be opened. */ + +int bad_input = 0; + +/* 'an empty regular expression is equivalent to the last regular + expression read' so we have to keep track of the last regex used. + Here's where we store a pointer to it (it is only malloc()'d once) */ +struct re_pattern_buffer *last_regex; + +/* Various error messages we may want to print */ +static char ONE_ADDR[] = "Command only uses one address"; +static char NO_ADDR[] = "Command doesn't take any addresses"; +static char LINE_JUNK[] = "Extra characters after command"; +static char BAD_EOF[] = "Unexpected End-of-file"; +static char NO_REGEX[] = "No previous regular expression"; + +static struct option longopts[] = +{ + {"expression", 1, NULL, 'e'}, + {"file", 1, NULL, 'f'}, + {"quiet", 0, NULL, 'n'}, + {"silent", 0, NULL, 'n'}, + {"version", 0, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +/* Yes, the main program, which parses arguments, and does the right + thing with them; it also inits the temporary storage, etc. */ +void +main(argc,argv) +int argc; +char **argv; +{ + int opt; + char *e_strings = NULL; + int compiled = 0; + struct sed_label *go,*lbl; + + myname=argv[0]; + while((opt=getopt_long(argc,argv,"ne:f:V", longopts, (int *) 0)) + !=EOF) { + switch(opt) { + case 'n': + no_default_output = 1; + break; + case 'e': + if(e_strings == NULL) { + e_strings=ck_malloc(strlen(optarg)+2); + strcpy(e_strings,optarg); + } else { + e_strings=ck_realloc(e_strings,strlen(e_strings)+strlen(optarg)+2); + strcat(e_strings,optarg); + } + strcat(e_strings,"\n"); + compiled = 1; + break; + case 'f': + compile_file(optarg); + compiled = 1; + break; + case 'V': + fprintf(stderr, "%s\n", version_string); + break; + default: + usage(); + } + } + if(e_strings) { + compile_string(e_strings); + free(e_strings); + } + if(!compiled) { + if (optind == argc) + usage(); + compile_string(argv[optind++]); + } + + for(go=jumps;go;go=go->next) { + for(lbl=labels;lbl;lbl=lbl->next) + if(!strcmp(lbl->name,go->name)) + break; + if(*go->name && !lbl) + panic("Can't find label for jump to '%s'",go->name); + go->v->v[go->v_index].x.jump=lbl; + } + + line.length=0; + line.alloc=50; + line.text=ck_malloc(50); + + append.length=0; + append.alloc=50; + append.text=ck_malloc(50); + + hold.length=0; + hold.alloc=50; + hold.text=ck_malloc(50); + + if(argc<=optind) { + last_input_file++; + read_file("-"); + } else while(optindv=(struct sed_cmd *)ck_malloc(MORE_CMDS*sizeof(struct sed_cmd)); + vector->v_allocated=MORE_CMDS; + vector->v_length=0; + vector->up_one = 0; + vector->next_one = 0; + } + for(;;) { + skip_comment: + do ch=inchar(); + while(ch!=EOF && (isblank(ch) || ch=='\n' || ch==';')); + if(ch==EOF) + break; + savchar(ch); + + if(vector->v_length==vector->v_allocated) { + vector->v=(struct sed_cmd *)ck_realloc((VOID *)vector->v,(vector->v_length+MORE_CMDS)*sizeof(struct sed_cmd)); + vector->v_allocated+=MORE_CMDS; + } + cur_cmd=vector->v+vector->v_length; + vector->v_length++; + + cur_cmd->a1.addr_type=0; + cur_cmd->a2.addr_type=0; + cur_cmd->aflags=0; + cur_cmd->cmd=0; + + if(compile_address(&(cur_cmd->a1))) { + ch=inchar(); + if(ch==',') { + do ch=inchar(); + while(ch!=EOF && isblank(ch)); + savchar(ch); + if(compile_address(&(cur_cmd->a2))) + ; + else + bad_prog("Unexpected ','"); + } else + savchar(ch); + } + ch=inchar(); + if(ch==EOF) + break; + new_cmd: + switch(ch) { + case '#': + if(cur_cmd->a1.addr_type!=0) + bad_prog(NO_ADDR); + do ch=inchar(); + while(ch!=EOF && ch!='\n'); + vector->v_length--; + goto skip_comment; + case '!': + if(cur_cmd->aflags & ADDR_BANG_BIT) + bad_prog("Multiple '!'s"); + cur_cmd->aflags|= ADDR_BANG_BIT; + do ch=inchar(); + while(ch!=EOF && isblank(ch)); + if(ch==EOF) + bad_prog(BAD_EOF); +#if 0 + savchar(ch); +#endif + goto new_cmd; + case 'a': + case 'i': + if(cur_cmd->a2.addr_type!=0) + bad_prog(ONE_ADDR); + /* Fall Through */ + case 'c': + cur_cmd->cmd=ch; + if(inchar()!='\\' || inchar()!='\n') + bad_prog(LINE_JUNK); + b=init_buffer(); + while((ch=inchar())!=EOF && ch!='\n') { + if(ch=='\\') + ch=inchar(); + add1_buffer(b,ch); + } + if(ch!=EOF) + add1_buffer(b,ch); + num=size_buffer(b); + string=(unsigned char *)ck_malloc(num); + bcopy(get_buffer(b),string,num); + flush_buffer(b); + cur_cmd->x.cmd_txt.text_len=num; + cur_cmd->x.cmd_txt.text=(char *)string; + break; + case '{': + cur_cmd->cmd=ch; + program_depth++; +#if 0 + while((ch=inchar())!=EOF && ch!='\n') + if(!isblank(ch)) + bad_prog(LINE_JUNK); +#endif + cur_cmd->x.sub=compile_program((struct vector *)0); + /* FOO JF is this the right thing to do? */ + break; + case '}': + if(!program_depth) + bad_prog("Unexpected '}'"); + --(vector->v_length); + while((ch=inchar())!=EOF && ch!='\n' && ch!=';') + if(!isblank(ch)) + bad_prog(LINE_JUNK); + return vector; + case ':': + cur_cmd->cmd=ch; + if(cur_cmd->a1.addr_type!=0) + bad_prog(": doesn't want any addresses"); + labels=setup_jump(labels,cur_cmd,vector); + break; + case 'b': + case 't': + cur_cmd->cmd=ch; + jumps=setup_jump(jumps,cur_cmd,vector); + break; + case 'q': + case '=': + if(cur_cmd->a2.addr_type) + bad_prog(ONE_ADDR); + /* Fall Through */ + case 'd': + case 'D': + case 'g': + case 'G': + case 'h': + case 'H': + case 'l': + case 'n': + case 'N': + case 'p': + case 'P': + case 'x': + cur_cmd->cmd=ch; + do ch=inchar(); + while(ch!=EOF && isblank(ch) && ch!='\n' && ch!=';'); + if(ch!='\n' && ch!=';' && ch!=EOF) + bad_prog(LINE_JUNK); + break; + + case 'r': + if(cur_cmd->a2.addr_type!=0) + bad_prog(ONE_ADDR); + /* FALL THROUGH */ + case 'w': + cur_cmd->cmd=ch; + cur_cmd->x.io_file=compile_filename(ch=='r'); + break; + + case 's': + cur_cmd->cmd=ch; + slash=inchar(); + compile_regex(slash); + + cur_cmd->x.cmd_regex.regx=last_regex; + + b=init_buffer(); + while((ch=inchar())!=EOF && ch!=slash) { + if(ch=='\\') { + int ci; + + ci=inchar(); + if(ci!=EOF) { + if(ci!='\n') + add1_buffer(b,ch); + add1_buffer(b,ci); + } + } else + add1_buffer(b,ch); + } + cur_cmd->x.cmd_regex.replace_length=size_buffer(b); + cur_cmd->x.cmd_regex.replacement=ck_malloc(cur_cmd->x.cmd_regex.replace_length); + bcopy(get_buffer(b),cur_cmd->x.cmd_regex.replacement,cur_cmd->x.cmd_regex.replace_length); + flush_buffer(b); + + cur_cmd->x.cmd_regex.flags=0; + cur_cmd->x.cmd_regex.numb=0; + + if(ch==EOF) + break; + do { + ch=inchar(); + switch(ch) { + case 'p': + if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT) + bad_prog("multiple 'p' options to 's' command"); + cur_cmd->x.cmd_regex.flags|=S_PRINT_BIT; + break; + case 'g': + if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) + cur_cmd->x.cmd_regex.flags&= ~S_NUM_BIT; + if(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT) + bad_prog("multiple 'g' options to 's' command"); + cur_cmd->x.cmd_regex.flags|=S_GLOBAL_BIT; + break; + case 'w': + cur_cmd->x.cmd_regex.flags|=S_WRITE_BIT; + cur_cmd->x.cmd_regex.wio_file=compile_filename(0); + ch='\n'; + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) + bad_prog("multiple number options to 's' command"); + if((cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT)==0) + cur_cmd->x.cmd_regex.flags|=S_NUM_BIT; + num = 0; + while(isdigit(ch)) { + num=num*10+ch-'0'; + ch=inchar(); + } + savchar(ch); + cur_cmd->x.cmd_regex.numb=num; + break; + case '\n': + case ';': + case EOF: + break; + default: + bad_prog("Unknown option to 's'"); + break; + } + } while(ch!=EOF && ch!='\n' && ch!=';'); + if(ch==EOF) + break; + break; + + case 'y': + cur_cmd->cmd=ch; + string=(unsigned char *)ck_malloc(256); + for(num=0;num<256;num++) + string[num]=num; + b=init_buffer(); + slash=inchar(); + while((ch=inchar())!=EOF && ch!=slash) + add1_buffer(b,ch); + cur_cmd->x.translate=string; + string=(unsigned char *)get_buffer(b); + for(num=size_buffer(b);num;--num) { + ch=inchar(); + if(ch==EOF) + bad_prog(BAD_EOF); + if(ch==slash) + bad_prog("strings for y command are different lengths"); + cur_cmd->x.translate[*string++]=ch; + } + flush_buffer(b); + if(inchar()!=slash || ((ch=inchar())!=EOF && ch!='\n' && ch!=';')) + bad_prog(LINE_JUNK); + break; + + default: + bad_prog("Unknown command"); + } + } + return vector; +} + +/* Complain about a programming error and exit. */ +void +bad_prog(why) +char *why; +{ + if(prog_line) + fprintf(stderr,"%s: file %s line %d: %s\n",myname,prog_name,prog_line,why); + else + fprintf(stderr,"%s: %s\n",myname,why); + exit(1); +} + +/* Read the next character from the program. Return EOF if there isn't + anything to read. Keep prog_line up to date, so error messages can + be meaningful. */ +int +inchar() +{ + int ch; + if(prog_file) { + if(feof(prog_file)) + return EOF; + else + ch=getc(prog_file); + } else { + if(!prog_cur) + return EOF; + else if(prog_cur==prog_end) { + ch=EOF; + prog_cur=0; + } else + ch= *prog_cur++; + } + if(ch=='\n' && prog_line) + prog_line++; + return ch; +} + +/* unget 'ch' so the next call to inchar will return it. 'ch' must not be + EOF or anything nasty like that. */ +void +savchar(ch) +int ch; +{ + if(ch==EOF) + return; + if(ch=='\n' && prog_line>1) + --prog_line; + if(prog_file) + ungetc(ch,prog_file); + else + *--prog_cur=ch; +} + + +/* Try to read an address for a sed command. If it succeeeds, + return non-zero and store the resulting address in *'addr'. + If the input doesn't look like an address read nothing + and return zero. */ +int +compile_address(addr) +struct addr *addr; +{ + int ch; + int num; + + ch=inchar(); + + if(isdigit(ch)) { + num=ch-'0'; + while((ch=inchar())!=EOF && isdigit(ch)) + num=num*10+ch-'0'; + while(ch!=EOF && isblank(ch)) + ch=inchar(); + savchar(ch); + addr->addr_type=ADDR_NUM; + addr->addr_number = num; + return 1; + } else if(ch=='/') { + addr->addr_type=ADDR_REGEX; + compile_regex('/'); + addr->addr_regex=last_regex; + do ch=inchar(); + while(ch!=EOF && isblank(ch)); + savchar(ch); + return 1; + } else if(ch=='$') { + addr->addr_type=ADDR_LAST; + do ch=inchar(); + while(ch!=EOF && isblank(ch)); + savchar(ch); + return 1; + } else + savchar(ch); + return 0; +} + +void +compile_regex (slash) + int slash; +{ + VOID *b; + int ch; + int in_char_class = 0; + + b=init_buffer(); + while((ch=inchar())!=EOF && (ch!=slash || in_char_class)) { + if(ch=='^') { + if(size_buffer(b)==0) { + add1_buffer(b,'\\'); + add1_buffer(b,'`'); + } else + add1_buffer(b,ch); + continue; + } else if(ch=='$') { + ch=inchar(); + savchar(ch); + if(ch==slash) { + add1_buffer(b,'\\'); + add1_buffer(b,'\''); + } else + add1_buffer(b,'$'); + continue; + } else if(ch == '[') { + add1_buffer(b,ch); + in_char_class = 1; + continue; + } else if(ch == ']') { + add1_buffer(b,ch); + in_char_class = 0; + continue; + } else if(ch!='\\') { + add1_buffer(b,ch); + continue; + } + ch=inchar(); + switch(ch) { + case 'n': + add1_buffer(b,'\n'); + break; +#if 0 + case 'b': + add1_buffer(b,'\b'); + break; + case 'f': + add1_buffer(b,'\f'); + break; + case 'r': + add1_buffer(b,'\r'); + break; + case 't': + add1_buffer(b,'\t'); + break; +#endif /* 0 */ + case EOF: + break; + default: + add1_buffer(b,'\\'); + add1_buffer(b,ch); + break; + } + } + if(ch==EOF) + bad_prog(BAD_EOF); + if(size_buffer(b)) { + last_regex=(struct re_pattern_buffer *)ck_malloc(sizeof(struct re_pattern_buffer)); + last_regex->allocated=size_buffer(b)+10; + last_regex->buffer=ck_malloc(last_regex->allocated); + last_regex->fastmap=ck_malloc(256); + last_regex->translate=0; + re_compile_pattern(get_buffer(b),size_buffer(b),last_regex); + } else if(!last_regex) + bad_prog(NO_REGEX); + flush_buffer(b); +} + +/* Store a label (or label reference) created by a ':', 'b', or 't' + comand so that the jump to/from the lable can be backpatched after + compilation is complete */ +struct sed_label * +setup_jump(list,cmd,vec) +struct sed_label *list; +struct sed_cmd *cmd; +struct vector *vec; +{ + struct sed_label *tmp; + VOID *b; + int ch; + + b=init_buffer(); + while((ch=inchar()) != EOF && isblank(ch)) + ; + while(ch!=EOF && ch!='\n') { + add1_buffer(b,ch); + ch=inchar(); + } + savchar(ch); + add1_buffer(b,'\0'); + tmp=(struct sed_label *)ck_malloc(sizeof(struct sed_label)); + tmp->v=vec; + tmp->v_index=cmd-vec->v; + tmp->name=ck_strdup(get_buffer(b)); + tmp->next=list; + flush_buffer(b); + return tmp; +} + +/* read in a filename for a 'r', 'w', or 's///w' command, and + update the internal structure about files. The file is + opened if it isn't already open. */ +FILE * +compile_filename(readit) + int readit; +{ + char *file_name; + int n; + VOID *b; + int ch; + + if(inchar()!=' ') + bad_prog("missing ' ' before filename"); + b=init_buffer(); + while((ch=inchar())!=EOF && ch!='\n') + add1_buffer(b,ch); + add1_buffer(b,'\0'); + file_name=get_buffer(b); + for(n=0;n=0 && errnov,n=vec->v_length;n;cur_cmd++,n--) { + + exe_loop: + addr_matched=0; + if(cur_cmd->aflags&A1_MATCHED_BIT) { + addr_matched=1; + if(match_address(&(cur_cmd->a2))) + cur_cmd->aflags&=~A1_MATCHED_BIT; + } else if(match_address(&(cur_cmd->a1))) { + addr_matched=1; + if(cur_cmd->a2.addr_type!=ADDR_NULL) + cur_cmd->aflags|=A1_MATCHED_BIT; + } + if(cur_cmd->aflags&ADDR_BANG_BIT) + addr_matched= !addr_matched; + if(!addr_matched) + continue; + switch(cur_cmd->cmd) { + case '{': /* Execute sub-program */ + execute_program(cur_cmd->x.sub); + break; + + case ':': /* Executing labels is easy. */ + break; + + case '=': + printf("%d\n",input_line_number); + break; + + case 'a': + while(append.alloc-append.lengthx.cmd_txt.text_len) { + append.alloc *= 2; + append.text=ck_realloc(append.text,append.alloc); + } + bcopy(cur_cmd->x.cmd_txt.text,append.text+append.length,cur_cmd->x.cmd_txt.text_len); + append.length+=cur_cmd->x.cmd_txt.text_len; + break; + + case 'b': + if(!cur_cmd->x.jump) + end_cycle++; + else { + struct sed_label *j = cur_cmd->x.jump; + + n= j->v->v_length - j->v_index; + cur_cmd= j->v->v + j->v_index; + goto exe_loop; + } + break; + + case 'c': + line.length=0; + if(!(cur_cmd->aflags&A1_MATCHED_BIT)) + ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout); + end_cycle++; + break; + + case 'd': + line.length=0; + end_cycle++; + break; + + case 'D': + { + char *tmp; + int newlength; + + tmp=memchr(line.text,'\n',line.length); + newlength=line.length-(tmp-line.text); + if(newlength) + memmove(line.text,tmp,newlength); + line.length=newlength; + } + end_cycle++; + break; + + case 'g': + line_copy(&hold,&line); + break; + + case 'G': + line_append(&hold,&line); + break; + + case 'h': + line_copy(&line,&hold); + break; + + case 'H': + line_append(&line,&hold); + break; + + case 'i': + ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout); + break; + + case 'l': + { + char *tmp; + int n; + int width = 0; + + n=line.length; + tmp=line.text; + /* Use --n so this'll skip the trailing newline */ + while(--n) { + if(width>77) { + width=0; + putchar('\n'); + } + if(*tmp == '\\') { + printf("\\\\"); + width+=2; + } else if(isprint(*tmp)) { + putchar(*tmp); + width++; + } else switch(*tmp) { +#if 0 + /* Should print \00 instead of \0 because (a) POSIX requires it, and + (b) this way \01 is unambiguous. */ + case '\0': + printf("\\0"); + width+=2; + break; +#endif + case 007: + printf("\\a"); + width+=2; + break; + case '\b': + printf("\\b"); + width+=2; + break; + case '\f': + printf("\\f"); + width+=2; + break; + case '\n': + printf("\\n"); + width+=2; + break; + case '\r': + printf("\\r"); + width+=2; + break; + case '\t': + printf("\\t"); + width+=2; + break; + case '\v': + printf("\\v"); + width+=2; + break; + default: + printf("\\%02x",(*tmp)&0xFF); + width+=2; + break; + } + tmp++; + } + putchar('\n'); + } + break; + + case 'n': + if (feof(input_file)) goto quit; + ck_fwrite(line.text,1,line.length,stdout); + read_pattern_space(); + break; + + case 'N': + if (feof(input_file)) goto quit; + append_pattern_space(); + break; + + case 'p': + ck_fwrite(line.text,1,line.length,stdout); + break; + + case 'P': + { + char *tmp; + + tmp=memchr(line.text,'\n',line.length); + ck_fwrite(line.text, 1, + tmp ? tmp - line.text + 1 + : line.length, stdout); + } + break; + + case 'q': quit: + quit_cmd++; + end_cycle++; + break; + + case 'r': + { + int n = 0; + + rewind(cur_cmd->x.io_file); + do { + append.length += n; + if(append.length==append.alloc) { + append.alloc *= 2; + append.text = ck_realloc(append.text, append.alloc); + } + } while((n=fread(append.text+append.length,sizeof(char),append.alloc-append.length,cur_cmd->x.io_file))>0); + if(ferror(cur_cmd->x.io_file)) + panic("Read error on input file to 'r' command"); + } + break; + + case 's': + if(!tmp.alloc) { + tmp.alloc=50; + tmp.text=ck_malloc(50); + } + count=0; + start = 0; + remain=line.length-1; + tmp.length=0; + rep = cur_cmd->x.cmd_regex.replacement; + rep_end=rep+cur_cmd->x.cmd_regex.replace_length; + + while((offset = re_search(cur_cmd->x.cmd_regex.regx, + line.text, + line.length-1, + start, + remain, + ®s))>=0) { + count++; + if(offset-start) + str_append(&tmp,line.text+start,offset-start); + + if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) { + if(count!=cur_cmd->x.cmd_regex.numb) { + str_append(&tmp,line.text+regs.start[0],regs.end[0]-regs.start[0]); + start = (offset == regs.end[0] ? offset + 1 : regs.end[0]); + remain = (line.length-1) - start; + continue; + } + } + + for(rep_next=rep_cur=rep;rep_next='0' && *rep_next<='9') { + n= *rep_next -'0'; + str_append(&tmp,line.text+regs.start[n],regs.end[n]-regs.start[n]); + } else + str_append(&tmp,rep_next,1); + } + rep_cur=rep_next+1; + } + } + if(rep_next-rep_cur) + str_append(&tmp,rep_cur,rep_next-rep_cur); + if (offset == regs.end[0]) { + str_append(&tmp, line.text + offset, 1); + ++regs.end[0]; + } + start = regs.end[0]; + + remain = (line.length-1) - start; + if(remain<0) + break; + if(!(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT)) + break; + } + if(!count) + break; + replaced=1; + str_append(&tmp,line.text+start,remain+1); + t.text=line.text; + t.length=line.length; + t.alloc=line.alloc; + line.text=tmp.text; + line.length=tmp.length; + line.alloc=tmp.alloc; + tmp.text=t.text; + tmp.length=t.length; + tmp.alloc=t.alloc; + if(cur_cmd->x.cmd_regex.flags&S_WRITE_BIT) + ck_fwrite(line.text,1,line.length,cur_cmd->x.cmd_regex.wio_file); + if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT) + ck_fwrite(line.text,1,line.length,stdout); + break; + + case 't': + if(replaced) { + replaced = 0; + if(!cur_cmd->x.jump) + end_cycle++; + else { + struct sed_label *j = cur_cmd->x.jump; + + n= j->v->v_length - j->v_index; + cur_cmd= j->v->v + j->v_index; + goto exe_loop; + } + } + break; + + case 'w': + ck_fwrite(line.text,1,line.length,cur_cmd->x.io_file); + break; + + case 'x': + { + struct line tmp; + + tmp=line; + line=hold; + hold=tmp; + } + break; + + case 'y': + { + unsigned char *p,*e; + + for(p=(unsigned char *)(line.text),e=p+line.length;px.translate[*p]; + } + break; + + default: + panic("INTERNAL ERROR: Bad cmd %c",cur_cmd->cmd); + } + if(end_cycle) + break; + } +} + + +/* Return non-zero if the current line matches the address + pointed to by 'addr'. */ +int +match_address(addr) +struct addr *addr; +{ + switch(addr->addr_type) { + case ADDR_NULL: + return 1; + case ADDR_NUM: + return (input_line_number==addr->addr_number); + + case ADDR_REGEX: + return (re_search(addr->addr_regex, + line.text, + line.length-1, + 0, + line.length-1, + (struct re_registers *)0)>=0) ? 1 : 0; + + case ADDR_LAST: + return (input_EOF) ? 1 : 0; + + default: + panic("INTERNAL ERROR: bad address type"); + break; + } + return -1; +} + +/* Read in the next line of input, and store it in the + pattern space. Return non-zero if this is the last line of input */ + +int +read_pattern_space() +{ + int n; + char *p; + int ch; + + p=line.text; + n=line.alloc; + + if(feof(input_file)) + return 0; + input_line_number++; + replaced=0; + for(;;) { + if(n==0) { + line.text=ck_realloc(line.text,line.alloc*2); + p=line.text+line.alloc; + n=line.alloc; + line.alloc*=2; + } + ch=getc(input_file); + if(ch==EOF) { + if(n==line.alloc) + return 0; + *p++='\n'; + --n; + line.length=line.alloc-n; + if(last_input_file) + input_EOF++; + return 1; + } + *p++=ch; + --n; + if(ch=='\n') { + line.length=line.alloc-n; + break; + } + } + ch=getc(input_file); + if(ch!=EOF) + ungetc(ch,input_file); + else if(last_input_file) + input_EOF++; + return 1; +} + +/* Inplement the 'N' command, which appends the next line of input to + the pattern space. */ +void +append_pattern_space() +{ + char *p; + int n; + int ch; + + p=line.text+line.length; + n=line.alloc-line.length; + + input_line_number++; + replaced=0; + for(;;) { + ch=getc(input_file); + if(ch==EOF) { + if(n==line.alloc) + return; + *p++='\n'; + --n; + line.length=line.alloc-n; + if(last_input_file) + input_EOF++; + return; + } + *p++=ch; + --n; + if(ch=='\n') { + line.length=line.alloc-n; + break; + } + if(n==0) { + line.text=ck_realloc(line.text,line.alloc*2); + p=line.text+line.alloc; + n=line.alloc; + line.alloc*=2; + } + } + ch=getc(input_file); + if(ch!=EOF) + ungetc(ch,input_file); + else if(last_input_file) + input_EOF++; +} + +/* Copy the contents of the line 'from' into the line 'to'. + This destroys the old contents of 'to'. It will still work + if the line 'from' contains nulls. */ +void +line_copy(from,to) +struct line *from,*to; +{ + if(from->length>to->alloc) { + to->alloc=from->length; + to->text=ck_realloc(to->text,to->alloc); + } + bcopy(from->text,to->text,from->length); + to->length=from->length; +} + +/* Append the contents of the line 'from' to the line 'to'. + This routine will work even if the line 'from' contains nulls */ +void +line_append(from,to) +struct line *from,*to; +{ + if(from->length>(to->alloc-to->length)) { + to->alloc+=from->length; + to->text=ck_realloc(to->text,to->alloc); + } + bcopy(from->text,to->text+to->length,from->length); + to->length+=from->length; +} + +/* Append 'length' bytes from 'string' to the line 'to' + This routine *will* append bytes with nulls in them, without + failing. */ +void +str_append(to,string,length) +struct line *to; +char *string; +int length; +{ + if(length>to->alloc-to->length) { + to->alloc+=length; + to->text=ck_realloc(to->text,to->alloc); + } + bcopy(string,to->text+to->length,length); + to->length+=length; +} + +void +usage() +{ + fprintf(stderr, "\ +Usage: %s [-nV] [+quiet] [+silent] [+version] [-e script] [-f script-file]\n\ + [+expression=script] [+file=script-file] [file...]\n", myname); + exit(4); +} diff --git a/usr/src/usr.bin/sed/utils.c b/usr/src/usr.bin/sed/utils.c new file mode 100644 index 0000000000..2d39cfc9cb --- /dev/null +++ b/usr/src/usr.bin/sed/utils.c @@ -0,0 +1,359 @@ +/* Functions from hack's utils library. + Copyright (C) 1989-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. */ + +/* These routines were written as part of a library (by hack), but since most + people don't have the library, here they are. */ + +#ifdef __STDC__ +#define VOID void +#else +#define VOID char +#endif + +#include +#if defined(USG) || defined(STDC_HEADERS) +#include +#define bcopy(src, dst, len) memcpy((dst), (src), (len)) +#else +#endif +#if defined(STDC_HEADERS) +#include +#else +VOID *malloc(); +VOID *realloc(); +#endif + +VOID *ck_malloc(); + +char *myname; + +#ifdef __STDC__ +#include + +/* Print an error message and exit */ +void +panic(char *str, ...) +{ + va_list iggy; + + fprintf(stderr,"%s: ",myname); + va_start(iggy,str); +#ifdef NO_VFPRINTF + _doprnt(str,&iggy,stderr); +#else + vfprintf(stderr,str,iggy); +#endif + va_end(iggy); + putc('\n',stderr); + exit(4); +} + +#else +#include + +void +panic(str,va_alist) +char *str; +va_dcl +{ + va_list iggy; + + fprintf(stderr,"%s: ",myname); + va_start(iggy); +#ifdef NO_VFPRINTF + _doprnt(str,&iggy,stderr); +#else + vfprintf(stderr,str,iggy); +#endif + va_end(iggy); + putc('\n',stderr); + exit(4); +} + +#endif + +/* Store information about files opened with ck_fopen + so that error messages from ck_fread, etc can print the + name of the file that had the error */ +#define N_FILE 32 + +struct id { + FILE *fp; + char *name; +}; + +static struct id __id_s[N_FILE]; + +/* Internal routine to get a filename from __id_s */ +char * +__fp_name(fp) +FILE *fp; +{ + int n; + + for(n=0;n 0; n--) + if (((*scan)&0xFF) == uc) + return((VOID *)scan); + else + scan++; + + return 0; +} +#endif + +#if !defined(STDC_HEADERS) +/* + * memmove - copy bytes, being careful about overlap. + */ + +VOID * +memmove(dst, src, size) +VOID *dst; +VOID *src; +int size; +{ + register char *d; + register char *s; + register int n; + + if (size <= 0) + return(dst); + + s = (char *)src; + d = (char *)dst; + if (s <= d && s + (size-1) >= d) { + /* Overlap, must copy right-to-left. */ + s += size-1; + d += size-1; + for (n = size; n > 0; n--) + *d-- = *s--; + } else + for (n = size; n > 0; n--) + *d++ = *s++; + + return(dst); +} +#endif + +/* Implement a variable sized buffer of 'stuff'. We don't know what it is, + nor do we care, as long as it doesn't mind being aligned by malloc. */ + +struct buffer { + int allocated; + int length; + char *b; +}; + +#define MIN_ALLOCATE 50 + +VOID * +init_buffer() +{ + struct buffer *b; + + b=(struct buffer *)ck_malloc(sizeof(struct buffer)); + b->allocated=MIN_ALLOCATE; + b->b=(char *)ck_malloc(MIN_ALLOCATE); + b->length=0; + return (VOID *)b; +} + +void +flush_buffer(bb) +VOID *bb; +{ + struct buffer *b; + + b=(struct buffer *)bb; + free(b->b); + b->b=0; + b->allocated=0; + b->length=0; + free(b); +} + +int +size_buffer(b) +VOID *b; +{ + struct buffer *bb; + + bb=(struct buffer *)b; + return bb->length; +} + +void +add_buffer(bb,p,n) +VOID *bb; +char *p; +int n; +{ + struct buffer *b; + + b=(struct buffer *)bb; + if(b->length+n>b->allocated) { + b->allocated*=2; + b->b=(char *)ck_realloc(b->b,b->allocated); + } + bcopy(p,b->b+b->length,n); + b->length+=n; +} + +void +add1_buffer(bb,ch) +VOID *bb; +int ch; +{ + struct buffer *b; + + b=(struct buffer *)bb; + if(b->length+1>b->allocated) { + b->allocated*=2; + b->b=(char *)ck_realloc(b->b,b->allocated); + } + b->b[b->length]=ch; + b->length++; +} + +char * +get_buffer(bb) +VOID *bb; +{ + struct buffer *b; + + b=(struct buffer *)bb; + return b->b; +} diff --git a/usr/src/usr.bin/tar/ChangeLog b/usr/src/usr.bin/tar/ChangeLog new file mode 100644 index 0000000000..0bb9ab1d3a --- /dev/null +++ b/usr/src/usr.bin/tar/ChangeLog @@ -0,0 +1,898 @@ +Mon Jul 1 14:14:06 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * Release of version 1.10; appropriate changes to README. + + * create.c: Removed printf's about sparse files. + + * Fix a misplaced quote in level-0 and change some >& into + 2>&1. + +Fri Jun 21 23:04:31 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * list.c (skip_extended_headers): Userec was being called in + the wrong place. + +Thu Jun 20 19:10:35 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * tar.h: Use ANSI prototypes for msg and msg_perror if + STDC_MSG is defined, even if BSD42 is also. + + * Makefile: Replace DESTDIR with bindir. + (install): Don't install tar.texinfo. There's no standard + place for texinfo files, and /usr/local/man is inappropriate. + Add TAGS, distclean, and realclean targets and SHELL= line. + + * version.c: Move old change history to bottom of ChangeLog. + +Wed Jun 12 12:43:58 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * rtape_lib.c (__rmt_write): #ifdef should reference + SIGNAL_VOID, not USG. + +Wed Jun 5 14:57:11 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu) + + * tar.c (name_match, addname): Ugly hack to handle -C without + any files specified. + tar.h (struct name): New field for ugly hack. + +Mon Jun 3 14:46:46 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu) + + * testpad.c: New file to determine if we need special padding + in struct header in tar.h. + + * tar.h (struct header): include padding if necessary, include + testpad.h. + + * Makefile: rules to create testpad.h, etc. + +Wed May 22 16:02:35 1991 Michael I Bushnell (mib@churchy.gnu.ai.mit.edu) + + * tar.c (options): -L takes an argument. + + * rtape_lib.c (__rmt_open): add /usr/bin/nsh to the list of + remote shell programs. + + * create.c: define MAXPATHLEN if we don't get it from a system + header file. + + * create.c (deal_with_sparse): return a real return value if + we can't open the file. + + * tar.c (long_options): +newer takes an argument. + (describe): fix printing in various trivial ways + +Tue May 21 17:15:19 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * tar.c (long_options): +get and +concatentate don't require arguments + +Mon May 20 15:55:30 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * create.c (write_eot): Don't try and write an EOF if we are + already at one. + + * port.c (strstr): Looking for null string should return zero. + +Sun May 19 22:30:10 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * tar.c (options): -l doesn't take an argument + + * Makefile: minor fix for SGI 4D defines from torda@scum.ethz.ch + + * rtape_server.c (main.c): Suggested mod for 386/AIX from + Minh Tran-Le. I'm suspicious about this one. + + * create.c (dump_file): Mods from Minh Tran-Le for hidden + files on AIX. + gnu.c (collect_and_sort_name, get_dir_contents): AIX hidden file mod. + + * tar.c: (name_next): Mod from David Taylor to allow -C inside + a file list given to -T. + + * Makefile: Comment describing presence of USE_REXEC. + + * extract.c (extract_archive, case LF_SPARSE): zero check for + last element on numbytes needs to look at value after + converted from octal. + + * port.c: Don't always demand strstr, check for HAVE_STRSTR + instead. + Makefile: Comment describing presence of HAVE_STRSTR option. + +Sun May 19 18:39:48 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu) + + * port.c (get_date): Renamed from getdate, to avoid SVR4 conflict. + * tar.c: Call get_date instead of getdate. + +Fri May 10 02:58:17 1991 Noah Friedman (friedman at nutrimat) + + * tar.c: added "\n\" to the end of some documentation strings + where they were left off. + +Thu May 9 17:28:54 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * Makefile: added level-0, level-1, and backup-specs to AUX. + * version.c: changed to 1.10 beta. + * README: updated for 1.10 beta release. + +Tue Apr 2 12:04:54 1991 Michael I Bushnell (mib at godwin) + + * create.c (dump_file): HPUX's st_blocks is in 1024 byte units + instead of 512 like the rest of the world, so I special cased + it. + * tar.c: Undo Noah's changes. + +Mon Apr 1 17:49:28 1991 Noah Friedman (friedman at wookumz.gnu.ai.mit.edu) + + (This ought to be temporary until things are fixed properly. ) + + * tar.c: (struct option long_options): flag for "sparse" zero if + compiling under hpux. + tar.c: (functon options): case 'S' is a no-op if compiling under + hpux. + +Sat Mar 30 12:20:41 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * tar.h: new variable tape_length. + + * tar.c (options): add new option +tape-length / -L. + + * buffer.c (fl_write): Turn #ifdef TEST code for limited tape + length on always, for tape-length option. + + * create.c (dump_file): avoid apollo lossage where S_IFIFO == S_IFSOCK. + + * buffer.c: include regex.h + * buffer.c (fl_read, open_archive): Use regex routines for + volume header match. + * xmalloc.c: removed file; wasn't necessary. + * tar.c: (main) use ck_malloc instead of xmalloc. + +Thu Mar 28 04:05:05 1991 Noah Friedman (friedman at goldman) + + * regex.c, regex.o: New links. + * tar.c: include regex.h. + * Makefile (OBJ2): Add regex.o. + (regex.o, tar.o): Depend on regex.h + (SRC2, AUX): Add the new files. + +Sat Mar 23 15:39:42 1991 Noah Friedman (friedman at wookumz.gnu.ai.mit.edu) + + * Makefile: added default flags and options for compiling under + hpux. + + * Added files alloca.c and xmalloc.c + +Sat Mar 23 14:35:31 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu) + + * port.c: Define WANT_VALLOC in HPUX. + +Fri Mar 15 06:20:15 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * rtape_lib.c: If USG and not HAVE_MTIO, define NO_RMTIOCTL + automatically. + (_rmt_rexec): Temporarily re-open stdin and stdout to + /dev/tty, to guarantee that rexec() can prompt and read the + login name and password from the user. + From pascal@cnam.cnam.fr (Pascal Meheut). + * Makefile: Mention -DUSE_REXEC. + +Fri Mar 8 20:15:11 1991 Michael I Bushnell (mib at wookumz.ai.mit.edu) + + * tar.h, Makefile: Makefile CPP macro HAVE_SIZE_T might be + useful for some people. + + * gnu.c: lstat->stat define where appropriate + + * buffer.c (fl_write): keep track of amount written for +totals. + * tar.c, tar.h: set flag f_totals from +totals option + * tar.h (f_totals, tot_written): new variables + * tar.c (main): print total written with CMD_CREATE + + * tar.c (main): return appropriate exit status + +Thu Jan 17 00:50:21 1991 David J. MacKenzie (djm at apple-gunkies) + + * port.c: Remove a spurious `+' between functions (a remnant + of a context diff, apparently). + +Wed Jan 9 19:43:59 1991 Michael I Bushnell (mib at pogo.ai.mit.edu) + + * create.c (where_is_data): Rewritten to be better, and then + #ifdef-ed out. + (deal_with_sparse): Severly pruned. Now we write or don't + write only complete blocks, not worrying about partial blocks. + This simplifies calculations, removes bugs, and elides the + second scan through the block. The first was zero_record, the + second was where_is_data. + +Mon Jan 7 17:13:29 1991 Michael I Bushnell (mib at wookumz.ai.mit.edu) + + * create.c (deal_with_sparse): Second computation (for short + reads) of numbytes increment had subtraction backwards. + Need to handle calling where_is_data better when we did a + short read (it might go past the end of the read), also, set + sparsearray[...].offset in this case too. + +Fri Jan 4 12:24:38 EST 1991 Jay Fenlason (hack@ai.mit.edu) + + * buffer.c Return a special error code if the archive you're + trying to read starts with a different label than the one specified + on the command line. + +Wed Jan 2 12:05:21 EST 1991 Jay Fenlason (hack@ai.mit.edu) + + * gnu.c Prepend the current directory to the gnu_dumpfile, so that + -C's won't affect where the output goes. (sigh.) + +Tue Dec 18 18:05:59 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * (gnu.c) Don't complain if the gnudumpfile we're reading info + from doesn't exist. + + * create.c Write out gnudumpfile after finishing writing the archive. + + * tar.c Add +exclude FNAME, and make +exclude-from do what +exclude + used to. + + Make +version an operation, not an option. + + add +confirmation alias for +interactive. + +Tue Dec 4 13:28:08 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * tar.c (check_exclude) Don't let MUMBLE match MUMBLE.c or fooMUMBLE + but only foo/MUMBLE + + * Add the name mangler (mangle.c, plus changes to create.c and + extract.c) + + * extract.c Three small patches from Chip Salzenberg + (tct!chip@uunet.uu.net) + + Don't complain when extracting a link, IFF it already exists. + + Don't complain when extracting a directory IFF it already + exists. + + Don't ad u+wx to directories when running as root. + + * gnu.c Some changes from Chip Salzenberg to make + +listed-incremental work. + + * port.c Add the F_FREESP emulation of the ftruncate syscall. + +Wed Nov 21 15:57:07 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + Remove excess \n from lots of msg() calls. + +Mon Nov 19 14:09:43 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * tar.c Rename +volume to +label + +Fri Nov 16 15:43:44 1990 David J. MacKenzie (djm at apple-gunkies) + + * tar.c (describe): Include the default values for -b and -f + (as set in the Makefile) in the message. + +Thu Nov 15 13:36:45 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * extract.c (extract_archive) Do the utime() call before the + chmod() call, 'cuz some versons of utime() trash the file's mode + bits. + + * list.c (read_and) Call do_something on volume headers and + multivol files even if they don't match the names we're looking for, + etc. . . + +Tue Nov 6 13:51:46 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * port.c (un-quote-string) Don't try to write a null + if there's already one there. + +Thu Nov 1 14:58:57 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * buffer.c (new_volume) fflush(msg_file) before reading for + confirmation on new volume. On EOF or error, print error msg and + abort. + +Mon Oct 29 12:06:35 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * getdate.y Use new version of getdate(). + + * tar.c (name_add) Use sizeof(char *) instead of sizeof(int) + + * README give the correct return address. + +Thu Oct 25 16:03:58 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + rtape_lib.c Change RMTIOCTL to NO_RMTIOCTL, so it is on by default. + + rmt.h Add _isrmt() #define for NO_REMOTE case. + + gnu.c Add forward reference for add_dir_name(). + +Tue Oct 16 11:04:52 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + 1.09 New -G file implementation of gnu-dump stuff. + + * tar.c (name_add) Get the calls to ck_realloc and ck_malloc right. + +Thu Oct 11 11:23:38 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * gnu.c Fix A couple of typos. + +Wed Sep 19 13:35:03 1990 David J. MacKenzie (djm at apple-gunkies) + + * getdate.y [USG] (ftime): Use `daylight' unless + DAYLIGHT_MISSING is defined. + +Mon Sep 17 18:04:21 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * gnu.c (gnu_restore) Don't use a passed char* for the + file name, use skipcrud+head->header.name, just like everything + else does. This means that gnu_restore will still work with + small buffers, etc. + +Thu Sep 13 15:01:17 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * tar.c (add_exclude) Don't bus-error if the exclude file doesn't + end with a newline. + +Sun Sep 9 22:35:27 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Makefile (dist): Remove .fname when done. + +Thu Sep 6 12:48:58 EDT 1990 Jay Fenlason (hack@ai.mti.edu) + + * gnu.c (gnu_restore) Rember to skip_file() over the directory + contents, even if we don't have to do anything with them. + + * create.c extract.c diffarch.c Free sparsearray after we're done + with it. + +Tue Sep 4 10:18:50 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile Include gnu.c in dist + + * gnu.c move add_dir above read_dir_file so that cc doesn't complain + about add_dir returning void. + +Sun Sep 2 20:46:34 1990 David J. MacKenzie (djm at apple-gunkies) + + * getdate.y: Declare some more functions and add storage + classes where omitted to shut compiler up. + [USG] (ftime): Don't use extern var `daylight'; appears that + some systems don't have it. + +Wed Aug 29 00:05:06 1990 David J. MacKenzie (djm at apple-gunkies) + + * getdate.y (lookup): In the code that allows `Aug.' to be + recognized as `Aug', don't chop off the final `.' from words + like `a.m.', so they can be recognized. + +Thu Aug 16 11:34:07 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * buffer.c (open_archive) If -O, write verbosity to stderr + instead of stdout. + +Fri Aug 10 12:29:28 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * getdate.y Handle an explicit DST in the input string. + A dozen line patch from Per Foreby (perf@efd.lth.se). + +Mon Jul 16 13:05:11 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * tar.c rename -g -G +incremental, +listed-imcremental, etc. + +Fri Jul 13 14:10:33 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * tar.c Make +newer and +newer-mtime work according to their names. + + * gnu.c If +newer or +newer-mtime, use the time specified on the + command line. + + * buffer.c, create.c Add test to see if dimwit is trying to + archive the archive. + + * tar.c (long_options[]) re-ordered, so that groups of similar + options are next to each other. . . I think. + + (describe) Modified to more closely reflect reality. + +Fri Jul 6 13:13:59 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * tar.c add compile-time option for SYS V (?) style + tape-drive names /dev/rmt/{n}[lmh] + + * tar.c Fix getopt-style stuff so that -C always works correctly. + + * gnu.c, tar.c make filename to -G optional. + + * {all over}, replace some fprintf(stderr...) calls with calls + to msg(). + + * port.c Make -Dmumble_MSG option on command line override + internal assumptions. + + * Makefile Mention -Dmumble_MSG options + +Fri Jul 6 02:35:31 1990 David J. MacKenzie (djm at apple-gunkies) + + * tar.c (options): Don't change `c' if it is 0, as getopt now + handles that internally. + +Mon Jul 2 15:21:13 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * gnu.c (new file) Moved all the f_gnudump stuff here where we + can keep track of it easier. Also made -G take a file name where it + stores the inode information about directories so that we can + detect moved directores. + + * create.c (dump_file) Changed slightly to work with the new + f_gnudump. + + * tar.c Moved the f_gnudump stuff to gnu.c + + * tar.c, extract.c added the +do-chown option, which forces tar + to always try to chown the created files to their original owners. + + * version.c New version 1.09 + +Sun Jun 24 14:26:28 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * create.c: Change ifdefs for directory library header + selection to be like the ones in tar.c. + * Makefile [Xenix]: Link with -ldir to get the dirent.h + directory library. + +Thu Jun 7 03:31:51 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Makefile, buffer.c, diffarch.c: Change MTIO symbol to HAVE_MTIO + because SCO Xenix defines 'MTIO' for an incompatible tape driver + system in a file included by termio.h. + * tar.h: Don't define size_t for Xenix. + +Tue Jun 5 11:38:00 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * create.c (dump_file) Only print the + "... is on a different filesystem..." if f_verbose is on. + also add a case for S_IFSOCK and treat it like a FIFO. + (Not sure if that's the right thing to do or not, but it's better + than all those Unknown File Type msgs.) + +Thu May 31 19:25:36 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * port.c Use #ifdef sparc instead of #ifdef SPARC since + the lowercase version is defined, and the uppercase one isn't. + +Tue May 22 11:49:18 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * port.c (ck_malloc) if size==0 pretend size=1 + (ck_realloc) if(!ptr) call ck_malloc instead. + +Tue May 15 12:05:45 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * diffarch.c (diff_archive) If not f_absolute_paths, and attempt to + open a file listed in the archive fails, try /filename also. This will + allow diff to open the wrong file if both /filename and filename exist, + but there's nothing we can do about that. + +Fri May 11 16:17:43 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile, Descripbe new -DMTIO option. + + * buffer.c diffarch.c Change ifdefs slightly, so that + -DMTIO will include sys/mtio.h even if USG is defined. + This is for HUPX and similar BSD/USG crossovers. + +Tue May 8 13:14:54 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + * update.c (update_archive) Call reset_eof() when appropriate. + + * buffer.c (reset_eof) New function, that turns of EOF flag, and + re-sets the ar_record and ar_last pointers. This will allow + 'tar rf non-existant-file' to not core-dump. + +Fri May 4 14:05:31 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * tar.c: Recognize the +sparse option. It was documented, but + only the short form (-S) was actually recognized. + +Tue Apr 17 21:34:14 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * create.c Don't access location 0 if ->dir_contents is null. + +Wed Apr 11 17:30:03 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * buffer.c (flush_archive, close_archive, new_volume) Always check + the return value of rmtclose(), and only give a warning msg if it is + <0. Some device drivers (including Sun floppy disk, and HP + streaming tape) return -1 after an IO error (or something like that.) + +Fri Mar 23 00:06:30 1990 Jim Kingdon (kingdon at mole.ai.mit.edu) + + * tar.c (long_options): Make it so +append +extract +list +update + +catenate and +delete don't take arguments. + +Mon Mar 12 13:33:53 EST 1990 + + * buffer.c (open_archive, fl_write) Set the mtime of the volume + header to the current time. + +Wed Mar 7 14:10:10 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * buffer.c Fix +compress-block A two character patch from + Juha Sarlin (juha@tds.kth.se) + Replace #ifdef __GNU__ with #ifdef __STDC__ + (new_volume) If open of new archive fails, ask again + (Is probably user error.) + + * tar.c Replace #ifdef __GNU__ with #ifdef __STDC__ + + * port.c Clean up #ifdef and #defines a bit. + (quote_copy_string) Sometimes the malloc'd buffer + would be up to two characters too short. + + * extract.c (extract_archive) Don't declare ind static. + + * create.c (dump_file) Don't declare index_offset static. + + * diffarch.c Remove diff_name variable, and always use + head->header.name, which will always work, unlike diff_name, which + becomes trash when the next block is read in. + +Thu Mar 1 13:43:30 EST 1990 Jay Fenlason (hack@wookumz.ai.mit.edu) + + * Makefile Mention the -NO_REMOTE option. + * port.c Fix typo, and define WANT_FTRUNCATE on i386 machines. + +Mon Feb 26 17:44:53 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * getdate.y: Declare yylex and yyerror as static. + #define yyparse to getdate_yyparse. + +Sun Feb 25 20:47:23 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * tar.c: Remove +old option, since it is a valid abbreviation of + +old-archive, which does the same thing. + (describe): A few small cleanups in message. + +Mon Feb 5 14:29:21 EST 1990 Jay Fenlason (hack@wookumz) + + * port.c define LOSING_MSG on sparc, since doprnt_msg doesn't work. + Fix typo in #ifdef WANT_GETWD + +Fri Jan 26 16:11:20 EST 1990 Jay Fenlason (hack@wookumz) + + 1.08 Sparse file support added. Also various other features. + + * diffarch.c (compare_chunk) Include correct arguments in + a call to fprintf() for an error msg. + (compare_chunks, compare_dir) First argument is a long, not an int. + + * tar.c (options) Use tar variable (argv[0]) as the name to print + in an error msg, instead of a constant "tar". + (confirm) Use external variable char TTY_NAME[] for name of file + to open for confirmation input. + + * buffer.c (new_volume) Ditto. + + * port.c Add declaration for TTY_NAME[]. + + * rmt.h Add long declarations for lseek() and __rmt_lseek(); + +Tue Jan 23 14:06:21 EST 1990 Jay Fenlason (hack@wookumz) + * tar.c, create.c Create the +newer-mtime option, which is like + +newer, but only looks for files whose mtime is newer than the + given date. + + * rtape_lib.c Make *both* instances of signal-handler stuff use + void (*foo)() on USG systems. + +Thu Jan 11 14:03:45 EST 1990 Jay Fenlason (hack@wookumz) + + * getdate.y Parse European dates of the form YYMMDD. + In ftime() Init timezone by calling localtime(), and remember that + timezone is in seconds, but we want timeb->timezone to be in minutes. + This small patch from Joergen Haegg (jh@aahas.se) + + * rtape_lib.c (__rmt_open) Also look for /usr/bsd/rsh. + Declare signal handler as returning void instead of int if USG is + defined. + + * port.c Declare WANT_GETWD for SGI 4-D IRIS. + + * Makefile Include defines for SGI 4D version. There are a simple + patch from Mike Muuss (mike@brl.mil). + + * buffer.c (fl_read) Work properly on broken Ultrix systems where + read() returns -1 with errno==ENOSPC on end of tape. Correctly go + on to the next volume if f_multivol. + + * list.c (list_archive,print_header) Flush msg_file after printing + messages. + + * port.c Delete unused references to alloca(). + Don't crash if malloc() returns zero in quote_copy_string. + Flush stderr in msg() and msg_perror(). + + * tar.c Flush msg_file after printing confirmation msg. + +Wed Jan 10 01:58:46 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * tar.c (main): Change -help option and references to it to +help, + and remove suggestion to run info (which is unreleased, so not + likely to be of any help). + +Tue Jan 9 16:16:00 EST 1990 Jay Fenlason (hack @wookumz) + + * create.c (dump_file) Close file descriptor if start_header() + fails. + (dump_file) Change test for ./ ness to not think that + .{any character} is a ./ These are both trivial changes from + Piercarlo "Peter" Grandi pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk + + * diffarch.c (diff_init) Print correct number of bytes in error + message. + +Tue Jan 9 03:19:49 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * Makefile: Add comment at top noting that two source files also + contain #defines that might need to be changed by hand. + + * create.c, diffarch.c, extract.c: Change L_SET to 0 in lseek + calls, because only BSD defines it. + * create.c (dump_file): Make sparse file checking code conditional + on BSD42 because it uses st_blocks, which the other systems lack. + +Tue Jan 2 13:35:56 EST 1990 Jay Fenlason (hack@gnu) + + * port.c (quote_copy_string) Fix so it doesn't scramble memory if + the last character is non-printable. A trivial fix from Kian-Tat Lim + (ktl@wag240.caltech.edu). + +Tue Dec 19 11:19:37 1989 Jim Kingdon (kingdon at pogo) + + * port.c [BSD42]: Define DOPRNT_MSG. + tar.h [BSD42]: Do not prototype msg{,_perror}. + +Fri Dec 8 11:02:47 EST 1989 Jay Fenlason (hack@gnu) + + * create.c (dump_file) Remove typo in msg. + +Fri Dec 1 19:26:47 1989 David J. MacKenzie (djm at trix) + + * Makefile: Remove comments referring to certain systems lacking + getopt, since it is now provided always and needed by all systems. + + * port.c: Remove copy of getopt.c, as it is now linked in + separately to always get the current version. + + * tar.c: Rename +cat-tars option to +catenate or +concatenate, + and +local-filesystem to +one-file-system (preferred by rms + and used in GNU cp for the same purpose). + (describe): Reflect changes. + +Tue Nov 28 04:28:26 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * port.c: Move declaration of alloca into #else /* sparc */ + so it will compile on sparcs. + +Mon Nov 27 15:17:08 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * tar.c (options): Remove -version option (replaced by +version). + (describe): Mention long options. + +Sat Nov 25 04:25:23 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) + + * getoldopt.c (getoldopt): Make `opt_index' argument a pointer to + an int, not char. + + * tar.c: Modify long options per rms's suggestions: + Make preserve-permissions an alias for same-permissions. + Make preserve-order an alias for same-order. + Define preserve to mean both of those combined. + Make old an alias for old-archive. + Make portability an alias for old-archive, also. + Rename sym-links to dereference. + Rename gnudump to incremental. + Rename filename to file. + Make compare an alias for diff. Leave diff but prefer compare. + Rename blocking-factor to block-size. + Rename chdir to directory. + Make uncompress an alias for compress. + Rename confirm to interactive. + Make get an alias for extract. + Rename volume-header to volume. + + Also make +version an alias for -version. + + (options): Shorten code that interprets long options by using + the equivalent short options' code. This also makes it tons + easier to change the long options. + + (describe): Make usage message more internally consistent + stylistically. + +Mon Nov 20 14:55:39 EST 1989 hack@ai.mit.edu + + * list.c (read_and) Call check_exclude() to see if the files + should be skipped on extract or list. + +Thu Nov 9 18:59:32 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * buffer.c (fl_read): Fix typos in error message + "tar EOF not on block boundary". + +Mon Oct 23 13:09:40 EDT 1989 (hack@ai.mit.edu) + + * tar.c (long_options[]) Add an option for blocked compression. + +Thu Oct 19 13:38:16 EDT 1989 (hack@ai.mit.edu) + + * buffer.c (writeerror) Print a more useful error msg. + +Wed Sep 27 18:33:41 EDT 1989 (hack@ai.mit.edu) + + * tar.c (main) Mention "tar -help" if the luser types a non-workable + set of options. + +Mon Sep 11 15:03:29 EDT 1989 (hack@ai.mit.edu) + + * tar.c (options) Have -F correctly set info_script. + +Tue Aug 29 12:58:06 EDT 1989 (hack@ai.mit.edu) + + * Makefile Include ChangeLog in tar.tar and tar.tar.Z + +Mon Aug 28 17:42:24 EDT 1989 (hack@ai.mit.edu) + + * tar.c (options) Made -F imply -M + Also remind tar that the -f option takes an argument! + + * Modified -F option to make it do what (I think) it + should. e.g, if you say -F, tar won't send a msg to + msg_file and wait for a It'll just run the program + it was given, and when the prog returns, the new tape had + *better* be ready. . . + + * buffer.c (open_archive) Give error message and abort if + the luser didn't give an archive name. + +Fri Aug 25 20:05:27 EDT 1989 Joy Kendall (jak at hobbes) + + * Added code to make a new option to run a specified script + at the end of each tape in a multi-volume backup. Changed: + tar.c: made new switch, -F, and new long-named option, + "info-script". Code is where you would expect. + tar.h: added flag f_run_script_at_end, and an extern char * + called info_script, which optarg gets set to. + buffer.c: line 1158 in new_volume(): if f_run_script_at_end + is set, we give info_script to system(), otherwise we do + what we've always done. **FIXME** I'm not sure if that's all + that has to be done here. + +Thu Aug 24 10:09:38 EDT 1989 Joy Kendall (jak at spiff) +(These changes made over the course of 6/89 - 8/89) + + * diffarch.c: diff_archive: Added switches for LF_SPARSE in the + case statements that needed it. Also, skip any extended headers + if we need to when we skip over a file. (need to change + the bit about, if the size doesn't agree AND the file is NOT + sparse, then there's a discrepancy, because I added another + field to the header which should be able to deal with the + sizes) If the file is sparse, call the added routine + "diff_sparse_files" to compare. Also added routine + "fill_in_sparse_array". + + * extract.c: extract_archive: added the switch LF_SPARSE + to the case statement as needed, and code to treat the + sparse file. At label "again_file", modified opening the + file to see if we should have O_APPEND be one of the modes. + Added code at label "extract_file" to call the new routine + "extract_sparse_file" when we have an LF_SPARSE flag. + + Note: really should erase the commented-out code in there, + because it's confusing. + + * update.c: made sure that if a file needed to be "skipped" + over, it would check to see if the linkflag was sparse, and + if so, would then make sure to skip over any "extended + headers" that might come after the header itself. Do so by + calling "skip_extended_headers". + + * create.c: create_archive: added code to detect a sparse + file when in the long case statement. Added ways to detect + extended headers, and label "extend" (ack! should get rid of + that, is atrocious). Call the new routine "finish_sparse_file" + if the linkflag is LF_SPARSE to write the info to the tape. + Also added routines "init_sparsearray", "deal_with_sparse", + "clear_buffer", "where_is_data", "zero_record", and + "find_new_file_size". + + * tar.h: Added the #define's SPARSE_EXT_HDR and + SPARSE_IN_HDR. Added the struct sparse and the struct + sp_array. Added the linkflag LF_SPARSE. Changed the tar + header in several ways: + - added an array of struct sparse's SPARSE_IN_HDR long + - added a char flag isextended + - added a char string realsize to store the true + size of a sparse file + Added another choice to the union record called a + struct extended_header, which is an array of 21 struct + sparse's and a char isextended flag. Added flag + f_sparse_file to list of flags. + + * tar.c: added long-named options to make tar compatible with + getopt_long, changed Makefile. + +... ... .. ..:..:.. ... .... Jay Fenlason (hack@ai.mit.edu) + + 1.07 New version to go on beta tape with GCC 1.35 + Better USG support. Also support for __builtin_alloca + if we're compiling with GCC. + diffarch.c: Include the correct header files so MTIOCTOP + is defined. + tar.c: Don't print the verbose list of options unless + given -help. The list of options is *way* too long. + + 1.06 Use STDC_MSG if __STDC__ defined + ENXIO meand end-of-volume in archive (for the UNIX PC) + Added break after volume-header case (line 440) extract.c + Added patch from arnold@unix.cc.emory.edu to rtape_lib.c + Added f_absolute_paths option. + Deleted refereces to UN*X manual sections (dump(8), etc) + Fixed to not core-dump on illegal options + Modified msg_perror to call perror("") instead of perror(0) + patch so -X - works + Fixed tar.c so 'tar cf - -C dir' doesn't core-dump + tar.c (name_match): Fixed to chdir() to the appropriate + directory if the matching name's change_dir is set. This + makes tar xv -C foo {files} work. + + 1.05 A fix to make confirm() work when the archive is on stdin + include 'extern FILE *msg_file;' in pr_mkdir(), and fix + tar.h to work with __STDC__ + + Added to port.c: mkdir() ftruncate() Removed: lstat() + Fixed -G to work with -X + Another fix to tar.texinfo + Changed tar.c to say argv[0]":you must specify exactly ... + buffer.c: modified child_open() to keep tar from hanging when + it is done reading/writing a compressed archive + added fflush(msg_file) before printing error messages + create.c: fixed to make link_names non-absolute + + 1.04 Added functions msg() and msg_perror() Modified all the + files to call them. Also checked that all (I hope) + calls to msg_perror() have a valid errno value + (modified anno() to leave errno alone), etc + Re-fixed the -X option. This time for sure. . . + re-modified the msg stuff. flushed anno() completely + Modified the directory stuff so it should work on sysV boxes + added ftime() to getdate.y + Fixed un_quote_string() so it won't wedge on \" Also fixed + \ddd (like \123, etc) + More fixes to tar.texinfo + + 1.03 Fixed buffer.c so 'tar tzf NON_EXISTENT_FILE' returns an error + message instead of hanging forever + More fixes to tar.texinfo + + 1.02 Fixed tar.c so 'tar -h' and 'tar -v' don't cause core dump + Also fixed the 'usage' message to be more up-to-date. + Fixed diffarch.c so verify should compile without MTIOCTOP + defined + + 1.01 Fixed typoes in tar.texinfo + Fixed a bug in the #define for rmtcreat() + Fixed the -X option to not call realloc() of 0. + + Version 1.00: version.c added. -version option added + Installed new version of the remote-tape library + Added -help option + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +End: diff --git a/usr/src/usr.bin/tar/README b/usr/src/usr.bin/tar/README new file mode 100644 index 0000000000..1e17b22384 --- /dev/null +++ b/usr/src/usr.bin/tar/README @@ -0,0 +1,50 @@ +This GNU tar 1.10. Please send bug reports, etc., to +bug-gnu-utils@prep.ai.mit.edu. + +This is GNU tar. It is based heavily on John Gilmore's public domain +tar, but with added features. The manual is currently being written. +An old manual, surely riddled with errors, is in tar.texinfo. Please +don't send in bug reports about that manual. In particular, the +mechanism for doing incremental dumps has been significantly changed. + +The mt program is in the GNU cpio distribution. + +Various people have been having problems using floppies on a NeXT. +I've gotten conflicting reports about what should be done to solve the +problems, and we have no way to test it ourselves. If you don't have +"rename" in your C library, you will need to find an implementation. +I'm not sure if I want to roll in the GNU implementation into tar. + + -mib + +User-visible changes since 1.09: + +Filename to -G is optional. -C works right. +Names +newer and +newer-mtime work right. + +-g is now +incremental +-G is now +listed-incremental + +Sparse files now work correctly. + ++volume is now called +label. + ++exclude now takes a filename argument, and +exclude-from does what ++exclude used to do. + +Exit status is now correct. + ++totals keeps track of total I/O and prints it when tar exits. + +When using +label with +extract, the label is now a regexp. + +New option +tape-length (-L) does multi-volume handling like BSD dump: +you tell tar how big the tape is and it will prompt at that point +instead of waiting for a write error. + +New backup scripts level-0 and level-1 which might be useful to +people. They use a file "backup-specs" for information, and shouldn't +need local modification. These are what we use to do all our backups +at the FSF. + + diff --git a/usr/src/usr.bin/tar/create.c b/usr/src/usr.bin/tar/create.c new file mode 100644 index 0000000000..ee601b0cea --- /dev/null +++ b/usr/src/usr.bin/tar/create.c @@ -0,0 +1,1302 @@ +/* Create a tar archive. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Create a tar archive. + * + * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu. + * + * @(#)create.c 1.36 11/6/87 - gnu + */ +#include +#include +#include + +#ifndef V7 +#include +#endif + +#ifndef __MSDOS__ +#include +#include /* for MAXPATHLEN */ +#include +#include +#endif + +#ifdef BSD42 +#include +#else +#ifdef __MSDOS__ +#include "msd_dir.h" +#else +#ifdef USG +#ifdef NDIR +#include +#else +#include +#endif +#ifndef DIRECT +#define direct dirent +#endif +#define DP_NAMELEN(x) strlen((x)->d_name) +#else +/* + * FIXME: On other systems there is no standard place for the header file + * for the portable directory access routines. Change the #include line + * below to bring it in from wherever it is. + */ +#include "ndir.h" +#endif +#endif +#endif + +#ifndef DP_NAMELEN +#define DP_NAMELEN(x) (x)->d_namlen +#endif + +#ifdef USG +#include /* major() and minor() defined here */ +#endif + +/* + * V7 doesn't have a #define for this. + */ +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +/* + * Most people don't have a #define for this. + */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#include "tar.h" +#include "port.h" + +extern struct stat hstat; /* Stat struct corresponding */ + +#ifndef __MSDOS__ +extern dev_t ar_dev; +extern ino_t ar_ino; +#endif + +/* JF */ +extern struct name *gnu_list_name; + +/* + * If there are no symbolic links, there is no lstat(). Use stat(). + */ +#ifndef S_IFLNK +#define lstat stat +#endif + +extern char *malloc(); +extern char *strcpy(); +extern char *strncpy(); +extern void bzero(); +extern void bcopy(); +extern int errno; + +extern void print_header(); + +union record *start_header(); +void finish_header(); +void finduname(); +void findgname(); +char *name_next(); +void to_oct(); +void dump_file(); + + +/* This code moved from tar.h since create.c is the only file that cares + about 'struct link's. This means that other files might not have to + include sys/types.h any more. + */ + +struct link { + struct link *next; + dev_t dev; + ino_t ino; + short linkcount; + char name[1]; +}; + +struct link *linklist; /* Points to first link in list */ + +static nolinks; /* Gets set if we run out of RAM */ + +/* + * "Scratch" space to store the information about a sparse file before + * writing the info into the header or extended header + */ +/* struct sp_array *sparsearray;*/ + +/* number of elts storable in the sparsearray */ +/*int sparse_array_size = 10;*/ + +void +create_archive() +{ + register char *p; + char *name_from_list(); + + open_archive(0); /* Open for writing */ + + if(f_gnudump) { + char buf[MAXNAMLEN],*q,*bufp; + + collect_and_sort_names(); + + while(p=name_from_list()) + dump_file(p,-1); + /* if(!f_dironly) { */ + blank_name_list(); + while(p=name_from_list()) { + strcpy(buf,p); + if(p[strlen(p)-1]!='/') + strcat(buf,"/"); + bufp=buf+strlen(buf); + for(q=gnu_list_name->dir_contents;q && *q;q+=strlen(q)+1) { + if(*q=='Y') { + strcpy(bufp,q+1); + dump_file(buf,-1); + } + } + } + /* } */ + + } else { + p = name_next(1); + if(!p) + dump_file(".", -1); + else { + do dump_file(p, -1); + while (p = name_next(1)); + } + } + + write_mangled(); + write_eot(); + close_archive(); + if(f_gnudump) + write_dir_file(); + name_close(); +} + +/* + * Dump a single file. If it's a directory, recurse. + * Result is 1 for success, 0 for failure. + * Sets global "hstat" to stat() output for this file. + */ +void +dump_file (p, curdev) + char *p; /* File name to dump */ + int curdev; /* Device our parent dir was on */ +{ + union record *header; + char type; + extern char *save_name; /* JF for multi-volume support */ + extern long save_totsize; + extern long save_sizeleft; + union record *exhdr; + char save_linkflag; + extern time_t new_time; + int sparse_ind = 0; + + + if(f_confirm && !confirm("add",p)) + return; + + /* + * Use stat if following (rather than dumping) 4.2BSD's + * symbolic links. Otherwise, use lstat (which, on non-4.2 + * systems, is #define'd to stat anyway. + */ +#ifdef AIX + if (0 != f_follow_links ? + statx (p, &hstat, STATSIZE, STX_HIDDEN): + statx (p, &hstat, STATSIZE, STX_HIDDEN|STX_LINK)) +#else + if (0 != f_follow_links? stat(p, &hstat): lstat(p, &hstat)) +#endif /* AIX */ + { +badperror: + msg_perror("can't add file %s",p); +badfile: + errors++; + return; + } + +#ifdef AIX + if (S_ISHIDDEN (hstat.st_mode)) { + char *new = (char *)allocate (strlen (p) + 2); + if (new) { + strcpy (new, p); + strcat (new, "@"); + p = new; + } + } +#endif /* AIX */ + + /* See if we only want new files, and check if this one is too old to + put in the archive. */ + if( f_new_files + && !f_gnudump + && new_time>hstat.st_mtime + && (hstat.st_mode&S_IFMT)!=S_IFDIR + && (f_new_files>1 || new_time>hstat.st_ctime)) { + if(curdev<0) { + msg("%s: is unchanged; not dumped",p); + } + return; + } + +#ifndef __MSDOS__ + /* See if we are trying to dump the archive */ + if(ar_dev && hstat.st_dev==ar_dev && hstat.st_ino==ar_ino) { + msg("%s is the archive; not dumped",p); + return; + } +#endif + /* + * Check for multiple links. + * + * We maintain a list of all such files that we've written so + * far. Any time we see another, we check the list and + * avoid dumping the data again if we've done it once already. + */ + if (hstat.st_nlink > 1) switch (hstat.st_mode & S_IFMT) { + register struct link *lp; + + case S_IFREG: /* Regular file */ +#ifdef S_IFCTG + case S_IFCTG: /* Contigous file */ +#endif +#ifdef S_IFCHR + case S_IFCHR: /* Character special file */ +#endif + +#ifdef S_IFBLK + case S_IFBLK: /* Block special file */ +#endif + +#ifdef S_IFIFO + case S_IFIFO: /* Fifo special file */ +#endif + + /* First quick and dirty. Hashing, etc later FIXME */ + for (lp = linklist; lp; lp = lp->next) { + if (lp->ino == hstat.st_ino && + lp->dev == hstat.st_dev) { + char *link_name = lp->name; + + /* We found a link. */ + hstat.st_size = 0; + header = start_header(p, &hstat); + if (header == NULL) goto badfile; + while(!f_absolute_paths && *link_name == '/') { + static int link_warn = 0; + + if (!link_warn) { + msg("Removing leading / from absolute links"); + link_warn++; + } + link_name++; + } + strncpy(header->header.linkname, + link_name,NAMSIZ); + if(header->header.linkname[NAMSIZ-1]) { + char *mangled; + extern char *find_mangled(); + + mangled=find_mangled(link_name); + msg("%s: link name too long: mangled to %s",link_name,mangled); + strncpy(header->header.linkname,mangled,NAMSIZ); + } + header->header.linkflag = LF_LINK; + finish_header(header); + /* FIXME: Maybe remove from list after all links found? */ + return; /* We dumped it */ + } + } + + /* Not found. Add it to the list of possible links. */ + lp = (struct link *)malloc((unsigned)(sizeof(struct link)+strlen(p))); + if (!lp) { + if (!nolinks) { + msg( + "no memory for links, they will be dumped as separate files"); + nolinks++; + } + } + lp->ino = hstat.st_ino; + lp->dev = hstat.st_dev; + strcpy(lp->name, p); + lp->next = linklist; + linklist = lp; + } + + /* + * This is not a link to a previously dumped file, so dump it. + */ + switch (hstat.st_mode & S_IFMT) { + + case S_IFREG: /* Regular file */ +#ifdef S_IFCTG + case S_IFCTG: /* Contiguous file */ +#endif + { + int f; /* File descriptor */ + long bufsize, count; + long sizeleft; + register union record *start; + int header_moved; + char isextended = 0; + int upperbound; + int end_nulls = 0; + + header_moved = 0; + +#ifdef BSD42 + if (f_sparse_files) { + /* + * JK - This is the test for sparseness: whether the + * "size" of the file matches the number of blocks + * allocated for it. If there is a smaller number + * of blocks that would be necessary to accommodate + * a file of this size, we have a sparse file, i.e., + * at least one of those records in the file is just + * a useless hole. + */ +#ifdef hpux /* Nice of HPUX to gratuitiously change it, huh? - mib */ + if (hstat.st_size - (hstat.st_blocks * 1024) > 1024 ) { +#else + if (hstat.st_size - (hstat.st_blocks * RECORDSIZE) > RECORDSIZE) { +#endif + int filesize = hstat.st_size; + register int i; + + header = start_header(p, &hstat); + if (header == NULL) + goto badfile; + header->header.linkflag = LF_SPARSE; + header_moved++; + + /* + * Call the routine that figures out the + * layout of the sparse file in question. + * UPPERBOUND is the index of the last + * element of the "sparsearray," i.e., + * the number of elements it needed to + * describe the file. + */ + + upperbound = deal_with_sparse(p, header); + + /* + * See if we'll need an extended header + * later + */ + if (upperbound > SPARSE_IN_HDR-1) + header->header.isextended++; + /* + * We store the "real" file size so + * we can show that in case someone wants + * to list the archive, i.e., tar tvf . + * It might be kind of disconcerting if the + * shrunken file size was the one that showed + * up. + */ + to_oct((long) hstat.st_size, 1+12, + header->header.realsize); + + /* + * This will be the new "size" of the + * file, i.e., the size of the file + * minus the records of holes that we're + * skipping over. + */ + + find_new_file_size(&filesize, upperbound); + hstat.st_size = filesize; + to_oct((long) filesize, 1+12, + header->header.size); +/* to_oct((long) end_nulls, 1+12, + header->header.ending_blanks);*/ + + for (i = 0; i < SPARSE_IN_HDR; i++) { + if (!sparsearray[i].numbytes) + break; + to_oct(sparsearray[i].offset, 1+12, + header->header.sp[i].offset); + to_oct(sparsearray[i].numbytes, 1+12, + header->header.sp[i].numbytes); + } + + } + } +#else + upperbound=SPARSE_IN_HDR-1; +#endif + + sizeleft = hstat.st_size; + /* Don't bother opening empty, world readable files. */ + if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode)) { + f = open(p, O_RDONLY|O_BINARY); + if (f < 0) goto badperror; + } else { + f = -1; + } + + /* If the file is sparse, we've already taken care of this */ + if (!header_moved) { + header = start_header(p, &hstat); + if (header == NULL) { + if(f>=0) + (void)close(f); + goto badfile; + } + } +#ifdef S_IFCTG + /* Mark contiguous files, if we support them */ + if (f_standard && (hstat.st_mode & S_IFMT) == S_IFCTG) { + header->header.linkflag = LF_CONTIG; + } +#endif + isextended = header->header.isextended; + save_linkflag = header->header.linkflag; + finish_header(header); + if (isextended) { + int sum = 0; + register int i; +/* register union record *exhdr;*/ + int arraybound = SPARSE_EXT_HDR; + /* static */ int index_offset = SPARSE_IN_HDR; + + extend: exhdr = findrec(); + + if (exhdr == NULL) goto badfile; + bzero(exhdr->charptr, RECORDSIZE); + for (i = 0; i < SPARSE_EXT_HDR; i++) { + if (i+index_offset > upperbound) + break; + to_oct((long) sparsearray[i+index_offset].numbytes, + 1+12, + exhdr->ext_hdr.sp[i].numbytes); + to_oct((long) sparsearray[i+index_offset].offset, + 1+12, + exhdr->ext_hdr.sp[i].offset); + } + userec(exhdr); +/* sum += i; + if (sum < upperbound) + goto extend;*/ + if (index_offset+i < upperbound) { + index_offset += i; + exhdr->ext_hdr.isextended++; + goto extend; + } + + } + if (save_linkflag == LF_SPARSE) { + if (finish_sparse_file(f, &sizeleft, hstat.st_size, p)) + goto padit; + } + else + while (sizeleft > 0) { + + if(f_multivol) { + save_name = p; + save_sizeleft = sizeleft; + save_totsize = hstat.st_size; + } + start = findrec(); + + bufsize = endofrecs()->charptr - start->charptr; + + if (sizeleft < bufsize) { + /* Last read -- zero out area beyond */ + bufsize = (int)sizeleft; + count = bufsize % RECORDSIZE; + if (count) + bzero(start->charptr + sizeleft, + (int)(RECORDSIZE - count)); + } + count = read(f, start->charptr, bufsize); + if (count < 0) { + msg_perror("read error at byte %ld, reading\ + %d bytes, in file %s", hstat.st_size - sizeleft, bufsize,p); + goto padit; + } + sizeleft -= count; + + /* This is nonportable (the type of userec's arg). */ + userec(start+(count-1)/RECORDSIZE); + + if (count == bufsize) continue; + msg( "file %s shrunk by %d bytes, padding with zeros.", p, sizeleft); + goto padit; /* Short read */ + } + + if(f_multivol) + save_name = 0; + + if (f >= 0) + (void)close(f); + + break; + + /* + * File shrunk or gave error, pad out tape to match + * the size we specified in the header. + */ + padit: + while(sizeleft>0) { + save_sizeleft=sizeleft; + start=findrec(); + bzero(start->charptr,RECORDSIZE); + userec(start); + sizeleft-=RECORDSIZE; + } + if(f_multivol) + save_name=0; + if(f>=0) + (void)close(f); + break; +/* abort(); */ + } + +#ifdef S_IFLNK + case S_IFLNK: /* Symbolic link */ + { + int size; + + hstat.st_size = 0; /* Force 0 size on symlink */ + header = start_header(p, &hstat); + if (header == NULL) goto badfile; + size = readlink(p, header->header.linkname, NAMSIZ); + if (size < 0) goto badperror; + if (size == NAMSIZ) { + char buf[MAXPATHLEN]; + + readlink(p,buf,MAXPATHLEN); + /* next_mangle(header->header.linkname); */ + add_symlink_mangle(buf,p,header->header.linkname); + msg("symbolic link %s too long: mangling to %s",p, header->header.linkname); + /* size=strlen(header->header.linkname); */ + } else + header->header.linkname[size] = '\0'; + header->header.linkflag = LF_SYMLINK; + finish_header(header); /* Nothing more to do to it */ + } + break; +#endif + + case S_IFDIR: /* Directory */ + { + register DIR *dirp; + register struct direct *d; + char *namebuf; + int buflen; + register int len; + int our_device = hstat.st_dev; + extern char *ck_malloc(),*ck_realloc(); + + /* Build new prototype name */ + len = strlen(p); + buflen=len+NAMSIZ; + namebuf=ck_malloc(buflen+1); + strncpy(namebuf, p, buflen); + while (len >= 1 && '/' == namebuf[len-1]) + len--; /* Delete trailing slashes */ + namebuf[len++] = '/'; /* Now add exactly one back */ + namebuf[len] = '\0'; /* Make sure null-terminated */ + + /* + * Output directory header record with permissions + * FIXME, do this AFTER files, to avoid R/O dir problems? + * If old archive format, don't write record at all. + */ + if (!f_oldarch) { + hstat.st_size = 0; /* Force 0 size on dir */ + /* + * If people could really read standard archives, + * this should be: (FIXME) + header = start_header(f_standard? p: namebuf, &hstat); + * but since they'd interpret LF_DIR records as + * regular files, we'd better put the / on the name. + */ + header = start_header(namebuf, &hstat); + if (header == NULL) + goto badfile; /* eg name too long */ + + if (f_gnudump) + header->header.linkflag = LF_DUMPDIR; + else if (f_standard) + header->header.linkflag = LF_DIR; + + /* If we're gnudumping, we aren't done yet so don't close it. */ + if(!f_gnudump) + finish_header(header); /* Done with directory header */ + } + + if(f_gnudump) { + int sizeleft; + int totsize; + int bufsize; + union record *start; + int count; + char *buf,*p_buf; + + buf=gnu_list_name->dir_contents; /* FOO */ + totsize=0; + for(p_buf=buf;p_buf && *p_buf;) { + int tmp; + + tmp=strlen(p_buf)+1; + totsize+=tmp; + p_buf+=tmp; + } + totsize++; + to_oct((long)totsize,1+12,header->header.size); + finish_header(header); + p_buf=buf; + sizeleft=totsize; + while(sizeleft>0) { + if(f_multivol) { + save_name=p; + save_sizeleft=sizeleft; + save_totsize=totsize; + } + start=findrec(); + bufsize=endofrecs()->charptr - start->charptr; + if(sizeleftcharptr+sizeleft,RECORDSIZE-count); + } + bcopy(p_buf,start->charptr,bufsize); + sizeleft-=bufsize; + p_buf+=bufsize; + userec(start+(bufsize-1)/RECORDSIZE); + } + if(f_multivol) + save_name = 0; + break; + } + + /* Now output all the files in the directory */ + /* if (f_dironly) + break; /* Unless the cmdline said not to */ + /* + * See if we are crossing from one file system to another, + * and avoid doing so if the user only wants to dump one file system. + */ + if (f_local_filesys && curdev >= 0 && curdev != hstat.st_dev) { + if(f_verbose) + msg("%s: is on a different filesystem; not dumped",p); + break; + } + + + errno = 0; + dirp = opendir(p); + if (!dirp) { + if (errno) { + msg_perror ("can't open directory %s",p); + } else { + msg("error opening directory %s", + p); + } + break; + } + + /* Hack to remove "./" from the front of all the file names */ + if (len == 2 && namebuf[0] == '.' && namebuf[1]=='/') + len = 0; + + /* Should speed this up by cd-ing into the dir, FIXME */ + while (NULL != (d=readdir(dirp))) { + /* Skip . and .. */ + if(is_dot_or_dotdot(d->d_name)) + continue; + + if (DP_NAMELEN(d) + len >= buflen) { + buflen=len+DP_NAMELEN(d); + namebuf=ck_realloc(namebuf,buflen+1); + /* namebuf[len]='\0'; + msg("file name %s%s too long", + namebuf, d->d_name); + continue; */ + } + strcpy(namebuf+len, d->d_name); + if(f_exclude && check_exclude(namebuf)) + continue; + dump_file(namebuf, our_device); + } + + closedir(dirp); + free(namebuf); + } + break; + +#ifdef S_IFCHR + case S_IFCHR: /* Character special file */ + type = LF_CHR; + goto easy; +#endif + +#ifdef S_IFBLK + case S_IFBLK: /* Block special file */ + type = LF_BLK; + goto easy; +#endif + +/* Avoid screwy apollo lossage where S_IFIFO == S_IFSOCK */ +#if ((_ISP__M68K == 0) && (_ISP__A88K == 0)) +#ifdef S_IFIFO + case S_IFIFO: /* Fifo special file */ + + type = LF_FIFO; + goto easy; +#endif +#endif + +#ifdef S_IFSOCK + case S_IFSOCK: /* Socket pretend its a fifo? */ + type = LF_FIFO; + goto easy; +#endif + + easy: + if (!f_standard) goto unknown; + + hstat.st_size = 0; /* Force 0 size */ + header = start_header(p, &hstat); + if (header == NULL) goto badfile; /* eg name too long */ + + header->header.linkflag = type; + if (type != LF_FIFO) { + to_oct((long) major(hstat.st_rdev), 8, + header->header.devmajor); + to_oct((long) minor(hstat.st_rdev), 8, + header->header.devminor); + } + + finish_header(header); + break; + + default: + unknown: + msg("%s: Unknown file type; file ignored.", p); + break; + } +} + +int +finish_sparse_file(fd, sizeleft, fullsize, name) + int fd; + long *sizeleft, + fullsize; + char *name; +{ + union record *start; + char tempbuf[RECORDSIZE]; + int bufsize, + sparse_ind = 0, + count; + long pos; + long nwritten = 0; + + + while (*sizeleft > 0) { + start = findrec(); + bzero(start->charptr, RECORDSIZE); + bufsize = sparsearray[sparse_ind].numbytes; + if (!bufsize) { /* we blew it, maybe */ + msg("Wrote %ld of %ld bytes to file %s", + fullsize - *sizeleft, fullsize, name); + break; + } + pos = lseek(fd, sparsearray[sparse_ind++].offset, 0); + /* + * If the number of bytes to be written here exceeds + * the size of the temporary buffer, do it in steps. + */ + while (bufsize > RECORDSIZE) { +/* if (amt_read) { + count = read(fd, start->charptr+amt_read, RECORDSIZE-amt_read); + bufsize -= RECORDSIZE - amt_read; + amt_read = 0; + userec(start); + start = findrec(); + bzero(start->charptr, RECORDSIZE); + }*/ + /* store the data */ + count = read(fd, start->charptr, RECORDSIZE); + if (count < 0) { + msg_perror("read error at byte %ld, reading %d bytes, in file %s", + fullsize - *sizeleft, bufsize, name); + return 1; + } + bufsize -= count; + *sizeleft -= count; + userec(start); + nwritten += RECORDSIZE; /* XXX */ + start = findrec(); + bzero(start->charptr, RECORDSIZE); + } + + + clear_buffer(tempbuf); + count = read(fd, tempbuf, bufsize); + bcopy(tempbuf, start->charptr, RECORDSIZE); + if (count < 0) { + msg_perror("read error at byte %ld, reading %d bytes, in file %s", + fullsize - *sizeleft, bufsize, name); + return 1; + } +/* if (amt_read >= RECORDSIZE) { + amt_read = 0; + userec(start+(count-1)/RECORDSIZE); + if (count != bufsize) { + msg("file %s shrunk by %d bytes, padding with zeros.", name, sizeleft); + return 1; + } + start = findrec(); + } else + amt_read += bufsize;*/ + nwritten += count; /* XXX */ + *sizeleft -= count; + userec(start); + + } + free(sparsearray); + printf ("Amount actually written is (I hope) %d.\n", nwritten); +/* userec(start+(count-1)/RECORDSIZE);*/ + return 0; + +} + +init_sparsearray() +{ + register int i; + + sp_array_size = 10; + /* + * Make room for our scratch space -- initially is 10 elts long + */ + sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array)); + for (i = 0; i < sp_array_size; i++) { + sparsearray[i].offset = 0; + sparsearray[i].numbytes = 0; + } +} + + + +/* + * Okay, we've got a sparse file on our hands -- now, what we need to do is + * make a pass through the file and carefully note where any data is, i.e., + * we want to find how far into the file each instance of data is, and how + * many bytes are there. We store this information in the sparsearray, + * which will later be translated into header information. For now, we use + * the sparsearray as convenient storage. + * + * As a side note, this routine is a mess. If I could have found a cleaner + * way to do it, I would have. If anyone wants to find a nicer way to do + * this, feel free. + */ + +/* There is little point in trimming small amounts of null data at the */ + /* head and tail of blocks -- it's ok if we only avoid dumping blocks */ + /* of complete null data */ +int +deal_with_sparse(name, header, nulls_at_end) + char *name; + union record *header; + +{ + long numbytes = 0; + long offset = 0; + long save_offset; + int fd; + int current_size = hstat.st_size; + int sparse_ind = 0, + cc; + char buf[RECORDSIZE]; + int read_last_data = 0; /* did we just read the last record? */ + int amidst_data = 0; + + header->header.isextended = 0; + /* + * Can't open the file -- this problem will be caught later on, + * so just return. + */ + if ((fd = open(name, O_RDONLY)) < 0) + return 0; + + init_sparsearray(); + clear_buffer(buf); + + while ((cc = read(fd, buf, sizeof buf)) != 0) { + + if (sparse_ind > sp_array_size-1) { + + /* + * realloc the scratch area, since we've run out of room -- + */ + sparsearray = (struct sp_array *) + realloc(sparsearray, + 2 * sp_array_size * (sizeof(struct sp_array))); + sp_array_size *= 2; + } + if (cc == sizeof buf) { + if (zero_record(buf)) { + if (amidst_data) { + sparsearray[sparse_ind++].numbytes + = numbytes; + amidst_data = 0; + } + } else { /* !zero_record(buf) */ + if (amidst_data) + numbytes += cc; + else { + amidst_data = 1; + numbytes = cc; + sparsearray[sparse_ind].offset + = offset; + } + } + } else if (cc < sizeof buf) { + /* This has to be the last bit of the file, so this */ + /* is somewhat shorter than the above. */ + if (!zero_record(buf)) { + if (!amidst_data) { + amidst_data = 1; + numbytes = cc; + sparsearray[sparse_ind].offset + = offset; + } else + numbytes += cc; + } + } + offset += cc; + clear_buffer(buf); + } + if (amidst_data) + sparsearray[sparse_ind++].numbytes = numbytes; + close(fd); + + return sparse_ind - 1; +} + +/* + * Just zeroes out the buffer so we don't confuse ourselves with leftover + * data. + */ +clear_buffer(buf) + char *buf; +{ + register int i; + + for (i = 0; i < RECORDSIZE; i++) + buf[i] = '\0'; +} + +#if 0 /* I'm leaving this as a monument to Joy Kendall, who wrote it */ +/* + * JK - + * This routine takes a character array, and tells where within that array + * the data can be found. It skips over any zeros, and sets the first + * non-zero point in the array to be the "start", and continues until it + * finds non-data again, which is marked as the "end." This routine is + * mainly for 1) seeing how far into a file we must lseek to data, given + * that we have a sparse file, and 2) determining the "real size" of the + * file, i.e., the number of bytes in the sparse file that are data, as + * opposed to the zeros we are trying to skip. + */ +where_is_data(from, to, buffer) + int *from, + *to; + char *buffer; +{ + register int i = 0; + register int save_to = *to; + int amidst_data = 0; + + + while (!buffer[i]) + i++; + *from = i; + + if (*from < 16) /* don't bother */ + *from = 0; + /* keep going to make sure there isn't more real + data in this record */ + while (i < RECORDSIZE) { + if (!buffer[i]) { + if (amidst_data) { + save_to = i; + amidst_data = 0; + } + i++; + } + else if (buffer[i]) { + if (!amidst_data) + amidst_data = 1; + i++; + } + } + if (i == RECORDSIZE) + *to = i; + else + *to = save_to; + +} +#endif + +/* Note that this routine is only called if zero_record returned true */ +#if 0 /* But we actually don't need it at all. */ +where_is_data (from, to, buffer) + int *from, *to; + char *buffer; +{ + char *fp, *tp; + + for (fp = buffer; ! *fp; fp++) + ; + for (tp = buffer + RECORDSIZE - 1; ! *tp; tp--) + ; + *from = fp - buffer; + *to = tp - buffer + 1; +} +#endif + + + +/* + * Takes a recordful of data and basically cruises through it to see if + * it's made *entirely* of zeros, returning a 0 the instant it finds + * something that is a non-zero, i.e., useful data. + */ +zero_record(buffer) + char *buffer; +{ + register int i; + + for (i = 0; i < RECORDSIZE; i++) + if (buffer[i] != '\000') + return 0; + return 1; +} + +find_new_file_size(filesize, highest_index) + int *filesize; + int highest_index; +{ + register int i; + + *filesize = 0; + for (i = 0; sparsearray[i].numbytes && i <= highest_index; i++) + *filesize += sparsearray[i].numbytes; +} + +/* + * Make a header block for the file name whose stat info is st . + * Return header pointer for success, NULL if the name is too long. + */ +union record * +start_header(name, st) + char *name; + register struct stat *st; +{ + register union record *header; + + header = (union record *) findrec(); + bzero(header->charptr, sizeof(*header)); /* XXX speed up */ + + /* + * Check the file name and put it in the record. + */ + if(!f_absolute_paths) { + static int warned_once = 0; +#ifdef __MSDOS__ + if(name[1]==':') { + name+=2; + if(!warned_once++) + msg("Removing drive spec from names in the archive"); + } +#endif + while ('/' == *name) { + name++; /* Force relative path */ + if (!warned_once++) + msg("Removing leading / from absolute path names in the archive."); + } + } + strncpy(header->header.name, name, NAMSIZ); + if (header->header.name[NAMSIZ-1]) { + char *mangled; + + /* next_mangle(header->header.name); */ + add_mangle(name,header->header.name); + msg("%s: is too long: mangling to %s", name, header->header.name); + } + + to_oct((long) (st->st_mode & ~S_IFMT), + 8, header->header.mode); + to_oct((long) st->st_uid, 8, header->header.uid); + to_oct((long) st->st_gid, 8, header->header.gid); + to_oct((long) st->st_size, 1+12, header->header.size); + to_oct((long) st->st_mtime, 1+12, header->header.mtime); + /* header->header.linkflag is left as null */ + if(f_gnudump) { + to_oct((long) st->st_atime, 1+12, header->header.atime); + to_oct((long) st->st_ctime, 1+12, header->header.ctime); + } + +#ifndef NONAMES + /* Fill in new Unix Standard fields if desired. */ + if (f_standard) { + header->header.linkflag = LF_NORMAL; /* New default */ + strcpy(header->header.magic, TMAGIC); /* Mark as Unix Std */ + finduname(header->header.uname, st->st_uid); + findgname(header->header.gname, st->st_gid); + } +#endif + return header; +} + +/* + * Finish off a filled-in header block and write it out. + * We also print the file name and/or full info if verbose is on. + */ +void +finish_header(header) + register union record *header; +{ + register int i, sum; + register char *p; + void bcopy(); + + bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum)); + + sum = 0; + p = header->charptr; + for (i = sizeof(*header); --i >= 0; ) { + /* + * We can't use unsigned char here because of old compilers, + * e.g. V7. + */ + sum += 0xFF & *p++; + } + + /* + * Fill in the checksum field. It's formatted differently + * from the other fields: it has [6] digits, a null, then a + * space -- rather than digits, a space, then a null. + * We use to_oct then write the null in over to_oct's space. + * The final space is already there, from checksumming, and + * to_oct doesn't modify it. + * + * This is a fast way to do: + * (void) sprintf(header->header.chksum, "%6o", sum); + */ + to_oct((long) sum, 8, header->header.chksum); + header->header.chksum[6] = '\0'; /* Zap the space */ + + userec(header); + + if (f_verbose) { + extern union record *head; /* Points to current tape header */ + extern int head_standard; /* Tape header is in ANSI format */ + + /* These globals are parameters to print_header, sigh */ + head = header; + /* hstat is already set up */ + head_standard = f_standard; + print_header(); + } + + return; +} + + +/* + * Quick and dirty octal conversion. + * Converts long "value" into a "digs"-digit field at "where", + * including a trailing space and room for a null. "digs"==3 means + * 1 digit, a space, and room for a null. + * + * We assume the trailing null is already there and don't fill it in. + * This fact is used by start_header and finish_header, so don't change it! + * + * This should be equivalent to: + * (void) sprintf(where, "%*lo ", digs-2, value); + * except that sprintf fills in the trailing null and we don't. + */ +void +to_oct(value, digs, where) + register long value; + register int digs; + register char *where; +{ + + --digs; /* Trailing null slot is left alone */ + where[--digs] = ' '; /* Put in the space, though */ + + /* Produce the digits -- at least one */ + do { + where[--digs] = '0' + (char)(value & 7); /* one octal digit */ + value >>= 3; + } while (digs > 0 && value != 0); + + /* Leading spaces, if necessary */ + while (digs > 0) + where[--digs] = ' '; + +} + + +/* + * Write the EOT record(s). + * We actually zero at least one record, through the end of the block. + * Old tar writes garbage after two zeroed records -- and PDtar used to. + */ +write_eot() +{ + union record *p; + int bufsize; + void bzero(); + + p = findrec(); + if (p) + { + bufsize = endofrecs()->charptr - p->charptr; + bzero(p->charptr, bufsize); + userec(p); + } +} diff --git a/usr/src/usr.bin/tar/diffarch.c b/usr/src/usr.bin/tar/diffarch.c new file mode 100644 index 0000000000..64273f0702 --- /dev/null +++ b/usr/src/usr.bin/tar/diffarch.c @@ -0,0 +1,686 @@ +/* Diff files from a tar archive. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Diff files from a tar archive. + * + * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu. + * + * @(#) diffarch.c 1.10 87/11/11 - gnu + */ + +#include +#include +#include +#include + +#ifdef BSD42 +#include +#endif + +#ifndef MSDOS +#include +#if !defined(USG) || defined(HAVE_MTIO) +#include +#endif +#endif + +#ifdef USG +#include +#endif + +/* Some systems don't have these #define's -- we fake it here. */ +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif +#ifndef O_NDELAY +#define O_NDELAY 0 +#endif + +#ifndef S_IFLNK +#define lstat stat +#endif + +extern int errno; /* From libc.a */ +extern char *valloc(); /* From libc.a */ + +#include "tar.h" +#include "port.h" +#include "rmt.h" + +extern union record *head; /* Points to current tape header */ +extern struct stat hstat; /* Stat struct corresponding */ +extern int head_standard; /* Tape header is in ANSI format */ + +extern void print_header(); +extern void skip_file(); +extern void skip_extended_headers(); + +extern FILE *msg_file; + +int now_verifying = 0; /* Are we verifying at the moment? */ + +int diff_fd; /* Descriptor of file we're diffing */ + +char *diff_buf = 0; /* Pointer to area for reading + file contents into */ + +char *diff_dir; /* Directory contents for LF_DUMPDIR */ + +int different = 0; + +/*struct sp_array *sparsearray; +int sp_ar_size = 10;*/ +/* + * Initialize for a diff operation + */ +diff_init() +{ + + /*NOSTRICT*/ + diff_buf = (char *) valloc((unsigned)blocksize); + if (!diff_buf) { + msg("could not allocate memory for diff buffer of %d bytes", + blocksize); + exit(EX_ARGSBAD); + } +} + +/* + * Diff a file against the archive. + */ +void +diff_archive() +{ + register char *data; + int check, namelen; + int err; + long offset; + struct stat filestat; + int compare_chunk(); + int compare_dir(); +#ifndef __MSDOS__ + dev_t dev; + ino_t ino; +#endif + char *get_dir_contents(); + long from_oct(); + long lseek(); + + errno = EPIPE; /* FIXME, remove perrors */ + + saverec(&head); /* Make sure it sticks around */ + userec(head); /* And go past it in the archive */ + decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */ + + /* Print the record from 'head' and 'hstat' */ + if (f_verbose) { + if(now_verifying) + fprintf(msg_file,"Verify "); + print_header(); + } + + switch (head->header.linkflag) { + + default: + msg("Unknown file type '%c' for %s, diffed as normal file", + head->header.linkflag, head->header.name); + /* FALL THRU */ + + case LF_OLDNORMAL: + case LF_NORMAL: + case LF_SPARSE: + case LF_CONTIG: + /* + * Appears to be a file. + * See if it's really a directory. + */ + namelen = strlen(head->header.name)-1; + if (head->header.name[namelen] == '/') + goto really_dir; + + + if(do_stat(&filestat)) { + if (head->header.isextended) + skip_extended_headers(); + skip_file((long)hstat.st_size); + different++; + goto quit; + } + + if ((filestat.st_mode & S_IFMT) != S_IFREG) { + fprintf(msg_file, "%s: not a regular file\n", + head->header.name); + skip_file((long)hstat.st_size); + different++; + goto quit; + } + + filestat.st_mode &= ~S_IFMT; + if (filestat.st_mode != hstat.st_mode) + sigh("mode"); + if (filestat.st_uid != hstat.st_uid) + sigh("uid"); + if (filestat.st_gid != hstat.st_gid) + sigh("gid"); + if (filestat.st_mtime != hstat.st_mtime) + sigh("mod time"); + if (head->header.linkflag != LF_SPARSE && + filestat.st_size != hstat.st_size) { + sigh("size"); + skip_file((long)hstat.st_size); + goto quit; + } + + diff_fd = open(head->header.name, O_NDELAY|O_RDONLY); + + if (diff_fd < 0 && !f_absolute_paths) { + char tmpbuf[NAMSIZ+2]; + + tmpbuf[0]='/'; + strcpy(&tmpbuf[1],head->header.name); + diff_fd=open(tmpbuf, O_NDELAY|O_RDONLY); + } + if (diff_fd < 0) { + msg_perror("cannot open %s",head->header.name); + if (head->header.isextended) + skip_extended_headers(); + skip_file((long)hstat.st_size); + different++; + goto quit; + } + /* + * Need to treat sparse files completely differently here. + */ + if (head->header.linkflag == LF_SPARSE) + diff_sparse_files(hstat.st_size); + else + wantbytes((long)(hstat.st_size),compare_chunk); + + check = close(diff_fd); + if (check < 0) + msg_perror("Error while closing %s",head->header.name); + + quit: + break; + +#ifndef __MSDOS__ + case LF_LINK: + if(do_stat(&filestat)) + break; + dev = filestat.st_dev; + ino = filestat.st_ino; + err = stat(head->header.linkname, &filestat); + if (err < 0) { + if (errno==ENOENT) { + fprintf(msg_file, "%s: does not exist\n",head->header.name); + } else { + msg_perror("cannot stat file %s",head->header.name); + } + different++; + break; + } + if(filestat.st_dev!=dev || filestat.st_ino!=ino) { + fprintf(msg_file, "%s not linked to %s\n",head->header.name,head->header.linkname); + break; + } + break; +#endif + +#ifdef S_IFLNK + case LF_SYMLINK: + { + char linkbuf[NAMSIZ+3]; + check = readlink(head->header.name, linkbuf, + (sizeof linkbuf)-1); + + if (check < 0) { + if (errno == ENOENT) { + fprintf(msg_file, + "%s: no such file or directory\n", + head->header.name); + } else { + msg_perror("cannot read link %s",head->header.name); + } + different++; + break; + } + + linkbuf[check] = '\0'; /* Null-terminate it */ + if (strncmp(head->header.linkname, linkbuf, check) != 0) { + fprintf(msg_file, "%s: symlink differs\n", + head->header.linkname); + different++; + } + } + break; +#endif + + case LF_CHR: + hstat.st_mode |= S_IFCHR; + goto check_node; + +#ifdef S_IFBLK + /* If local system doesn't support block devices, use default case */ + case LF_BLK: + hstat.st_mode |= S_IFBLK; + goto check_node; +#endif + +#ifdef S_IFIFO + /* If local system doesn't support FIFOs, use default case */ + case LF_FIFO: + hstat.st_mode |= S_IFIFO; + hstat.st_rdev = 0; /* FIXME, do we need this? */ + goto check_node; +#endif + + check_node: + /* FIXME, deal with umask */ + if(do_stat(&filestat)) + break; + if(hstat.st_rdev != filestat.st_rdev) { + fprintf(msg_file, "%s: device numbers changed\n", head->header.name); + different++; + break; + } + if(hstat.st_mode != filestat.st_mode) { + fprintf(msg_file, "%s: mode or device-type changed\n", head->header.name); + different++; + break; + } + break; + + case LF_DUMPDIR: + data=diff_dir=get_dir_contents(head->header.name,0); + wantbytes((long)(hstat.st_size),compare_dir); + free(data); + /* FALL THROUGH */ + + case LF_DIR: + /* Check for trailing / */ + namelen = strlen(head->header.name)-1; + really_dir: + while (namelen && head->header.name[namelen] == '/') + head->header.name[namelen--] = '\0'; /* Zap / */ + + if(do_stat(&filestat)) + break; + if((filestat.st_mode&S_IFMT)!=S_IFDIR) { + fprintf(msg_file, "%s is no longer a directory\n",head->header.name); + different++; + break; + } + if((filestat.st_mode&~S_IFMT) != hstat.st_mode) + sigh("mode"); + break; + + case LF_VOLHDR: + break; + + case LF_MULTIVOL: + namelen = strlen(head->header.name)-1; + if (head->header.name[namelen] == '/') + goto really_dir; + + if(do_stat(&filestat)) + break; + + if ((filestat.st_mode & S_IFMT) != S_IFREG) { + fprintf(msg_file, "%s: not a regular file\n", + head->header.name); + skip_file((long)hstat.st_size); + different++; + break; + } + + filestat.st_mode &= ~S_IFMT; + offset = from_oct(1+12, head->header.offset); + if (filestat.st_size != hstat.st_size + offset) { + sigh("size"); + skip_file((long)hstat.st_size); + different++; + break; + } + + diff_fd = open(head->header.name, O_NDELAY|O_RDONLY); + + if (diff_fd < 0) { + msg_perror("cannot open file %s",head->header.name); + skip_file((long)hstat.st_size); + different++; + break; + } + err = lseek(diff_fd, offset, 0); + if(err!=offset) { + msg_perror("cannot seek to %ld in file %s",offset,head->header.name); + different++; + break; + } + + wantbytes((long)(hstat.st_size),compare_chunk); + + check = close(diff_fd); + if (check < 0) { + msg_perror("Error while closing %s",head->header.name); + } + break; + + } + + /* We don't need to save it any longer. */ + saverec((union record **) 0); /* Unsave it */ +} + +int +compare_chunk(bytes,buffer) +long bytes; +char *buffer; +{ + int err; + + err=read(diff_fd,diff_buf,bytes); + if(err!=bytes) { + if(err<0) { + msg_perror("can't read %s",head->header.name); + } else { + fprintf(msg_file,"%s: could only read %d of %d bytes\n",head->header.name,err,bytes); + } + different++; + return -1; + } + if(bcmp(buffer,diff_buf,bytes)) { + fprintf(msg_file, "%s: data differs\n",head->header.name); + different++; + return -1; + } + return 0; +} + +int +compare_dir(bytes,buffer) +long bytes; +char *buffer; +{ + if(bcmp(buffer,diff_dir,bytes)) { + fprintf(msg_file, "%s: data differs\n",head->header.name); + different++; + return -1; + } + diff_dir+=bytes; + return 0; +} + +/* + * Sigh about something that differs. + */ +sigh(what) + char *what; +{ + + fprintf(msg_file, "%s: %s differs\n", + head->header.name, what); +} + +verify_volume() +{ + int status; +#ifdef MTIOCTOP + struct mtop t; + int er; +#endif + + if(!diff_buf) + diff_init(); +#ifdef MTIOCTOP + t.mt_op = MTBSF; + t.mt_count = 1; + if((er=rmtioctl(archive,MTIOCTOP,&t))<0) { + if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) { +#endif + if(rmtlseek(archive,0L,0)!=0) { + /* Lseek failed. Try a different method */ + msg_perror("Couldn't rewind archive file for verify"); + return; + } +#ifdef MTIOCTOP + } + } +#endif + ar_reading=1; + now_verifying = 1; + fl_read(); + for(;;) { + status = read_header(); + if(status==0) { + unsigned n; + + n=0; + do { + n++; + status=read_header(); + } while(status==0); + msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s"); + } + if(status==2 || status==EOF) + break; + diff_archive(); + } + ar_reading=0; + now_verifying = 0; + +} + +int do_stat(statp) +struct stat *statp; +{ + int err; + + err = f_follow_links ? stat(head->header.name, statp) : lstat(head->header.name, statp); + if (err < 0) { + if (errno==ENOENT) { + fprintf(msg_file, "%s: does not exist\n",head->header.name); + } else + msg_perror("can't stat file %s",head->header.name); +/* skip_file((long)hstat.st_size); + different++;*/ + return 1; + } else + return 0; +} + +/* + * JK + * Diff'ing a sparse file with its counterpart on the tar file is a + * bit of a different story than a normal file. First, we must know + * what areas of the file to skip through, i.e., we need to contruct + * a sparsearray, which will hold all the information we need. We must + * compare small amounts of data at a time as we find it. + */ + +diff_sparse_files(filesize) +int filesize; + +{ + int sparse_ind = 0; + char *buf; + int buf_size = RECORDSIZE; + union record *datarec; + int err; + long numbytes; + int amt_read = 0; + int size = filesize; + + buf = (char *) malloc(buf_size * sizeof (char)); + + fill_in_sparse_array(); + + + while (size > 0) { + datarec = findrec(); + if (!sparsearray[sparse_ind].numbytes) + break; + + /* + * 'numbytes' is nicer to write than + * 'sparsearray[sparse_ind].numbytes' all the time ... + */ + numbytes = sparsearray[sparse_ind].numbytes; + + lseek(diff_fd, sparsearray[sparse_ind].offset, 0); + /* + * take care to not run out of room in our buffer + */ + while (buf_size < numbytes) { + buf = (char *) realloc(buf, buf_size * 2 * sizeof(char)); + buf_size *= 2; + } + while (numbytes > RECORDSIZE) { + if ((err = read(diff_fd, buf, RECORDSIZE)) != RECORDSIZE) { + if (err < 0) + msg_perror("can't read %s", head->header.name); + else + fprintf(msg_file, "%s: could only read %d of %d bytes\n", + err, numbytes); + break; + } + if (bcmp(buf, datarec->charptr, RECORDSIZE)) { + different++; + break; + } + numbytes -= err; + size -= err; + userec(datarec); + datarec = findrec(); + } + if ((err = read(diff_fd, buf, numbytes)) != numbytes) { + if (err < 0) + msg_perror("can't read %s", head->header.name); + else + fprintf(msg_file, "%s: could only read %d of %d bytes\n", + err, numbytes); + break; + } + + if (bcmp(buf, datarec->charptr, numbytes)) { + different++; + break; + } +/* amt_read += numbytes; + if (amt_read >= RECORDSIZE) { + amt_read = 0; + userec(datarec); + datarec = findrec(); + }*/ + userec(datarec); + sparse_ind++; + size -= numbytes; + } + /* + * if the number of bytes read isn't the + * number of bytes supposedly in the file, + * they're different + */ +/* if (amt_read != filesize) + different++;*/ + userec(datarec); + free(sparsearray); + if (different) + fprintf(msg_file, "%s: data differs\n", head->header.name); + +} + +/* + * JK + * This routine should be used more often than it is ... look into + * that. Anyhow, what it does is translate the sparse information + * on the header, and in any subsequent extended headers, into an + * array of structures with true numbers, as opposed to character + * strings. It simply makes our life much easier, doing so many + * comparisong and such. + */ +fill_in_sparse_array() +{ + int ind; + + /* + * allocate space for our scratch space; it's initially + * 10 elements long, but can change in this routine if + * necessary + */ + sp_array_size = 10; + sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array)); + + /* + * there are at most five of these structures in the header + * itself; read these in first + */ + for (ind = 0; ind < SPARSE_IN_HDR; ind++) { + if (!head->header.sp[ind].numbytes) + break; + sparsearray[ind].offset = + from_oct(1+12, head->header.sp[ind].offset); + sparsearray[ind].numbytes = + from_oct(1+12, head->header.sp[ind].numbytes); + } + /* + * if the header's extended, we gotta read in exhdr's till + * we're done + */ + if (head->header.isextended) { + /* how far into the sparsearray we are 'so far' */ + static int so_far_ind = SPARSE_IN_HDR; + union record *exhdr; + + for (;;) { + exhdr = findrec(); + for (ind = 0; ind < SPARSE_EXT_HDR; ind++) { + if (ind+so_far_ind > sp_array_size-1) { + /* + * we just ran out of room in our + * scratch area - realloc it + */ + sparsearray = (struct sp_array *) + realloc(sparsearray, + sp_array_size*2*sizeof(struct sp_array)); + sp_array_size *= 2; + } + /* + * convert the character strings into longs + */ + sparsearray[ind+so_far_ind].offset = + from_oct(1+12, exhdr->ext_hdr.sp[ind].offset); + sparsearray[ind+so_far_ind].numbytes = + from_oct(1+12, exhdr->ext_hdr.sp[ind].numbytes); + } + /* + * if this is the last extended header for this + * file, we can stop + */ + if (!exhdr->ext_hdr.isextended) + break; + else { + so_far_ind += SPARSE_EXT_HDR; + userec(exhdr); + } + } + /* be sure to skip past the last one */ + userec(exhdr); + } +} diff --git a/usr/src/usr.bin/tar/extract.c b/usr/src/usr.bin/tar/extract.c new file mode 100644 index 0000000000..bcd9bf07d3 --- /dev/null +++ b/usr/src/usr.bin/tar/extract.c @@ -0,0 +1,743 @@ +/* Extract files from a tar archive. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Extract files from a tar archive. + * + * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu. + * + * @(#) extract.c 1.32 87/11/11 - gnu + */ + +#include +#include +#include +#include + +#ifdef BSD42 +#include +#endif + +#ifdef USG +#include +#endif + +#ifdef MSDOS +#include +#endif /* MSDOS */ + +/* + * Some people don't have a #define for these. + */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#ifndef O_NDELAY +#define O_NDELAY 0 +#endif + +#ifdef NO_OPEN3 +/* We need the #define's even though we don't use them. */ +#include "open3.h" +#endif + +#ifdef EMUL_OPEN3 +/* Simulated 3-argument open for systems that don't have it */ +#include "open3.h" +#endif + +extern int errno; /* From libc.a */ +extern time_t time(); /* From libc.a */ +extern char *index(); /* From libc.a or port.c */ + +#include "tar.h" +#include "port.h" + +extern FILE *msg_file; + +extern union record *head; /* Points to current tape header */ +extern struct stat hstat; /* Stat struct corresponding */ +extern int head_standard; /* Tape header is in ANSI format */ + +extern char *save_name; +extern long save_totsize; +extern long save_sizeleft; + +extern void print_header(); +extern void skip_file(); +extern void skip_extended_headers(); +extern void pr_mkdir(); + +int make_dirs(); /* Makes required directories */ + +static time_t now = 0; /* Current time */ +static we_are_root = 0; /* True if our effective uid == 0 */ +static int notumask = ~0; /* Masks out bits user doesn't want */ + +/* + * "Scratch" space to store the information about a sparse file before + * writing the info into the header or extended header + */ +/*struct sp_array *sparsearray;*/ + +/* number of elts storable in the sparsearray */ +/*int sp_array_size = 10;*/ + +/* + * Set up to extract files. + */ +extr_init() +{ + int ourmask; + + now = time((time_t *)0); + if (geteuid() == 0) + we_are_root = 1; + + /* + * We need to know our umask. But if f_use_protection is set, + * leave our kernel umask at 0, and our "notumask" at ~0. + */ + ourmask = umask(0); /* Read it */ + if (!f_use_protection) { + (void) umask (ourmask); /* Set it back how it was */ + notumask = ~ourmask; /* Make umask override permissions */ + } +} + + +/* + * Extract a file from the archive. + */ +void +extract_archive() +{ + register char *data; + int fd, check, namelen, written, openflag; + long size; + time_t acc_upd_times[2]; + register int skipcrud; + register int i; + int sparse_ind = 0; + union record *exhdr; + int end_nulls; + + saverec(&head); /* Make sure it sticks around */ + userec(head); /* And go past it in the archive */ + decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */ + + if(f_confirm && !confirm("extract",head->header.name)) { + if (head->header.isextended) + skip_extended_headers(); + skip_file((long)hstat.st_size); + saverec((union record **)0); + return; + } + + /* Print the record from 'head' and 'hstat' */ + if (f_verbose) + print_header(); + + /* + * Check for fully specified pathnames and other atrocities. + * + * Note, we can't just make a pointer to the new file name, + * since saverec() might move the header and adjust "head". + * We have to start from "head" every time we want to touch + * the header record. + */ + skipcrud = 0; + while (!f_absolute_paths && '/' == head->header.name[skipcrud]) { + static int warned_once = 0; + + skipcrud++; /* Force relative path */ + if (!warned_once++) { + msg("Removing leading / from absolute path names in the archive."); + } + } + + switch (head->header.linkflag) { + + default: + msg("Unknown file type '%c' for %s, extracted as normal file", + head->header.linkflag, skipcrud+head->header.name); + /* FALL THRU */ + + /* + * JK - What we want to do if the file is sparse is loop through + * the array of sparse structures in the header and read in + * and translate the character strings representing 1) the offset + * at which to write and 2) how many bytes to write into numbers, + * which we store into the scratch array, "sparsearray". This + * array makes our life easier the same way it did in creating + * the tar file that had to deal with a sparse file. + * + * After we read in the first five (at most) sparse structures, + * we check to see if the file has an extended header, i.e., + * if more sparse structures are needed to describe the contents + * of the new file. If so, we read in the extended headers + * and continue to store their contents into the sparsearray. + */ + case LF_SPARSE: + sp_array_size = 10; + sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array)); + for (i = 0; i < SPARSE_IN_HDR; i++) { + sparsearray[i].offset = + from_oct(1+12, head->header.sp[i].offset); + sparsearray[i].numbytes = + from_oct(1+12, head->header.sp[i].numbytes); + if (!sparsearray[i].numbytes) + break; + } + +/* end_nulls = from_oct(1+12, head->header.ending_blanks);*/ + + if (head->header.isextended) { + /* read in the list of extended headers + and translate them into the sparsearray + as before */ + + /* static */ int ind = SPARSE_IN_HDR; + + for (;;) { + + exhdr = findrec(); + for (i = 0; i < SPARSE_EXT_HDR; i++) { + + if (i+ind > sp_array_size-1) { + /* + * realloc the scratch area + * since we've run out of room -- + */ + sparsearray = (struct sp_array *) + realloc(sparsearray, + 2 * sp_array_size * (sizeof(struct sp_array))); + sp_array_size *= 2; + } + if (!exhdr->ext_hdr.sp[i].numbytes) + break; + sparsearray[i+ind].offset = + from_oct(1+12, exhdr->ext_hdr.sp[i].offset); + sparsearray[i+ind].numbytes = + from_oct(1+12, exhdr->ext_hdr.sp[i].numbytes); + } + if (!exhdr->ext_hdr.isextended) + break; + else { + ind += SPARSE_EXT_HDR; + userec(exhdr); + } + } + userec(exhdr); + } + + /* FALL THRU */ + case LF_OLDNORMAL: + case LF_NORMAL: + case LF_CONTIG: + /* + * Appears to be a file. + * See if it's really a directory. + */ + namelen = strlen(skipcrud+head->header.name)-1; + if (head->header.name[skipcrud+namelen] == '/') + goto really_dir; + + /* FIXME, deal with protection issues */ + again_file: + openflag = (f_keep? + O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_EXCL: + O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_TRUNC) + | ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND); + /* + * JK - The last | is a kludge to solve the problem + * the O_APPEND flag causes with files we are + * trying to make sparse: when a file is opened + * with O_APPEND, it writes to the last place + * that something was written, thereby ignoring + * any lseeks that we have done. We add this + * extra condition to make it able to lseek when + * a file is sparse, i.e., we don't open the new + * file with this flag. (Grump -- this bug caused + * me to waste a good deal of time, I might add) + */ + + if(f_exstdout) { + fd = 1; + goto extract_file; + } +#ifdef O_CTG + /* + * Contiguous files (on the Masscomp) have to specify + * the size in the open call that creates them. + */ + if (head->header.linkflag == LF_CONTIG) + fd = open(skipcrud+head->header.name, openflag | O_CTG, + hstat.st_mode, hstat.st_size); + else +#endif + { +#ifdef NO_OPEN3 + /* + * On raw V7 we won't let them specify -k (f_keep), but + * we just bull ahead and create the files. + */ + fd = creat(skipcrud+head->header.name, + hstat.st_mode); +#else + /* + * With 3-arg open(), we can do this up right. + */ + fd = open(skipcrud+head->header.name, openflag, + hstat.st_mode); +#endif + } + + if (fd < 0) { + if (make_dirs(skipcrud+head->header.name)) + goto again_file; + msg_perror("Could not create file %s",skipcrud+head->header.name); + if (head->header.isextended) + skip_extended_headers(); + skip_file((long)hstat.st_size); + goto quit; + } + + extract_file: + if (head->header.linkflag == LF_SPARSE) { + char *name; + int namelen; + + /* + * Kludge alert. NAME is assigned to header.name + * because during the extraction, the space that + * contains the header will get scribbled on, and + * the name will get munged, so any error messages + * that happen to contain the filename will look + * REAL interesting unless we do this. + */ + namelen = strlen(skipcrud+head->header.name); + name = (char *) malloc((sizeof(char)) * namelen); + bcopy(skipcrud+head->header.name, name, namelen); + size = hstat.st_size; + extract_sparse_file(fd, &size, hstat.st_size, + name); + } + else + for (size = hstat.st_size; + size > 0; + size -= written) { + + long offset, + numbytes; + + if(f_multivol) { + save_name=head->header.name; + save_totsize=hstat.st_size; + save_sizeleft=size; + } + + /* + * Locate data, determine max length + * writeable, write it, record that + * we have used the data, then check + * if the write worked. + */ + data = findrec()->charptr; + if (data == NULL) { /* Check it... */ + msg("Unexpected EOF on archive file"); + break; + } + /* + * JK - If the file is sparse, use the sparsearray + * that we created before to lseek into the new + * file the proper amount, and to see how many + * bytes we want to write at that position. + */ +/* if (head->header.linkflag == LF_SPARSE) { + off_t pos; + + pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0); + printf("%d at %d\n", (int) pos, sparse_ind); + written = sparsearray[sparse_ind++].numbytes; + } else*/ + written = endofrecs()->charptr - data; + if (written > size) + written = size; + errno = 0; + check = write(fd, data, written); + /* + * The following is in violation of strict + * typing, since the arg to userec + * should be a struct rec *. FIXME. + */ + userec((union record *)(data + written - 1)); + if (check == written) continue; + /* + * Error in writing to file. + * Print it, skip to next file in archive. + */ + if(check<0) + msg_perror("couldn't write to file %s",skipcrud+head->header.name); + else + msg("could only write %d of %d bytes to file %s",written,check,skipcrud+head->header.name); + skip_file((long)(size - written)); + break; /* Still do the close, mod time, chmod, etc */ + } + + if(f_multivol) + save_name = 0; + + /* If writing to stdout, don't try to do anything + to the filename; it doesn't exist, or we don't + want to touch it anyway */ + if(f_exstdout) + break; + +/* if (head->header.isextended) { + register union record *exhdr; + register int i; + + for (i = 0; i < 21; i++) { + long offset; + + if (!exhdr->ext_hdr.sp[i].numbytes) + break; + offset = from_oct(1+12, + exhdr->ext_hdr.sp[i].offset); + written = from_oct(1+12, + exhdr->ext_hdr.sp[i].numbytes); + lseek(fd, offset, 0); + check = write(fd, data, written); + if (check == written) continue; + + } + + + }*/ + check = close(fd); + if (check < 0) { + msg_perror("Error while closing %s",skipcrud+head->header.name); + } + + + set_filestat: + + /* + * If we are root, set the owner and group of the extracted + * file. This does what is wanted both on real Unix and on + * System V. If we are running as a user, we extract as that + * user; if running as root, we extract as the original owner. + */ + if (we_are_root || f_do_chown) { + if (chown(skipcrud+head->header.name, hstat.st_uid, + hstat.st_gid) < 0) { + msg_perror("cannot chown file %s to uid %d gid %d",skipcrud+head->header.name,hstat.st_uid,hstat.st_gid); + } + } + + /* + * Set the modified time of the file. + * + * Note that we set the accessed time to "now", which + * is really "the time we started extracting files". + * unless f_gnudump is used, in which case .st_atime is used + */ + if (!f_modified) { + /* fixme if f_gnudump should set ctime too, but how? */ + if(f_gnudump) + acc_upd_times[0]=hstat.st_atime; + else acc_upd_times[0] = now; /* Accessed now */ + acc_upd_times[1] = hstat.st_mtime; /* Mod'd */ + if (utime(skipcrud+head->header.name, + acc_upd_times) < 0) { + msg_perror("couldn't change access and modification times of %s",skipcrud+head->header.name); + } + } + /* We do the utime before the chmod because some versions of + utime are broken and trash the modes of the file. Since + we then change the mode anyway, we don't care. . . */ + + /* + * If '-k' is not set, open() or creat() could have saved + * the permission bits from a previously created file, + * ignoring the ones we specified. + * Even if -k is set, if the file has abnormal + * mode bits, we must chmod since writing or chown() has + * probably reset them. + * + * If -k is set, we know *we* created this file, so the mode + * bits were set by our open(). If the file is "normal", we + * skip the chmod. This works because we did umask(0) if -p + * is set, so umask will have left the specified mode alone. + */ + if ((!f_keep) + || (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) { + if (chmod(skipcrud+head->header.name, + notumask & (int)hstat.st_mode) < 0) { + msg_perror("cannot change mode of file %s to %ld",skipcrud+head->header.name,notumask & (int)hstat.st_mode); + } + } + + quit: + break; + + case LF_LINK: + again_link: + { + struct stat st1,st2; + + check = link (head->header.linkname, + skipcrud+head->header.name); + if (check == 0) + break; + if (make_dirs(skipcrud+head->header.name)) + goto again_link; + if(f_gnudump && errno==EEXIST) + break; + if( stat(head->header.linkname, &st1) == 0 + && stat(skipcrud+head->header.name, &st2)==0 + && st1.st_dev==st2.st_dev + && st1.st_ino==st2.st_ino) + break; + msg_perror("Could not link %s to %s", + skipcrud+head->header.name,head->header.linkname); + } + break; + +#ifdef S_IFLNK + case LF_SYMLINK: + again_symlink: + check = symlink(head->header.linkname, + skipcrud+head->header.name); + /* FIXME, don't worry uid, gid, etc... */ + if (check == 0) + break; + if (make_dirs(skipcrud+head->header.name)) + goto again_symlink; + msg_perror("Could not create symlink to %s",head->header.linkname); + break; +#endif + +#ifdef S_IFCHR + case LF_CHR: + hstat.st_mode |= S_IFCHR; + goto make_node; +#endif + +#ifdef S_IFBLK + case LF_BLK: + hstat.st_mode |= S_IFBLK; + goto make_node; +#endif + +#ifdef S_IFIFO + /* If local system doesn't support FIFOs, use default case */ + case LF_FIFO: + hstat.st_mode |= S_IFIFO; + hstat.st_rdev = 0; /* FIXME, do we need this? */ + goto make_node; +#endif + + make_node: + check = mknod(skipcrud+head->header.name, + (int) hstat.st_mode, (int) hstat.st_rdev); + if (check != 0) { + if (make_dirs(skipcrud+head->header.name)) + goto make_node; + msg_perror("Could not make %s",skipcrud+head->header.name); + break; + }; + goto set_filestat; + + case LF_DIR: + case LF_DUMPDIR: + namelen = strlen(skipcrud+head->header.name)-1; + really_dir: + /* Check for trailing /, and zap as many as we find. */ + while (namelen && head->header.name[skipcrud+namelen] == '/') + head->header.name[skipcrud+namelen--] = '\0'; + if(f_gnudump) { /* Read the entry and delete files + that aren't listed in the archive */ + gnu_restore(skipcrud); + + } else if(head->header.linkflag==LF_DUMPDIR) + skip_file((long)(hstat.st_size)); + + + again_dir: + check = mkdir(skipcrud+head->header.name, + (we_are_root ? 0 : 0300) | (int)hstat.st_mode); + if (check != 0) { + struct stat st1; + + if (make_dirs(skipcrud+head->header.name)) + goto again_dir; + /* If we're trying to create '.', let it be. */ + if (head->header.name[skipcrud+namelen] == '.' && + (namelen==0 || + head->header.name[skipcrud+namelen-1]=='/')) + goto check_perms; + if( errno==EEXIST + && stat(skipcrud+head->header.name,&st1)==0 + && (st1.st_mode&S_IFMT)==S_IFDIR) + break; + msg_perror("Could not create directory %s",skipcrud+head->header.name); + break; + } + + check_perms: + if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode)) { + hstat.st_mode |= 0300; + msg("Added write and execute permission to directory %s", + skipcrud+head->header.name); + } + + goto set_filestat; + /* FIXME, Remember timestamps for after files created? */ + /* FIXME, change mode after files created (if was R/O dir) */ + case LF_VOLHDR: + if(f_verbose) { + printf("Reading %s\n",head->header.name); + } + break; + + case LF_NAMES: + extract_mangle(head); + break; + + case LF_MULTIVOL: + msg("Can't extract '%s'--file is continued from another volume\n",head->header.name); + skip_file((long)hstat.st_size); + break; + + } + + /* We don't need to save it any longer. */ + saverec((union record **) 0); /* Unsave it */ +} + +/* + * After a file/link/symlink/dir creation has failed, see if + * it's because some required directory was not present, and if + * so, create all required dirs. + */ +int +make_dirs(pathname) + char *pathname; +{ + char *p; /* Points into path */ + int madeone = 0; /* Did we do anything yet? */ + int save_errno = errno; /* Remember caller's errno */ + int check; + + if (errno != ENOENT) + return 0; /* Not our problem */ + + for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) { + /* Avoid mkdir of empty string, if leading or double '/' */ + if (p == pathname || p[-1] == '/') + continue; + /* Avoid mkdir where last part of path is '.' */ + if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/')) + continue; + *p = 0; /* Truncate the path there */ + check = mkdir (pathname, 0777); /* Try to create it as a dir */ + if (check == 0) { + /* Fix ownership */ + if (we_are_root) { + if (chown(pathname, hstat.st_uid, + hstat.st_gid) < 0) { + msg_perror("cannot change owner of %s to uid %d gid %d",pathname,hstat.st_uid,hstat.st_gid); + } + } + pr_mkdir(pathname, p-pathname, notumask&0777); + madeone++; /* Remember if we made one */ + *p = '/'; + continue; + } + *p = '/'; + if (errno == EEXIST) /* Directory already exists */ + continue; + /* + * Some other error in the mkdir. We return to the caller. + */ + break; + } + + errno = save_errno; /* Restore caller's errno */ + return madeone; /* Tell them to retry if we made one */ +} + +extract_sparse_file(fd, sizeleft, totalsize, name) + int fd; + long *sizeleft, + totalsize; + char *name; +{ + register char *data; + union record *datarec; + int sparse_ind = 0; + int written, + count; + + /* assuming sizeleft is initially totalsize */ + + + while (*sizeleft > 0) { + datarec = findrec(); + if (datarec == NULL) { + msg("Unexpected EOF on archive file"); + return; + } + lseek(fd, sparsearray[sparse_ind].offset, 0); + written = sparsearray[sparse_ind++].numbytes; + while (written > RECORDSIZE) { + count = write(fd, datarec->charptr, RECORDSIZE); + if (count < 0) + msg_perror("couldn't write to file %s", name); + written -= count; + *sizeleft -= count; + userec(datarec); + datarec = findrec(); + } + + count = write(fd, datarec->charptr, written); + + if (count < 0) { + msg_perror("couldn't write to file %s", name); + } else if (count != written) { + msg("could only write %d of %d bytes to file %s", totalsize - *sizeleft, totalsize, name); + skip_file((long) (*sizeleft)); + } + + written -= count; + *sizeleft -= count; + userec(datarec); + } + free(sparsearray); +/* if (end_nulls) { + register int i; + + printf("%d\n", (int) end_nulls); + for (i = 0; i < end_nulls; i++) + write(fd, "\000", 1); + }*/ + userec(datarec); +} diff --git a/usr/src/usr.bin/tar/getdate.y b/usr/src/usr.bin/tar/getdate.y new file mode 100644 index 0000000000..84e1693e5c --- /dev/null +++ b/usr/src/usr.bin/tar/getdate.y @@ -0,0 +1,896 @@ +%{ +/* $Revision: 2.1 $ +** +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** send any email to Rich. +** +** This grammar has eight shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include +#else +#ifdef _AIX /* for Bison */ +#pragma alloca +#else +char *alloca (); +#endif +#endif +#endif + +#include +#include + +#if defined(vms) +#include +#include +#else +#include +#if defined(USG) +/* +** Uncomment the next line if you need to do a tzset() call to set the +** timezone, and don't have ftime(). Some SystemV releases, I think. +*/ +/*#define NEED_TZSET */ +struct timeb { + time_t time; /* Seconds since the epoch */ + unsigned short millitm; /* Field not used */ + short timezone; + short dstflag; /* Field not used */ +}; +#else +#include +#endif /* defined(USG) */ +#if defined(BSD4_2) || defined(BSD4_1C) +#include +#else +#include +#endif /* defined(BSD4_2) */ +#endif /* defined(vms) */ + +#if defined (STDC_HEADERS) || defined (USG) +#include +#endif + +extern struct tm *localtime(); + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +#if !defined(lint) && !defined(SABER) +static char RCS[] = + "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $"; +#endif /* !defined(lint) && !defined(SABER) */ + + +#define EPOCH 1970 +#define HOUR(x) (x * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE + +%type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + time_t date_part; + + date_part= $1/10000; + yyHaveDate++; + yyDay= (date_part)%100; + yyMonth= (date_part/100)%100; + yyYear = date_part/10000; + } + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL } +}; + +/* The timezone table. */ +static TABLE TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ + { "it", tZONE, -HOUR(3.5) },/* Iran */ + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, nad SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* Military timezone table. */ +static TABLE MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL } +}; + + + + +/* ARGSUSED */ +int +yyerror(s) + char *s; +{ + return 0; +} + + +static time_t +ToSeconds(Hours, Minutes, Seconds, Meridian) + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + } + /* NOTREACHED */ +} + + +static time_t +Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) + time_t Month; + time_t Day; + time_t Year; + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; + DSTMODE DSTmode; +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + if (Year < EPOCH || Year > 1999 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(Start, Future) + time_t Start; + time_t Future; +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(Start, DayOrdinal, DayNumber) + time_t Start; + time_t DayOrdinal; + time_t DayNumber; +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(Start, RelMonth) + time_t Start; + time_t RelMonth; +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(buff) + char *buff; +{ + register char *p; + register char *q; + register TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + + +time_t +get_date(p, now) + char *p; + struct timeb *now; +{ + struct tm *tm; + struct timeb ftz; + time_t Start; + time_t tod; + + yyInput = p; + if (now == NULL) { + now = &ftz; +#if defined(NEED_TZSET) + (void)time(&ftz.time); + /* Set the timezone global. */ + tzset(); + ftz.timezone = (int) timezone / 60; +#else + (void)ftime(&ftz); +#endif /* defined(NEED_TZSET) */ + } + + tm = localtime(&now->time); + yyYear = tm->tm_year; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = now->timezone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = now->time; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff, (struct timeb *)NULL); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/usr/src/usr.bin/tar/getoldopt.c b/usr/src/usr.bin/tar/getoldopt.c new file mode 100644 index 0000000000..00be92e44c --- /dev/null +++ b/usr/src/usr.bin/tar/getoldopt.c @@ -0,0 +1,88 @@ +/* Replacement for getopt() that can be used by tar. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Plug-compatible replacement for getopt() for parsing tar-like + * arguments. If the first argument begins with "-", it uses getopt; + * otherwise, it uses the old rules used by tar, dump, and ps. + * + * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) + * + * @(#)getoldopt.c 1.4 2/4/86 - gnu + */ + +#include +#include "getopt.h" + +int +getoldopt(argc, argv, optstring, long_options, opt_index) + int argc; + char **argv; + char *optstring; + struct option *long_options; + int *opt_index; +{ + extern char *optarg; /* Points to next arg */ + extern int optind; /* Global argv index */ + static char *key; /* Points to next keyletter */ + static char use_getopt; /* !=0 if argv[1][0] was '-' */ + extern char *index(); + char c; + char *place; + + optarg = NULL; + + if (key == NULL) { /* First time */ + if (argc < 2) return EOF; + key = argv[1]; + if ((*key == '-') || (*key == '+')) + use_getopt++; + else + optind = 2; + } + + if (use_getopt) + return getopt_long(argc, argv, optstring, + long_options, opt_index); + + c = *key++; + if (c == '\0') { + key--; + return EOF; + } + place = index(optstring, c); + + if (place == NULL || c == ':') { + msg("unknown option %c", c); + return('?'); + } + + place++; + if (*place == ':') { + if (optind < argc) { + optarg = argv[optind]; + optind++; + } else { + msg("%c argument missing", c); + return('?'); + } + } + + return(c); +} diff --git a/usr/src/usr.bin/tar/getopt.c b/usr/src/usr.bin/tar/getopt.c new file mode 100644 index 0000000000..d7e2137970 --- /dev/null +++ b/usr/src/usr.bin/tar/getopt.c @@ -0,0 +1,596 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989, 1990, 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. */ + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +/* 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 _POSIX_OPTION_ORDER 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 + +/* If compiled with GNU C, use the built-in alloca */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#ifdef sparc +#include +#else +#ifdef _AIX +#pragma alloca +#else +char *alloca (); +#endif +#endif /* sparc */ +#endif /* not __GNUC__ */ + +#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) +#include +#else /* STDC_HEADERS or __GNU_LIBRARY__ */ +char *getenv (); +char *malloc (); +#endif /* STDC_HEADERS or __GNU_LIBRARY__ */ + +#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) +#include +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#define index strchr +#else /* USG or STDC_HEADERS or __GNU_LIBRARY__ */ +#ifdef VMS +#include +#else /* VMS */ +#include +#endif /* VMS */ +/* Declaring bcopy causes errors on systems whose declarations are different. + If the declaration is omitted, everything works fine. */ +#endif /* USG or STDC_HEADERS or __GNU_LIBRARY__ */ + +/* 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 = 0; + +/* 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. */ + +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; + +/* 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 + _POSIX_OPTION_ORDER 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 _POSIX_OPTION_ORDER, 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; + +/* Describe the long-named options requested by the application. + _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an + element containing a name which is zero. + The field `has_arg' is 1 if the option takes an argument, + 2 if it takes an optional argument. */ + +struct option +{ + char *name; + int has_arg; + int *flag; + int val; +}; + +CONST struct option *_getopt_long_options; + +int _getopt_long_only = 0; + +/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found. + Only valid when a long-named option was found. */ + +int option_index; + +/* 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 nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) alloca (nonopts_size); + + /* Interchange the two blocks of data in ARGV. */ + + bcopy (&argv[first_nonopt], temp, nonopts_size); + bcopy (&argv[last_nonopt], &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* 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 + otherwise. */ + +int +getopt (argc, argv, optstring) + int argc; + char **argv; + CONST char *optstring; +{ + optarg = 0; + + /* Initialize the internal data when the first call is made. + 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. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = 0; + + /* 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 (getenv ("_POSIX_OPTION_ORDER") != 0) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == 0 || *nextchar == 0) + { + 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 (argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' + || argv[optind][1] == 0) + && (_getopt_long_options == 0 + || argv[optind][0] != '+' + || argv[optind][1] == 0)) + optind++; + last_nonopt = optind; + } + + /* 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 (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) + && (_getopt_long_options == 0 + || 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. + Start decoding its characters. */ + + nextchar = argv[optind] + 1; + } + + if (_getopt_long_options != 0 + && (argv[optind][0] == '+' + || (_getopt_long_only && argv[optind][0] == '-')) + ) + { + CONST struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + CONST struct option *pfound = 0; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = _getopt_long_options, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == 0) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != 0) + { + option_index = indfound; + optind++; + if (*s) + { + if (pfound->has_arg > 0) + optarg = s + 1; + else + { + 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 + { + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return '?'; + } + } + nextchar += strlen (nextchar); + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is getopt_long_only, + and the option starts with '-' and is a valid short + option, then interpret it as a short option. Otherwise it's + an error. */ + if (_getopt_long_only == 0 || argv[optind][0] == '+' || + index (optstring, *nextchar) == 0) + { + if (opterr != 0) + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == 0) + optind++; + + if (temp == 0 || c == ':') + { + if (opterr != 0) + { + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", + argv[0], 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 = 0; + nextchar = 0; + } + 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 != 0) + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = 0; + } + } + return c; + } +} + +#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/usr/src/usr.bin/tar/getopt.h b/usr/src/usr.bin/tar/getopt.h new file mode 100644 index 0000000000..151379fc69 --- /dev/null +++ b/usr/src/usr.bin/tar/getopt.h @@ -0,0 +1,102 @@ +/* declarations for getopt + Copyright (C) 1989, 1990, 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. */ + +/* 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; + +/* Describe the long-named options requested by the application. + _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an + element containing a name which is zero. + + The field `has_arg' is: + 0 if the option does not take an argument, + 1 if the option requires an argument, + 2 if the option takes an optional argument. + + If the field `flag' is nonzero, 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 +{ + char *name; + int has_arg; + int *flag; + int val; +}; + +#ifdef __STDC__ +extern const struct option *_getopt_long_options; +#else +extern struct option *_getopt_long_options; +#endif + +/* If nonzero, '-' can introduce long-named options. + Set by getopt_long_only. */ + +extern int _getopt_long_only; + +/* The index in GETOPT_LONG_OPTIONS of the long-named option found. + Only valid when a long-named option has been found by the most + recent call to `getopt'. */ + +extern int option_index; + +#ifdef __STDC__ +int getopt (int argc, char **argv, const char *shortopts); +int getopt_long (int argc, char **argv, const char *shortopts, + const struct option *longopts, int *longind); +int getopt_long_only (int argc, char **argv, const char *shortopts, + const struct option *longopts, int *longind); +void envopt(int *pargc, char ***pargv, char *optstr); +#else +int getopt (); +int getopt_long (); +int getopt_long_only (); +void envopt(); +#endif diff --git a/usr/src/usr.bin/tar/getopt1.c b/usr/src/usr.bin/tar/getopt1.c new file mode 100644 index 0000000000..d739acb323 --- /dev/null +++ b/usr/src/usr.bin/tar/getopt1.c @@ -0,0 +1,160 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989, 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. */ + +#include "getopt.h" + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +#if !defined (NULL) +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char **argv; + CONST char *options; + CONST struct option *long_options; + int *opt_index; +{ + int val; + + _getopt_long_options = long_options; + val = getopt (argc, argv, options); + if (val == 0 && opt_index != NULL) + *opt_index = option_index; + return val; +} + +/* Like getopt_long, but '-' as well as '+' can indicate a long option. + If an option that starts with '-' 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 **argv; + CONST char *options; + CONST struct option *long_options; + int *opt_index; +{ + int val; + + _getopt_long_options = long_options; + _getopt_long_only = 1; + val = getopt (argc, argv, options); + if (val == 0 && opt_index != NULL) + *opt_index = option_index; + return val; +} + + +#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; + char *name = '\0'; + 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 '?': + 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/usr/src/usr.bin/tar/gnu.c b/usr/src/usr.bin/tar/gnu.c new file mode 100644 index 0000000000..dba5f09f40 --- /dev/null +++ b/usr/src/usr.bin/tar/gnu.c @@ -0,0 +1,630 @@ +#include +#include +#include +#include +#include + +#ifdef BSD42 +#include +#else +#ifdef __MSDOS__ +#include "msd_dir.h" +#else +#ifdef USG +#ifdef NDIR +#include +#else +#include +#endif +#ifndef DIRECT +#define direct dirent +#endif +#define DP_NAMELEN(x) strlen((x)->d_name) +#else +/* + * FIXME: On other systems there is no standard place for the header file + * for the portable directory access routines. Change the #include line + * below to bring it in from wherever it is. + */ +#include "ndir.h" +#endif +#endif +#endif + +#ifndef DP_NAMELEN +#define DP_NAMELEN(x) (x)->d_namlen +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +/* + * If there are no symbolic links, there is no lstat(). Use stat(). + */ +#ifndef S_IFLNK +#define lstat stat +#endif + +#ifdef __STDC__ +#define VOIDSTAR void * +#else +#define VOIDSTAR char * +#endif +extern VOIDSTAR ck_malloc(); +extern VOIDSTAR ck_realloc(); + +#ifndef S_IFLNK +#define lstat stat +#endif + +extern VOIDSTAR malloc(); + +#include "tar.h" + +extern time_t new_time; +extern FILE *msg_file; + +extern VOIDSTAR init_buffer(); +extern char *get_buffer(); +extern void add_buffer(); +extern void flush_buffer(); + +extern char *new_name(); + +static void add_dir_name(); + +struct dirname { + struct dirname *next; + char *name; + char *dir_text; + int dev; + int ino; + int allnew; +}; +static struct dirname *dir_list; +static time_t this_time; + +void +add_dir(name,dev,ino,text) +char *name; +char *text; +{ + struct dirname *dp; + + dp=(struct dirname *)malloc(sizeof(struct dirname)); + if(!dp) + abort(); + dp->next=dir_list; + dir_list=dp; + dp->dev=dev; + dp->ino=ino; + dp->name=malloc(strlen(name)+1); + strcpy(dp->name,name); + dp->dir_text=text; + dp->allnew=0; +} + +void +read_dir_file() +{ + int dev; + int ino; + char *strp; + FILE *fp; + char buf[512]; + extern int errno; + static char path[MAXPATHLEN]; + + time(&this_time); + if(gnu_dumpfile[0]!='/') { +#if defined(MSDOS) || defined(USG) + int getcwd(); + + if(!getcwd(path,MAXPATHLEN)) + msg("Couldn't get current directory."); + exit(EX_SYSTEM); +#else + char *getwd(); + + if(!getwd(path)) { + msg("Couldn't get current directory: %s",path); + exit(EX_SYSTEM); + } +#endif + /* If this doesn't fit, we're in serious trouble */ + strcat(path,"/"); + strcat(path,gnu_dumpfile); + gnu_dumpfile=path; + } + fp=fopen(gnu_dumpfile,"r"); + if(fp==0 && errno!=ENOENT) { + msg_perror("Can't open %s",gnu_dumpfile); + return; + } + if(!fp) + return; + fgets(buf,sizeof(buf),fp); + if(!f_new_files) { + f_new_files++; + new_time=atol(buf); + } + while(fgets(buf,sizeof(buf),fp)) { + strp= &buf[strlen(buf)]; + if(strp[-1]=='\n') + strp[-1]='\0'; + strp=buf; + dev=atol(strp); + while(isdigit(*strp)) + strp++; + ino=atol(strp); + while(isspace(*strp)) + strp++; + while(isdigit(*strp)) + strp++; + strp++; + add_dir(un_quote_string(strp),dev,ino,(char *)0); + } + fclose(fp); +} + +void +write_dir_file() +{ + FILE *fp; + struct dirname *dp; + char *str; + extern char *quote_copy_string(); + + fp=fopen(gnu_dumpfile,"w"); + if(fp==0) { + msg_perror("Can't write to %s",gnu_dumpfile); + return; + } + fprintf(fp,"%lu\n",this_time); + for(dp=dir_list;dp;dp=dp->next) { + if(!dp->dir_text) + continue; + str=quote_copy_string(dp->name); + if(str) { + fprintf(fp,"%u %u %s\n",dp->dev,dp->ino,str); + free(str); + } else + fprintf(fp,"%u %u %s\n",dp->dev,dp->ino,dp->name); + } + fclose(fp); +} + +struct dirname * +get_dir(name) +char *name; +{ + struct dirname *dp; + + for(dp=dir_list;dp;dp=dp->next) { + if(!strcmp(dp->name,name)) + return dp; + } + return 0; +} + + +/* Collect all the names from argv[] (or whatever), then expand them into + a directory tree, and put all the directories at the beginning. */ +collect_and_sort_names() +{ + struct name *n,*n_next; + int num_names; + struct stat statbuf; + int name_cmp(); + char *merge_sort(); + + name_gather(); + + if(gnu_dumpfile) + read_dir_file(); + if(!namelist) addname("."); + for(n=namelist;n;n=n_next) { + n_next=n->next; + if(n->found || n->dir_contents) + continue; + if(n->regexp) /* FIXME just skip regexps for now */ + continue; + if(n->change_dir) + if(chdir(n->change_dir)<0) { + msg_perror("can't chdir to %s",n->change_dir); + continue; + } + +#ifdef AIX + if (statx (n->name, &statbuf, STATSIZE, STX_HIDDEN|STX_LINK)) +#else + if(lstat(n->name,&statbuf)<0) { +#endif /* AIX */ + msg_perror("can't stat %s",n->name); + continue; + } + if((statbuf.st_mode&S_IFMT)==S_IFDIR) { + n->found++; + add_dir_name(n->name,statbuf.st_dev); + } + } + + num_names=0; + for(n=namelist;n;n=n->next) + num_names++; + namelist=(struct name *)merge_sort((VOIDSTAR)namelist,num_names,(char *)(&(namelist->next))-(char *)namelist,name_cmp); + + for(n=namelist;n;n=n->next) { + n->found=0; + } + /* if(gnu_dumpfile) + write_dir_file(gnu_dumpfile); */ +} + +int +name_cmp(n1,n2) +struct name *n1,*n2; +{ + if(n1->found) { + if(n2->found) + return strcmp(n1->name,n2->name); + else + return -1; + } else if(n2->found) + return 1; + else + return strcmp(n1->name,n2->name); +} + +int +dirent_cmp(p1,p2) +char **p1,**p2; +{ + char *frst,*scnd; + + frst= (*p1)+1; + scnd= (*p2)+1; + + return strcmp(frst,scnd); +} + +char * +get_dir_contents(p,device) +char *p; +int device; +{ + DIR *dirp; + register struct direct *d; + char *new_buf; + char *namebuf; + int bufsiz; + int len; + VOIDSTAR the_buffer; + char *buf; + int n_strs; + int n_size; + char *p_buf; + char **vec,**p_vec; + + extern int errno; + + errno=0; + dirp=opendir(p); + bufsiz=strlen(p)+NAMSIZ; + namebuf=ck_malloc(bufsiz+2); + if(!dirp) { + if(errno) + msg_perror("can't open directory %s",p); + else + msg("error opening directory %s",p); + new_buf="\0\0\0\0"; + } else { + struct dirname *dp; + int all_children; + + dp=get_dir(p); + all_children= dp ? dp->allnew : 0; + (void) strcpy(namebuf,p); + if(p[strlen(p)-1]!='/') + (void) strcat(namebuf,"/"); + len=strlen(namebuf); + + the_buffer=init_buffer(); + while(d=readdir(dirp)) { + struct stat hs; + + /* Skip . and .. */ + if(is_dot_or_dotdot(d->d_name)) + continue; + if(DP_NAMELEN(d) + len >=bufsiz) { + bufsiz+=NAMSIZ; + namebuf=ck_realloc(namebuf,bufsiz+2); + } + (void) strcpy(namebuf+len,d->d_name); +#ifdef AIX + if (0 != f_follow_links? + statx(namebuf, &hs, STATSIZE, STX_HIDDEN): + statx(namebuf, &hs, STATSIZE, STX_HIDDEN|STX_LINK)) +#else + if (0 != f_follow_links? stat(namebuf, &hs): lstat(namebuf, &hs)) +#endif + { + msg_perror("can't stat %s",namebuf); + continue; + } + if( (f_local_filesys && device!=hs.st_dev) + || (f_exclude && check_exclude(namebuf))) + add_buffer(the_buffer,"N",1); +#ifdef AIX + else if (S_ISHIDDEN (hs.st_mode)) { + add_buffer (the_buffer, "D", 1); + strcat (d->d_name, "A"); + d->d_namlen++; + } +#endif /* AIX */ + else if((hs.st_mode&S_IFMT)==S_IFDIR) { + if(dp=get_dir(namebuf)) { + if( dp->dev!=hs.st_dev + || dp->ino!=hs.st_ino) { + if(f_verbose) + msg("directory %s has been renamed.",namebuf); + dp->allnew=1; + dp->dev=hs.st_dev; + dp->ino=hs.st_ino; + } + dp->dir_text=""; + } else { + if(f_verbose) + msg("Directory %s is new",namebuf); + add_dir(namebuf,hs.st_dev,hs.st_ino,""); + dp=get_dir(namebuf); + dp->allnew=1; + } + if(all_children) + dp->allnew=1; + + add_buffer(the_buffer,"D",1); + } else if( !all_children + && f_new_files + && new_time>hs.st_mtime + && ( f_new_files>1 + || new_time>hs.st_ctime)) + add_buffer(the_buffer,"N",1); + else + add_buffer(the_buffer,"Y",1); + add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1)); + } + add_buffer(the_buffer,"\000\000",2); + closedir(dirp); + + /* Well, we've read in the contents of the dir, now sort them */ + buf=get_buffer(the_buffer); + if(buf[0]=='\0') { + flush_buffer(the_buffer); + new_buf="\0\0\0\0"; + } else { + n_strs=0; + for(p_buf=buf;*p_buf;) { + int tmp; + + tmp=strlen(p_buf)+1; + n_strs++; + p_buf+=tmp; + } + vec=(char **)malloc(sizeof(char *)*(n_strs+1)); + for(p_vec=vec,p_buf=buf;*p_buf;p_buf+=strlen(p_buf)+1) + *p_vec++= p_buf; + *p_vec= 0; + qsort((VOIDSTAR)vec,n_strs,sizeof(char *),dirent_cmp); + new_buf=(char *)malloc(p_buf-buf+2); + for(p_vec=vec,p_buf=new_buf;*p_vec;p_vec++) { + char *p_tmp; + + for(p_tmp= *p_vec;*p_buf++= *p_tmp++;) + ; + } + *p_buf++='\0'; + free(vec); + flush_buffer(the_buffer); + } + } + free(namebuf); + return new_buf; +} + +/* p is a directory. Add all the files in P to the namelist. If any of the + files is a directory, recurse on the subdirectory. . . */ +static void +add_dir_name(p,device) +char *p; +int device; +{ + char *new_buf; + char *p_buf; + + char *namebuf; + int buflen; + register int len; + int sublen; + + VOIDSTAR the_buffer; + + char *buf; + char **vec,**p_vec; + int n_strs,n_size; + + struct name *n; + + int dirent_cmp(); + + new_buf=get_dir_contents(p,device); + + for(n=namelist;n;n=n->next) { + if(!strcmp(n->name,p)) { + n->dir_contents = new_buf; + break; + } + } + + len=strlen(p); + buflen= NAMSIZ<=len ? len + NAMSIZ : NAMSIZ; + namebuf= ck_malloc(buflen+1); + + (void)strcpy(namebuf,p); + if(namebuf[len-1]!='/') { + namebuf[len++]='/'; + namebuf[len]='\0'; + } + for(p_buf=new_buf;*p_buf;p_buf+=sublen+1) { + sublen=strlen(p_buf); + if(*p_buf=='D') { + if(len+sublen>=buflen) { + buflen+=NAMSIZ; + namebuf= ck_realloc(namebuf,buflen+1); + } + (void)strcpy(namebuf+len,p_buf+1); + addname(namebuf); + add_dir_name(namebuf,device); + } + } + free(namebuf); +} + +/* Returns non-zero if p is . or .. This could be a macro for speed. */ +is_dot_or_dotdot(p) +char *p; +{ + return (p[0]=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0'))); +} + + + + + + +gnu_restore(skipcrud) +int skipcrud; +{ + char *current_dir; +/* int current_dir_length; */ + + char *archive_dir; +/* int archive_dir_length; */ + VOIDSTAR the_buffer; + char *p; + DIR *dirp; + struct direct *d; + char *cur,*arc; + extern struct stat hstat; /* Stat struct corresponding */ + long size,copied; + char *from,*to; + extern union record *head; + + dirp=opendir(skipcrud+head->header.name); + + if(!dirp) { + /* The directory doesn't exist now. It'll be created. + In any case, we don't have to delete any files out + of it */ + skip_file((long)hstat.st_size); + return; + } + + the_buffer=init_buffer(); + while(d=readdir(dirp)) { + if(is_dot_or_dotdot(d->d_name)) + continue; + + add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1)); + } + closedir(dirp); + add_buffer(the_buffer,"",1); + + current_dir=get_buffer(the_buffer); + archive_dir=(char *)malloc(hstat.st_size); + if(archive_dir==0) { + msg("Can't allocate %d bytes for restore",hstat.st_size); + skip_file((long)hstat.st_size); + return; + } + to=archive_dir; + for(size=hstat.st_size;size>0;size-=copied) { + from=findrec()->charptr; + if(!from) { + msg("Unexpected EOF in archive\n"); + break; + } + copied=endofrecs()->charptr - from; + if(copied>size) + copied=size; + bcopy((VOIDSTAR)from,(VOIDSTAR)to,(int)copied); + to+=copied; + userec((union record *)(from+copied-1)); + } + + for(cur=current_dir;*cur;cur+=strlen(cur)+1) { + for(arc=archive_dir;*arc;arc+=strlen(arc)+1) { + arc++; + if(!strcmp(arc,cur)) + break; + } + if(*arc=='\0') { + p=new_name(skipcrud+head->header.name,cur); + if(f_confirm && !confirm("delete",p)) { + free(p); + continue; + } + if(f_verbose) + fprintf(msg_file,"%s: deleting %s\n",tar,p); + if(recursively_delete(p)) { + msg("%s: Error while deleting %s\n",tar,p); + } + free(p); + } + + } + flush_buffer(the_buffer); + free(archive_dir); +} + +recursively_delete(path) +char *path; +{ + struct stat sbuf; + DIR *dirp; + struct direct *dp; + char *path_buf; + /* int path_len; */ + + + if(lstat(path,&sbuf)<0) + return 1; + if((sbuf.st_mode &S_IFMT)==S_IFDIR) { + + /* path_len=strlen(path); */ + dirp=opendir(path); + if(dirp==0) + return 1; + while(dp=readdir(dirp)) { + if(is_dot_or_dotdot(dp->d_name)) + continue; + path_buf=new_name(path,dp->d_name); + if(recursively_delete(path_buf)) { + free(path_buf); + closedir(dirp); + return 1; + } + free(path_buf); + } + closedir(dirp); + + if(rmdir(path)<0) + return 1; + return 0; + } + if(unlink(path)<0) + return 1; + return 0; +} + diff --git a/usr/src/usr.bin/tar/list.c b/usr/src/usr.bin/tar/list.c new file mode 100644 index 0000000000..1d950bd15d --- /dev/null +++ b/usr/src/usr.bin/tar/list.c @@ -0,0 +1,721 @@ +/* List a tar archive. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * List a tar archive. + * + * Also includes support routines for reading a tar archive. + * + * this version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu). + * + * @(#)list.c 1.31 11/5/87 - gnu + */ +#include +#include +#include +#include +#ifndef MSDOS +#include +#endif /* MSDOS */ + +#ifdef USG +#include /* major() and minor() defined here */ +#endif + +char *ctime(); /* From libc.a */ + +#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') ) + +#include "tar.h" +#include "port.h" + +extern FILE *msg_file; + +long from_oct(); /* Decode octal number */ +void demode(); /* Print file mode */ + +union record *head; /* Points to current archive header */ +struct stat hstat; /* Stat struct corresponding */ +int head_standard; /* Tape header is in ANSI format */ + +void print_header(); +void skip_file(); +void skip_extended_headers(); + +extern char *quote_copy_string(); + + +/* + * Main loop for reading an archive. + */ +void +read_and(do_something) + void (*do_something)(); +{ + int status = 3; /* Initial status at start of archive */ + int prev_status; + extern time_t new_time; + char save_linkflag; + + name_gather(); /* Gather all the names */ + open_archive(1); /* Open for reading */ + + for(;;) { + prev_status = status; + status = read_header(); + switch (status) { + + case 1: /* Valid header */ + /* We should decode next field (mode) first... */ + /* Ensure incoming names are null terminated. */ + head->header.name[NAMSIZ-1] = '\0'; + + if ( !name_match(head->header.name) + || (f_new_files && hstat.st_mtimeheader.name))) { + + int isextended = 0; + + if( head->header.linkflag==LF_VOLHDR + || head->header.linkflag==LF_MULTIVOL + || head->header.linkflag==LF_NAMES) { + (*do_something)(); + continue; + } + /* Skip past it in the archive */ + if (head->header.isextended) + isextended = 1; + save_linkflag = head->header.linkflag; + userec(head); + if (isextended) { +/* register union record *exhdr; + + for (;;) { + exhdr = findrec(); + if (!exhdr->ext_hdr.isextended) { + userec(exhdr); + break; + } + } + userec(exhdr);*/ + skip_extended_headers(); + } + /* Skip to the next header on the archive */ + if(save_linkflag != LF_DIR) + skip_file((long)hstat.st_size); + continue; + + } + + (*do_something)(); + continue; + + /* + * If the previous header was good, tell them + * that we are skipping bad ones. + */ + case 0: /* Invalid header */ + userec(head); + switch (prev_status) { + case 3: /* Error on first record */ + msg("Hmm, this doesn't look like a tar archive."); + /* FALL THRU */ + case 2: /* Error after record of zeroes */ + case 1: /* Error after header rec */ + msg("Skipping to next file header..."); + case 0: /* Error after error */ + break; + } + continue; + + case 2: /* Record of zeroes */ + userec(head); + status = prev_status; /* If error after 0's */ + if (f_ignorez) + continue; + /* FALL THRU */ + case EOF: /* End of archive */ + break; + } + break; + }; + + close_archive(); + names_notfound(); /* Print names not found */ +} + + +/* + * Print a header record, based on tar options. + */ +void +list_archive() +{ + extern char *save_name; + int isextended = 0; /* Flag to remember if head is extended */ + + /* Save the record */ + saverec(&head); + + /* Print the header record */ + if (f_verbose) { + if (f_verbose > 1) + decode_header(head, &hstat, &head_standard, 0); + print_header(); + } + + if(f_gnudump && head->header.linkflag==LF_DUMPDIR) { + size_t size, written, check; + char *data; + extern int errno; + extern long save_totsize; + extern long save_sizeleft; + + userec(head); + if(f_multivol) { + save_name = head->header.name; + save_totsize=hstat.st_size; + } + for(size = hstat.st_size;size>0;size-=written) { + if(f_multivol) + save_sizeleft=size; + data = findrec()->charptr; + if(data==NULL) { + msg("EOF in archive file?"); + break; + } + written = endofrecs()->charptr - data; + if(written>size) + written=size; + errno=0; + check=fwrite(data,sizeof(char), written, msg_file); + userec((union record *)(data+written - 1)); + if(check!=written) { + msg_perror("only wrote %ld of %ld bytes to file %s",check, written,head->header.name); + skip_file((long)(size)-written); + break; + } + } + if(f_multivol) + save_name = 0; + saverec((union record **) 0); /* Unsave it */ + fputc('\n',msg_file); + fflush(msg_file); + return; + + } + saverec((union record **) 0); /* Unsave it */ + /* Check to see if we have an extended header to skip over also */ + if (head->header.isextended) + isextended = 1; + + /* Skip past the header in the archive */ + userec(head); + + /* + * If we needed to skip any extended headers, do so now, by + * reading extended headers and skipping past them in the + * archive. + */ + if (isextended) { +/* register union record *exhdr; + + for (;;) { + exhdr = findrec(); + + if (!exhdr->ext_hdr.isextended) { + userec(exhdr); + break; + } + userec(exhdr); + }*/ + skip_extended_headers(); + } + + if(f_multivol) + save_name=head->header.name; + /* Skip to the next header on the archive */ + + skip_file((long) hstat.st_size); + + if(f_multivol) + save_name = 0; +} + + +/* + * Read a record that's supposed to be a header record. + * Return its address in "head", and if it is good, the file's + * size in hstat.st_size. + * + * Return 1 for success, 0 if the checksum is bad, EOF on eof, + * 2 for a record full of zeros (EOF marker). + * + * You must always userec(head) to skip past the header which this + * routine reads. + */ +int +read_header() +{ + register int i; + register long sum, recsum; + register char *p; + register union record *header; + long from_oct(); + + header = findrec(); + head = header; /* This is our current header */ + if (NULL == header) + return EOF; + + recsum = from_oct(8, header->header.chksum); + + sum = 0; + p = header->charptr; + for (i = sizeof(*header); --i >= 0;) { + /* + * We can't use unsigned char here because of old compilers, + * e.g. V7. + */ + sum += 0xFF & *p++; + } + + /* Adjust checksum to count the "chksum" field as blanks. */ + for (i = sizeof(header->header.chksum); --i >= 0;) + sum -= 0xFF & header->header.chksum[i]; + sum += ' '* sizeof header->header.chksum; + + if (sum == recsum) { + /* + * Good record. Decode file size and return. + */ + if (header->header.linkflag == LF_LINK) + hstat.st_size = 0; /* Links 0 size on tape */ + else + hstat.st_size = from_oct(1+12, header->header.size); + return 1; + } + + if (sum == 8*' ') { + /* + * This is a zeroed record...whole record is 0's except + * for the 8 blanks we faked for the checksum field. + */ + return 2; + } + + return 0; +} + + +/* + * Decode things from a file header record into a "struct stat". + * Also set "*stdp" to !=0 or ==0 depending whether header record is "Unix + * Standard" tar format or regular old tar format. + * + * read_header() has already decoded the checksum and length, so we don't. + * + * If wantug != 0, we want the uid/group info decoded from Unix Standard + * tapes (for extraction). If == 0, we are just printing anyway, so save time. + * + * decode_header should NOT be called twice for the same record, since the + * two calls might use different "wantug" values and thus might end up with + * different uid/gid for the two calls. If anybody wants the uid/gid they + * should decode it first, and other callers should decode it without uid/gid + * before calling a routine, e.g. print_header, that assumes decoded data. + */ +decode_header(header, st, stdp, wantug) + register union record *header; + register struct stat *st; + int *stdp; + int wantug; +{ + + long from_oct(); + + st->st_mode = from_oct(8, header->header.mode); + st->st_mtime = from_oct(1+12, header->header.mtime); + if(f_gnudump) { + st->st_atime = from_oct(1+12, header->header.atime); + st->st_ctime = from_oct(1+12, header->header.ctime); + } + + if (0==strcmp(header->header.magic, TMAGIC)) { + /* Unix Standard tar archive */ + *stdp = 1; + if (wantug) { +#ifdef NONAMES + st->st_uid = from_oct(8, header->header.uid); + st->st_gid = from_oct(8, header->header.gid); +#else + st->st_uid = finduid(header->header.uname); + st->st_gid = findgid(header->header.gname); +#endif + } + switch (header->header.linkflag) { + case LF_BLK: case LF_CHR: + st->st_rdev = makedev(from_oct(8, header->header.devmajor), + from_oct(8, header->header.devminor)); + } + } else { + /* Old fashioned tar archive */ + *stdp = 0; + st->st_uid = from_oct(8, header->header.uid); + st->st_gid = from_oct(8, header->header.gid); + st->st_rdev = 0; + } +} + + +/* + * Quick and dirty octal conversion. + * + * Result is -1 if the field is invalid (all blank, or nonoctal). + */ +long +from_oct(digs, where) + register int digs; + register char *where; +{ + register long value; + + while (isspace(*where)) { /* Skip spaces */ + where++; + if (--digs <= 0) + return -1; /* All blank field */ + } + value = 0; + while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */ + value = (value << 3) | (*where++ - '0'); + --digs; + } + + if (digs > 0 && *where && !isspace(*where)) + return -1; /* Ended on non-space/nul */ + + return value; +} + + +/* + * Actually print it. + * + * Plain and fancy file header block logging. + * Non-verbose just prints the name, e.g. for "tar t" or "tar x". + * This should just contain file names, so it can be fed back into tar + * with xargs or the "-T" option. The verbose option can give a bunch + * of info, one line per file. I doubt anybody tries to parse its + * format, or if they do, they shouldn't. Unix tar is pretty random here + * anyway. + * + * Note that print_header uses the globals , , and + * , which must be set up in advance. This is not very clean + * and should be cleaned up. FIXME. + */ +#define UGSWIDTH 11 /* min width of User, group, size */ +#define DATEWIDTH 19 /* Last mod date */ +static int ugswidth = UGSWIDTH; /* Max width encountered so far */ + +void +print_header() +{ + char modes[11]; + char *timestamp; + char uform[11], gform[11]; /* These hold formatted ints */ + char *user, *group; + char size[24]; /* Holds a formatted long or maj, min */ + long longie; /* To make ctime() call portable */ + int pad; + char *name; + extern long baserec; + + if(f_sayblock) + fprintf(msg_file,"rec %10d: ",baserec + (ar_record - ar_block)); + /* annofile(msg_file, (char *)NULL); */ + + if (f_verbose <= 1) { + /* Just the fax, mam. */ + char *name; + + name=quote_copy_string(head->header.name); + if(name==0) + name=head->header.name; + fprintf(msg_file, "%s\n", name); + if(name!=head->header.name) + free(name); + } else { + /* File type and modes */ + modes[0] = '?'; + switch (head->header.linkflag) { + case LF_VOLHDR: + modes[0]='V'; + break; + + case LF_MULTIVOL: + modes[0]='M'; + break; + + case LF_NAMES: + modes[0]='N'; + break; + + case LF_SPARSE: + case LF_NORMAL: + case LF_OLDNORMAL: + case LF_LINK: + modes[0] = '-'; + if ('/' == head->header.name[strlen(head->header.name)-1]) + modes[0] = 'd'; + break; + case LF_DUMPDIR:modes[0] = 'd'; break; + case LF_DIR: modes[0] = 'd'; break; + case LF_SYMLINK:modes[0] = 'l'; break; + case LF_BLK: modes[0] = 'b'; break; + case LF_CHR: modes[0] = 'c'; break; + case LF_FIFO: modes[0] = 'p'; break; + case LF_CONTIG: modes[0] = 'C'; break; + } + + demode((unsigned)hstat.st_mode, modes+1); + + /* Timestamp */ + longie = hstat.st_mtime; + timestamp = ctime(&longie); + timestamp[16] = '\0'; + timestamp[24] = '\0'; + + /* User and group names */ + if (*head->header.uname && head_standard) { + user = head->header.uname; + } else { + user = uform; + (void)sprintf(uform, "%d", (int)hstat.st_uid); + } + if (*head->header.gname && head_standard) { + group = head->header.gname; + } else { + group = gform; + (void)sprintf(gform, "%d", (int)hstat.st_gid); + } + + /* Format the file size or major/minor device numbers */ + switch (head->header.linkflag) { + case LF_CHR: + case LF_BLK: + (void)sprintf(size, "%d,%d", + major(hstat.st_rdev), + minor(hstat.st_rdev)); + break; + case LF_SPARSE: + (void)sprintf(size, "%ld", + from_oct(1+12, head->header.realsize)); + break; + default: + (void)sprintf(size, "%ld", (long)hstat.st_size); + } + + /* Figure out padding and print the whole line. */ + pad = strlen(user) + strlen(group) + strlen(size) + 1; + if (pad > ugswidth) ugswidth = pad; + + name = quote_copy_string(head->header.name); + if(!name) + name=head->header.name; + fprintf(msg_file, "%s %s/%s %*s%s %s %s %.*s", + modes, + user, + group, + ugswidth - pad, + "", + size, + timestamp+4, timestamp+20, + sizeof(head->header.name), + name); + + if(name!=head->header.name) + free(name); + switch (head->header.linkflag) { + case LF_SYMLINK: + name=quote_copy_string(head->header.linkname); + if(!name) + name=head->header.linkname; + fprintf(msg_file, " -> %s\n", name); + if(name!=head->header.linkname) + free(name); + break; + + case LF_LINK: + name=quote_copy_string(head->header.linkname); + if(!name) + name=head->header.linkname; + fprintf(msg_file, " link to %s\n", head->header.linkname); + if(name!=head->header.linkname) + free(name); + break; + + default: + fprintf(msg_file, " unknown file type '%c'\n", + head->header.linkflag); + break; + + case LF_OLDNORMAL: + case LF_NORMAL: + case LF_SPARSE: + case LF_CHR: + case LF_BLK: + case LF_DIR: + case LF_FIFO: + case LF_CONTIG: + case LF_DUMPDIR: + putc('\n', msg_file); + break; + + case LF_VOLHDR: + fprintf(msg_file, "--Volume Header--\n"); + break; + + case LF_MULTIVOL: + fprintf(msg_file, "--Continued at byte %ld--\n",from_oct(1+12,head->header.offset)); + break; + + case LF_NAMES: + fprintf(msg_file,"--Mangled file names--\n"); + break; + } + } + fflush(msg_file); +} + +/* + * Print a similar line when we make a directory automatically. + */ +void +pr_mkdir(pathname, length, mode) + char *pathname; + int length; + int mode; +{ + char modes[11]; + char *name; + extern long baserec; + + if (f_verbose > 1) { + /* File type and modes */ + modes[0] = 'd'; + demode((unsigned)mode, modes+1); + + if(f_sayblock) + fprintf(msg_file,"rec %10d: ",baserec + (ar_record - ar_block)); + /* annofile(msg_file, (char *)NULL); */ + name=quote_copy_string(pathname); + if(!name) + name=pathname; + fprintf(msg_file, "%s %*s %.*s\n", + modes, + ugswidth+DATEWIDTH, + "Creating directory:", + length, + pathname); + if(name!=pathname) + free(name); + } +} + + +/* + * Skip over bytes of data in records in the archive. + */ +void +skip_file(size) + register long size; +{ + union record *x; + extern long save_totsize; + extern long save_sizeleft; + + if(f_multivol) { + save_totsize=size; + save_sizeleft=size; + } + + while (size > 0) { + x = findrec(); + if (x == NULL) { /* Check it... */ + msg("Unexpected EOF on archive file"); + exit(EX_BADARCH); + } + userec(x); + size -= RECORDSIZE; + if(f_multivol) + save_sizeleft-=RECORDSIZE; + } +} + +void +skip_extended_headers() +{ + register union record *exhdr; + + for (;;) { + exhdr = findrec(); + if (!exhdr->ext_hdr.isextended) { + userec(exhdr); + break; + } + userec (exhdr); + } +} + +/* + * Decode the mode string from a stat entry into a 9-char string and a null. + */ +void +demode(mode, string) + register unsigned mode; + register char *string; +{ + register unsigned mask; + register char *rwx = "rwxrwxrwx"; + + for (mask = 0400; mask != 0; mask >>= 1) { + if (mode & mask) + *string++ = *rwx++; + else { + *string++ = '-'; + rwx++; + } + } + + if (mode & S_ISUID) + if (string[-7] == 'x') + string[-7] = 's'; + else + string[-7] = 'S'; + if (mode & S_ISGID) + if (string[-4] == 'x') + string[-4] = 's'; + else + string[-4] = 'S'; + if (mode & S_ISVTX) + if (string[-1] == 'x') + string[-1] = 't'; + else + string[-1] = 'T'; + *string = '\0'; +} diff --git a/usr/src/usr.bin/tar/mangle.c b/usr/src/usr.bin/tar/mangle.c new file mode 100644 index 0000000000..9c54f1fc51 --- /dev/null +++ b/usr/src/usr.bin/tar/mangle.c @@ -0,0 +1,226 @@ +#include +#include +#include "tar.h" + +#ifdef __STDC__ +#define VOIDSTAR void * +#else +#define VOIDSTAR char * +#endif +extern VOIDSTAR ck_malloc(); +extern VOIDSTAR init_buffer(); +extern char *quote_copy_string(); +extern char *get_buffer(); +extern char *index(); + +extern union record *start_header(); + +extern struct stat hstat; /* Stat struct corresponding */ + +struct mangled { + struct mangled *next; + int type; + char mangled[NAMSIZ]; + char *linked_to; + char normal[1]; +}; + + +/* Should use a hash table, etc. . */ +struct mangled *first_mangle; +int mangled_num = 0; + +char * +find_mangled (name) +char *name; +{ + struct mangled *munge; + + for(munge=first_mangle;munge;munge=munge->next) + if(!strcmp(name,munge->normal)) + return munge->mangled; + return 0; +} + + +#ifdef S_IFLNK +void add_symlink_mangle(symlink, linkto, buffer) +char *symlink; +char *linkto; +char *buffer; +{ + struct mangled *munge,*kludge; + + munge=(struct mangled *)ck_malloc(sizeof(struct mangled)+strlen(symlink)+strlen(linkto)+2); + if(!first_mangle) + first_mangle=munge; + else { + for(kludge=first_mangle;kludge->next;kludge=kludge->next) + ; + kludge->next=munge; + } + munge->type=1; + munge->next=0; + strcpy(munge->normal,symlink); + munge->linked_to=munge->normal+strlen(symlink)+1; + strcpy(munge->linked_to,linkto); + sprintf(munge->mangled,"@@MaNgLeD.%d",mangled_num++); + strncpy(buffer,munge->mangled,NAMSIZ); +} +#endif + +void +add_mangle (name, buffer) +char *name; +char *buffer; +{ + struct mangled *munge,*kludge; + + munge=(struct mangled *)ck_malloc(sizeof(struct mangled)+strlen(name)); + if(!first_mangle) + first_mangle=munge; + else { + for(kludge=first_mangle;kludge->next;kludge=kludge->next) + ; + kludge->next=munge; + } + munge->next=0; + munge->type=0; + strcpy(munge->normal,name); + sprintf(munge->mangled,"@@MaNgLeD.%d",mangled_num++); + strncpy(buffer,munge->mangled,NAMSIZ); +} + +void +write_mangled() +{ + struct mangled *munge; + struct stat hstat; + union record *header; + char *ptr1,*ptr2; + VOIDSTAR the_buffer; + int size; + int bufsize; + + if(!first_mangle) + return; + the_buffer=init_buffer(); + for(munge=first_mangle,size=0;munge;munge=munge->next) { + ptr1=quote_copy_string(munge->normal); + if(!ptr1) + ptr1=munge->normal; + if(munge->type) { + add_buffer(the_buffer,"Symlink ",8); + add_buffer(the_buffer,ptr1,strlen(ptr1)); + add_buffer(the_buffer," to ",4); + + if(ptr2=quote_copy_string(munge->linked_to)) { + add_buffer(the_buffer,ptr2,strlen(ptr2)); + free(ptr2); + } else + add_buffer(the_buffer,munge->linked_to,strlen(munge->linked_to)); + } else { + add_buffer(the_buffer,"Rename ",7); + add_buffer(the_buffer,munge->mangled,strlen(munge->mangled)); + add_buffer(the_buffer," to ",4); + add_buffer(the_buffer,ptr1,strlen(ptr1)); + } + add_buffer(the_buffer,"\n",1); + if(ptr1!=munge->normal) + free(ptr1); + } + + bzero(&hstat,sizeof(struct stat)); + hstat.st_atime=hstat.st_mtime=hstat.st_ctime=time(0); + ptr1=get_buffer(the_buffer); + hstat.st_size=strlen(ptr1); + + header=start_header("././@MaNgLeD_NaMeS",&hstat); + header->header.linkflag=LF_NAMES; + finish_header(header); + size=hstat.st_size; + header=findrec(); + bufsize = endofrecs()->charptr - header->charptr; + + while(bufsizecharptr,bufsize); + ptr1+=bufsize; + size-=bufsize; + userec(header+(bufsize-1)/RECORDSIZE); + header=findrec(); + bufsize = endofrecs()->charptr - header->charptr; + } + bcopy(ptr1,header->charptr,size); + bzero(header->charptr+size,bufsize-size); + userec(header+(size-1)/RECORDSIZE); +} + +void +extract_mangle(head) +union record *head; +{ + char *buf; + char *fromtape; + char *to; + char *ptr,*ptrend; + char *nam1,*nam1end; + int size; + int copied; + + size=hstat.st_size; + buf=to=ck_malloc(size+1); + buf[size]='\0'; + while(size>0) { + fromtape=findrec()->charptr; + if(fromtape==0) { + msg("Unexpected EOF in mangled names!"); + return; + } + copied=endofrecs()->charptr-fromtape; + if(copied>size) + copied=size; + bcopy(fromtape,to,copied); + to+=copied; + size-=copied; + userec((union record *)(fromtape+copied-1)); + } + for(ptr=buf;*ptr;ptr=ptrend) { + ptrend=index(ptr,'\n'); + *ptrend++='\0'; + + if(!strncmp(ptr,"Rename ",7)) { + nam1=ptr+7; + nam1end=index(nam1,' '); + while(strncmp(nam1end," to ",4)) { + nam1end++; + nam1end=index(nam1end,' '); + } + *nam1end='\0'; + if(ptrend[-2]=='/') + ptrend[-2]='\0'; + un_quote_string(nam1end+4); + if(rename(nam1,nam1end+4)) + msg_perror("Can't rename %s to %s",nam1,nam1end+4); + else if(f_verbose) + msg("Renamed %s to %s",nam1,nam1end+4); + } +#ifdef S_IFLNK + else if(!strncmp(ptr,"Symlink ",8)) { + nam1=ptr+8; + nam1end=index(nam1,' '); + while(strncmp(nam1end," to ",4)) { + nam1end++; + nam1end=index(nam1end,' '); + } + un_quote_string(nam1); + un_quote_string(nam1end+4); + if(symlink(nam1,nam1end+4) && (unlink(nam1end+4) || symlink(nam1,nam1end+4))) + msg_perror("Can't symlink %s to %s",nam1,nam1end+4); + else if(f_verbose) + msg("Symlinkd %s to %s",nam1,nam1end+4); + } +#endif + else + msg("Unknown demangling command %s",ptr); + } +} diff --git a/usr/src/usr.bin/tar/names.c b/usr/src/usr.bin/tar/names.c new file mode 100644 index 0000000000..c2b8157ed2 --- /dev/null +++ b/usr/src/usr.bin/tar/names.c @@ -0,0 +1,135 @@ +/* Look up user and/or group names. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Look up user and/or group names. + * + * This file should be modified for non-unix systems to do something + * reasonable. + * + * @(#)names.c 1.3 10/30/87 - gnu + */ +#include +#include "tar.h" + +extern char *strncpy(); + +#ifndef NONAMES +/* Whole module goes away if NONAMES defined. Otherwise... */ +#include +#include + +static int saveuid = -993; +static char saveuname[TUNMLEN]; +static int my_uid = -993; + +static int savegid = -993; +static char savegname[TGNMLEN]; +static int my_gid = -993; + +#define myuid ( my_uid < 0? (my_uid = getuid()): my_uid ) +#define mygid ( my_gid < 0? (my_gid = getgid()): my_gid ) + +/* + * Look up a user or group name from a uid/gid, maintaining a cache. + * FIXME, for now it's a one-entry cache. + * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup. + * + * This is ifdef'd because on Suns, it drags in about 38K of "yellow + * pages" code, roughly doubling the program size. Thanks guys. + */ +void +finduname(uname, uid) + char uname[TUNMLEN]; + int uid; +{ + struct passwd *pw; + extern struct passwd *getpwuid (); + + if (uid != saveuid) { + saveuid = uid; + saveuname[0] = '\0'; + pw = getpwuid(uid); + if (pw) + strncpy(saveuname, pw->pw_name, TUNMLEN); + } + strncpy(uname, saveuname, TUNMLEN); +} + +int +finduid(uname) + char uname[TUNMLEN]; +{ + struct passwd *pw; + extern struct passwd *getpwnam(); + + if (uname[0] != saveuname[0] /* Quick test w/o proc call */ + || 0!=strncmp(uname, saveuname, TUNMLEN)) { + strncpy(saveuname, uname, TUNMLEN); + pw = getpwnam(uname); + if (pw) { + saveuid = pw->pw_uid; + } else { + saveuid = myuid; + } + } + return saveuid; +} + + +void +findgname(gname, gid) + char gname[TGNMLEN]; + int gid; +{ + struct group *gr; + extern struct group *getgrgid (); + + if (gid != savegid) { + savegid = gid; + savegname[0] = '\0'; + (void)setgrent(); + gr = getgrgid(gid); + if (gr) + strncpy(savegname, gr->gr_name, TGNMLEN); + } + (void) strncpy(gname, savegname, TGNMLEN); +} + + +int +findgid(gname) + char gname[TUNMLEN]; +{ + struct group *gr; + extern struct group *getgrnam(); + + if (gname[0] != savegname[0] /* Quick test w/o proc call */ + || 0!=strncmp(gname, savegname, TUNMLEN)) { + strncpy(savegname, gname, TUNMLEN); + gr = getgrnam(gname); + if (gr) { + savegid = gr->gr_gid; + } else { + savegid = mygid; + } + } + return savegid; +} +#endif diff --git a/usr/src/usr.bin/tar/open3.h b/usr/src/usr.bin/tar/open3.h new file mode 100644 index 0000000000..2fa18dfcfc --- /dev/null +++ b/usr/src/usr.bin/tar/open3.h @@ -0,0 +1,69 @@ +/* Defines for Sys V style 3-argument open call. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * @(#)open3.h 1.4 87/11/11 + * + * open3.h -- #defines for the various flags for the Sys V style 3-argument + * open() call. On BSD or System 5, the system already has this in an + * include file. This file is needed for V7 and MINIX systems for the + * benefit of open3() in port.c, a routine that emulates the 3-argument + * call using system calls available on V7/MINIX. + * + * This file is needed by PD tar even if we aren't using the + * emulator, since the #defines for O_WRONLY, etc. are used in + * a couple of places besides the open() calls, (e.g. in the assignment + * to openflag in extract.c). We just #include this rather than + * #ifdef them out. + * + * Written 6/10/87 by rmtodd@uokmax (Richard Todd). + * + * The names have been changed by John Gilmore, 31 July 1987, since + * Richard called it "bsdopen", and really this change was introduced in + * AT&T Unix systems before BSD picked it up. + */ + +/* Only one of the next three should be specified */ +#define O_RDONLY 0 /* only allow read */ +#define O_WRONLY 1 /* only allow write */ +#define O_RDWR 2 /* both are allowed */ + +/* The rest of these can be OR-ed in to the above. */ +/* + * O_NDELAY isn't implemented by the emulator. It's only useful (to tar) on + * systems that have named pipes anyway; it prevents tar's hanging by + * opening a named pipe. We #ifndef it because some systems already have + * it defined. + */ +#ifndef O_NDELAY +#define O_NDELAY 4 /* don't block on opening devices that would + * block on open -- ignored by emulator. */ +#endif +#define O_CREAT 8 /* create file if needed */ +#define O_EXCL 16 /* file cannot already exist */ +#define O_TRUNC 32 /* truncate file on open */ +#define O_APPEND 64 /* always write at end of file -- ignored by emul */ + +#ifdef EMUL_OPEN3 +/* + * make emulation transparent to rest of file -- redirect all open() calls + * to our routine + */ +#define open open3 +#endif diff --git a/usr/src/usr.bin/tar/port.c b/usr/src/usr.bin/tar/port.c new file mode 100644 index 0000000000..faf6e35dad --- /dev/null +++ b/usr/src/usr.bin/tar/port.c @@ -0,0 +1,1324 @@ +/* Supporting routines which may sometimes be missing. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * @(#)port.c 1.15 87/11/05 by John Gilmore, 1986 + * + * These are routines not available in all environments. + * + * I know this introduces an extra level of subroutine calls and is + * slightly slower. Frankly, my dear, I don't give a damn. Let the + * Missed-Em Vee losers suffer a little. This software is proud to + * have been written on a BSD system. + */ +#include +#include +#include +#include +#include + +#if defined(__MSDOS__) || defined(USG) +#include +#else +#include +#endif + +#include "tar.h" +#include "port.h" + +#ifdef USG +#include +#else +extern size_t strlen(); +#endif + +extern long baserec; +/* + * Some people (e.g. V7) don't have a #define for these. + */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif +#ifndef NULL +#define NULL 0 +#endif + +/* JF: modified so all configuration information can appear here, instead of + being scattered through the file. Add all the machine-dependent #ifdefs + here */ +#undef WANT_DUMB_GET_DATE/* WANT_DUMB_GET_DATE --> get_date() */ +#undef WANT_VALLOC /* WANT_VALLOC --> valloc() */ +#undef WANT_MKDIR /* WANT_MKDIR --> mkdir() rmdir() */ +#undef WANT_STRING /* WANT_STRING --> index() bcopy() bzero() bcmp() */ +#undef WANT_BZERO /* WANT_BZERO --> bzero() bcmp() execlp() */ + /* EMUL_OPEN3 --> open3() */ +#undef WANT_MKNOD /* WANT_MKNOD --> mknod() link() chown() geteuid() */ +#undef WANT_UTILS /* WANT_UTILS --> panic() ck_*() *_buffer() + merge_sort() quote_copy_string() un_quote_string() */ +#undef WANT_CK_PIPE /* WANT_CK_PIPE --> ck_pipe() */ +#undef WANT_GETWD /* WANT_GETWD --> getwd() */ +#undef WANT_STRSTR /* WANT_STRSTR --> strstr() */ +#undef WANT_FTRUNCATE /* WANT_FTRUNCATE --> frtruncate() */ + +/* Define only ONE of these four . . . */ +/* #undef DOPRNT_MSG /* Define this one if you have _doprnt() and + no varargs support */ +/* #undef VARARGS_MSG /* Define this one if you have varargs.h and + vfprintf() */ +/* #undef STDC_MSG /* Define this one if you are using ANSI C and + and have vfprintf() */ +/* #undef LOSING_MSG /* Define this one if you don't have any of the + above */ +#ifdef USG +#define WANT_STRING +#define WANT_VALLOC + +#if defined(sgi) && defined(mips) +#define WANT_GETWD +#endif + +#if defined(i386) +#define WANT_FTRUNCATE +#endif + +#endif + +#ifdef hpux +#define WANT_VALLOC +#endif + +#ifdef MINIX +#define WANT_BZERO +#endif + +#ifdef __MSDOS__ +char TTY_NAME[] = "con"; + +#define WANT_STRING +#define WANT_MKNOD +#define WANT_UTILS +#define WANT_VALLOC + +#if (!defined(STDC_MSG) && !defined(DOPRNT_MSG) && !defined(VARARGS_MSG) && !defined(LOSING_MSG)) +#ifdef __STDC__ +#define STDC_MSG +#else +#define LOSING_MSG +#endif +#endif + +#else /* not MSDOS */ +char TTY_NAME[] ="/dev/tty"; + +#define WANT_UTILS +#define WANT_CK_PIPE +#ifndef HAVE_STRSTR +#define WANT_STRSTR +#endif + +#if (!defined(STDC_MSG) && !defined(DOPRNT_MSG) && !defined(VARARGS_MSG) && !defined(LOSING_MSG)) +#ifdef BSD42 +/* BSD systems should do this even if __STDC__, because + we might be using an ANSI compiler without an ANSI library. (sigh) */ +#ifdef sparc +#define LOSING_MSG +#else +#define DOPRNT_MSG +#endif +#else /* not BSD */ +#ifdef __STDC__ +#define STDC_MSG +#else /* not ANSI C */ +#define LOSING_MSG +#endif /* not ANSI C */ +#endif /* not BSD */ +#endif /* Need to define some form of _MSG */ +#endif /* not MSDOS */ + +/* End of system-dependent #ifdefs */ + +#ifdef WANT_DUMB_GET_DATE +/* JF a get_date() routine takes a date/time/etc and turns it into a time_t */ +/* This one is a quick hack I wrote in about five minutes to see if the N + option works. Someone should replace it with one that works */ + +/* This get_date takes an arg of the form mm/dd/yyyy hh:mm:ss and turns it + into a time_t . Its not well tested or anything. . . */ +/* In general, you should use the get_date() supplied in getdate.y */ + +#define OFF_FROM GMT 18000 /* Change for your time zone! */ + +time_t +get_date(str) +char *str; +{ + int month,day,year,hour,minute,second; + time_t ret; + int n; + +#define SECS_PER_YEAR (365L*SECS_PER_DAY) +#define SECS_PER_LEAP_YEAR (366L*SECS_PER_DAY) + +#define SECS_PER_DAY (24L*60*60) + static int days_per_month[2][12] = { + 31,28,31,30,31,30,31,31,30,31,30,31, + 31,29,31,30,31,30,31,31,30,31,30,31 + }; + + static int days_per_year[2]={365,366}; + + month=day=year=hour=minute=second=0; + n=sscanf(str,"%d/%d/%d %d:%d:%d",&month,&day,&year,&hour,&minute,&second); + if(n<3) + return 0; + if(year<100) + year+=1900; + if(year<1970) + return 0; + ret=0; + + ret+=OFF_FROM_GMT; + + for(n=1970;n +#ifndef __MSDOS__ +#include +#endif + +char * +index (s, c) + char *s; + int c; +{ + return (strchr (s, c)); +} + +char * +rindex(s,c) +char *s; +int c; +{ + return strrchr(s,c); +} + +void +bcopy (s1, s2, n) + char *s1, *s2; + int n; +{ + (void) memcpy (s2, s1, n); +} + +void +bzero (s1, n) + char *s1; + int n; +{ + (void) memset(s1, 0, n); +} + +int +bcmp(s1, s2, n) + char *s1, *s2; + int n; +{ + return memcmp(s1, s2, n); +} +#endif + +#ifdef WANT_BZERO +/* Minix has bcopy but not bzero, and no memset. Thanks, Andy. */ +void +bzero (s1, n) + register char *s1; + register int n; +{ + while (n--) *s1++ = '\0'; +} + +/* It also has no bcmp() */ +int +bcmp (s1, s2, n) + register char *s1,*s2; + register int n; +{ + for ( ; n-- ; ++s1, ++s2) { + if (*s1 != *s2) return *s1 - *s2; + } + return 0; +} + +/* + * Groan, Minix doesn't have execlp either! + * + * execlp(file,arg0,arg1...argn,(char *)NULL) + * exec a program, automatically searching for the program through + * all the directories on the PATH. + * + * This version is naive about variable argument lists, it assumes + * a straightforward C calling sequence. If your system has odd stacks + * *and* doesn't have execlp, YOU get to fix it. + */ +int +execlp(filename, arg0) + char *filename, *arg0; +{ + register char *p, *path; + register char *fnbuffer; + char **argstart = &arg0; + struct stat statbuf; + extern char **environ; + extern int errno; + extern char *malloc(), *getenv(), *index(); + + if ((p = getenv("PATH")) == NULL) { + /* couldn't find path variable -- try to exec given filename */ + return execve(filename, argstart, environ); + } + + /* + * make a place to build the filename. We malloc larger than we + * need, but we know it will fit in this. + */ + fnbuffer = malloc( strlen(p) + 1 + strlen(filename) ); + if (fnbuffer == NULL) { + errno = ENOMEM; + return -1; + } + + /* + * try each component of the path to see if the file's there + * and executable. + */ + for (path = p ; path ; path = p) { + /* construct full path name to try */ + if ((p = index(path,':')) == NULL) { + strcpy(fnbuffer, path); + } else { + strncpy(fnbuffer, path, p-path); + fnbuffer[p-path] = '\0'; + p++; /* Skip : for next time */ + } + if (strlen(fnbuffer) != 0) + strcat(fnbuffer,"/"); + strcat(fnbuffer,filename); + + /* check to see if file is there and is a normal file */ + if (stat(fnbuffer, &statbuf) < 0) { + if (errno == ENOENT) + continue; /* file not there,keep on looking */ + else + goto fail; /* failed for some reason, return */ + } + if ( (statbuf.st_mode & S_IFMT) != S_IFREG) continue; + + if (execve(fnbuffer, argstart, environ) < 0 + && errno != ENOENT + && errno != ENOEXEC) { + /* failed, for some other reason besides "file + * not found" or "not a.out format" + */ + goto fail; + } + + /* + * If we got error ENOEXEC, the file is executable but is + * not an object file. Try to execute it as a shell script, + * returning error if we can't execute /bin/sh. + * + * FIXME, this code is broken in several ways. Shell + * scripts should not in general be executed by the user's + * SHELL variable program. On more mature systems, the + * script can specify with #!/bin/whatever. Also, this + * code clobbers argstart[-1] if the exec of the shell + * fails. + */ + if (errno == ENOEXEC) { + char *shell; + + /* Try to execute command "sh arg0 arg1 ..." */ + if ((shell = getenv("SHELL")) == NULL) + shell = "/bin/sh"; + argstart[-1] = shell; + argstart[0] = fnbuffer; + execve(shell, &argstart[-1], environ); + goto fail; /* Exec didn't work */ + } + + /* + * If we succeeded, the execve() doesn't return, so we + * can only be here is if the file hasn't been found yet. + * Try the next place on the path. + */ + } + + /* all attempts failed to locate the file. Give up. */ + errno = ENOENT; + +fail: + free(fnbuffer); + return -1; +} +#endif + + +#ifdef EMUL_OPEN3 +#include "open3.h" +/* + * open3 -- routine to emulate the 3-argument open system + * call that is present in most modern Unix systems. + * This version attempts to support all the flag bits except for O_NDELAY + * and O_APPEND, which are silently ignored. The emulation is not as efficient + * as the real thing (at worst, 4 system calls instead of one), but there's + * not much I can do about that. + * + * Written 6/10/87 by rmtodd@uokmax + * + * open3(path, flag, mode) + * Attempts to open the file specified by + * the given pathname. The following flag bits (#defined in tar.h) + * specify options to the routine: + * O_RDONLY file open for read only + * O_WRONLY file open for write only + * O_RDWR file open for both read & write + * (Needless to say, you should only specify one of the above). + * O_CREAT file is created with specified mode if it needs to be. + * O_TRUNC if file exists, it is truncated to 0 bytes + * O_EXCL used with O_CREAT--routine returns error if file exists + * Function returns file descriptor if successful, -1 and errno if not. + */ + +/* + * array to give arguments to access for various modes + * FIXME, this table depends on the specific integer values of O_XXX, + * and also contains integers (args to 'access') that should be #define's. + */ +static int modes[] = + { + 04, /* O_RDONLY */ + 02, /* O_WRONLY */ + 06, /* O_RDWR */ + 06, /* invalid but we'd better cope -- O_WRONLY+O_RDWR */ + }; + +/* Shut off the automatic emulation of open(), we'll need it. */ +#undef open + +int +open3(path, flags, mode) +char *path; +int flags, mode; +{ + extern int errno; + int exists = 1; + int call_creat = 0; + int fd; + /* + * We actually do the work by calling the open() or creat() system + * call, depending on the flags. Call_creat is true if we will use + * creat(), false if we will use open(). + */ + + /* + * See if the file exists and is accessible in the requested mode. + * + * Strictly speaking we shouldn't be using access, since access checks + * against real uid, and the open call should check against euid. + * Most cases real uid == euid, so it won't matter. FIXME. + * FIXME, the construction "flags & 3" and the modes table depends + * on the specific integer values of the O_XXX #define's. Foo! + */ + if (access(path,modes[flags & 3]) < 0) { + if (errno == ENOENT) { + /* the file does not exist */ + exists = 0; + } else { + /* probably permission violation */ + if (flags & O_EXCL) { + /* Oops, the file exists, we didn't want it. */ + /* No matter what the error, claim EEXIST. */ + errno = EEXIST; + } + return -1; + } + } + + /* if we have the O_CREAT bit set, check for O_EXCL */ + if (flags & O_CREAT) { + if ((flags & O_EXCL) && exists) { + /* Oops, the file exists and we didn't want it to. */ + errno = EEXIST; + return -1; + } + /* + * If the file doesn't exist, be sure to call creat() so that + * it will be created with the proper mode. + */ + if (!exists) call_creat = 1; + } else { + /* If O_CREAT isn't set and the file doesn't exist, error. */ + if (!exists) { + errno = ENOENT; + return -1; + } + } + + /* + * If the O_TRUNC flag is set and the file exists, we want to call + * creat() anyway, since creat() guarantees that the file will be + * truncated and open()-for-writing doesn't. + * (If the file doesn't exist, we're calling creat() anyway and the + * file will be created with zero length.) + */ + if ((flags & O_TRUNC) && exists) call_creat = 1; + /* actually do the call */ + if (call_creat) { + /* + * call creat. May have to close and reopen the file if we + * want O_RDONLY or O_RDWR access -- creat() only gives + * O_WRONLY. + */ + fd = creat(path,mode); + if (fd < 0 || (flags & O_WRONLY)) return fd; + if (close(fd) < 0) return -1; + /* Fall out to reopen the file we've created */ + } + + /* + * calling old open, we strip most of the new flags just in case. + */ + return open(path, flags & (O_RDONLY|O_WRONLY|O_RDWR|O_BINARY)); +} +#endif + +#ifdef WANT_MKNOD +#ifdef __MSDOS__ +typedef int dev_t; +#endif +/* Fake mknod by complaining */ +int +mknod(path, mode, dev) + char *path; + unsigned short mode; + dev_t dev; +{ + extern int errno; + int fd; + + errno = ENXIO; /* No such device or address */ + return -1; /* Just give an error */ +} + +/* Fake links by copying */ +int +link(path1, path2) + char *path1; + char *path2; +{ + char buf[256]; + int ifd, ofd; + int nrbytes; + int nwbytes; + + fprintf(stderr, "%s: %s: cannot link to %s, copying instead\n", + tar, path1, path2); + if ((ifd = open(path1, O_RDONLY|O_BINARY)) < 0) + return -1; + if ((ofd = creat(path2, 0666)) < 0) + return -1; + setmode(ofd, O_BINARY); + while ((nrbytes = read(ifd, buf, sizeof(buf))) > 0) { + if ((nwbytes = write(ofd, buf, nrbytes)) != nrbytes) { + nrbytes = -1; + break; + } + } + /* Note use of "|" rather than "||" below: we want to close + * the files even if an error occurs. + */ + if ((nrbytes < 0) | (0 != close(ifd)) | (0 != close(ofd))) { + unlink(path2); + return -1; + } + return 0; +} + +/* everyone owns everything on MS-DOS (or is it no one owns anything?) */ +int +chown(path, uid, gid) + char *path; + int uid; + int gid; +{ + return 0; +} + +int +geteuid() +{ + return 0; +} +#endif /* WANT_MKNOD */ + +#ifdef WANT_UTILS +/* Stash argv[0] here so panic will know what the program is called */ +char *myname = 0; + +void +panic(s) +char *s; +{ + if(myname) + fprintf(stderr,"%s:",myname); + fprintf(stderr,s); + putc('\n',stderr); + exit(12); +} + + +char * +ck_malloc(size) +size_t size; +{ + char *ret; + char *malloc(); + + if(!size) + size++; + ret=malloc(size); + if(ret==0) + panic("Couldn't allocate memory"); + return ret; +} + +char * +ck_realloc(ptr,size) +char *ptr; +size_t size; +{ + char *ret; + char *realloc(); + + if(!ptr) + ret=ck_malloc(size); + else + ret=realloc(ptr,size); + if(ret==0) + panic("Couldn't re-allocate memory"); + return ret; +} + +/* Implement a variable sized buffer of 'stuff'. We don't know what it is, + nor do we care, as long as it doesn't mind being aligned on a char boundry. + */ + +struct buffer { + int allocated; + int length; + char *b; +}; + +#define MIN_ALLOCATE 50 + +char * +init_buffer() +{ + struct buffer *b; + + b=(struct buffer *)ck_malloc(sizeof(struct buffer)); + b->allocated=MIN_ALLOCATE; + b->b=(char *)ck_malloc(MIN_ALLOCATE); + b->length=0; + return (char *)b; +} + +void +flush_buffer(bb) +char *bb; +{ + struct buffer *b; + + b=(struct buffer *)bb; + free(b->b); + b->b=0; + b->allocated=0; + b->length=0; + free((void *)b); +} + +void +add_buffer(bb,p,n) +char *bb; +char *p; +int n; +{ + struct buffer *b; + + b=(struct buffer *)bb; + if(b->length+n>b->allocated) { + b->allocated=b->length+n+MIN_ALLOCATE; + b->b=(char *)ck_realloc(b->b,b->allocated); + } + bcopy(p,b->b+b->length,n); + b->length+=n; +} + +char * +get_buffer(bb) +char *bb; +{ + struct buffer *b; + + b=(struct buffer *)bb; + return b->b; +} + +char * +merge_sort(list,n,off,cmp) +char *list; +int (*cmp)(); +unsigned n; +int off; +{ + char *ret; + + char *alist,*blist; + unsigned alength,blength; + + char *tptr; + int tmp; + char **prev; +#define NEXTOF(ptr) (* ((char **)(((char *)(ptr))+off) ) ) + if(n==1) + return list; + if(n==2) { + if((*cmp)(list,NEXTOF(list))>0) { + ret=NEXTOF(list); + NEXTOF(ret)=list; + NEXTOF(list)=0; + return ret; + } + return list; + } + alist=list; + alength=(n+1)/2; + blength=n/2; + for(tptr=list,tmp=(n-1)/2;tmp;tptr=NEXTOF(tptr),tmp--) + ; + blist=NEXTOF(tptr); + NEXTOF(tptr)=0; + + alist=merge_sort(alist,alength,off,cmp); + blist=merge_sort(blist,blength,off,cmp); + prev = &ret; + for(;alist && blist;) { + if((*cmp)(alist,blist)<0) { + tptr=NEXTOF(alist); + *prev = alist; + prev = &(NEXTOF(alist)); + alist=tptr; + } else { + tptr=NEXTOF(blist); + *prev = blist; + prev = &(NEXTOF(blist)); + blist=tptr; + } + } + if(alist) + *prev = alist; + else + *prev = blist; + + return ret; +} + +void +ck_close(fd) +int fd; +{ + if(close(fd)<0) { + msg_perror("can't close a file #%d",fd); + exit(EX_SYSTEM); + } +} + +#include + +/* Quote_copy_string is like quote_string, but instead of modifying the + string in place, it malloc-s a copy of the string, and returns that. + If the string does not have to be quoted, it returns the NULL string. + The allocated copy can, of course, be freed with free() after the + caller is done with it. + */ +char * +quote_copy_string(string) +char *string; +{ + char *from_here; + char *to_there = 0; + char *copy_buf = 0; + int c; + int copying = 0; + + from_here=string; + while(*from_here) { + c= *from_here++; + if(c=='\\') { + if(!copying) { + int n; + + n=(from_here-string)-1; + copying++; + copy_buf=(char *)malloc(n+5+strlen(from_here)*4); + if(!copy_buf) + return 0; + bcopy(string,copy_buf,n); + to_there=copy_buf+n; + } + *to_there++='\\'; + *to_there++='\\'; + } else if(isprint(c)) { + if(copying) + *to_there++= c; + } else { + if(!copying) { + int n; + + n=(from_here-string)-1; + copying++; + copy_buf=(char *)malloc(n+5+strlen(from_here)*4); + if(!copy_buf) + return 0; + bcopy(string,copy_buf,n); + to_there=copy_buf+n; + } + *to_there++='\\'; + if(c=='\n') *to_there++='n'; + else if(c=='\t') *to_there++='t'; + else if(c=='\f') *to_there++='f'; + else if(c=='\b') *to_there++='b'; + else if(c=='\r') *to_there++='r'; + else if(c=='\177') *to_there++='?'; + else { + to_there[0]=(c>>6)+'0'; + to_there[1]=((c>>3)&07)+'0'; + to_there[2]=(c&07)+'0'; + to_there+=3; + } + } + } + if(copying) { + *to_there='\0'; + return copy_buf; + } + return (char *)0; +} + + +/* Un_quote_string takes a quoted c-string (like those produced by + quote_string or quote_copy_string and turns it back into the + un-quoted original. This is done in place. + */ + +/* There is no un-quote-copy-string. Write it yourself */ + +char *un_quote_string(string) +char *string; +{ + char *ret; + char *from_here; + char *to_there; + int tmp; + + ret=string; + to_there=string; + from_here=string; + while(*from_here) { + if(*from_here!='\\') { + if(from_here!=to_there) + *to_there++= *from_here++; + else + from_here++,to_there++; + continue; + } + switch(*++from_here) { + case '\\': + *to_there++= *from_here++; + break; + case 'n': + *to_there++= '\n'; + from_here++; + break; + case 't': + *to_there++= '\t'; + from_here++; + break; + case 'f': + *to_there++= '\f'; + from_here++; + break; + case 'b': + *to_there++= '\b'; + from_here++; + break; + case 'r': + *to_there++= '\r'; + from_here++; + break; + case '?': + *to_there++= 0177; + from_here++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + tmp= *from_here - '0'; + from_here++; + if(*from_here<'0' || *from_here>'7') { + *to_there++= tmp; + break; + } + tmp= tmp*8 + *from_here-'0'; + from_here++; + if(*from_here<'0' || *from_here>'7') { + *to_there++= tmp; + break; + } + tmp=tmp*8 + *from_here-'0'; + from_here++; + *to_there=tmp; + break; + default: + ret=0; + *to_there++='\\'; + *to_there++= *from_here++; + break; + } + } + if(*to_there) + *to_there++='\0'; + return ret; +} +#endif + +#ifdef WANT_CK_PIPE +void ck_pipe(pipes) +int *pipes; +{ + if(pipe(pipes)<0) { + msg_perror("can't open a pipe"); + exit(EX_SYSTEM); + } +} + +#endif + +#ifdef WANT_GETWD +char * +getwd(path) +char *path; +{ + FILE *fp; + FILE *popen(); + + fp=popen("pwd","r"); + if(!fp) + return 0; + if(!fgets(path,100,fp)) + return 0; + if(!pclose(fp)) + return 0; + return path; +} +#endif /* WANT_CK_PIPE */ + + +#ifdef WANT_STRSTR + +/* + * strstr - find first occurrence of wanted in s + */ + +char * /* found string, or NULL if none */ +strstr(s, wanted) +char *s; +char *wanted; +{ + register char *scan; + register size_t len; + register char firstc; + extern int strcmp(); + + if (*wanted == '\0') + return (char *)0; + /* + * The odd placement of the two tests is so "" is findable. + * Also, we inline the first char for speed. + * The ++ on scan has been moved down for optimization. + */ + firstc = *wanted; + len = strlen(wanted); + for (scan = s; *scan != firstc || strncmp(scan, wanted, len) != 0; ) + if (*scan++ == '\0') + return (char *)0; + return scan; +} +#endif + +#ifdef WANT_FTRUNCATE + +#ifdef F_FREESP +/* code courtesy of William Kucharski */ + +int +ftruncate(fd, length) +int fd; /* file descriptor */ +off_t length; /* length to set file to */ +{ + struct flock fl; + + fl.l_whence = 0; + fl.l_len = 0; + fl.l_start = length; + fl.l_type = F_WRLCK; /* write lock on file space */ + + /* + * This relies on the UNDOCUMENTED F_FREESP argument to + * fcntl(2), which truncates the file so that it ends at the + * position indicated by fl.l_start. + * + * Will minor miracles never cease? + */ + + if (fcntl(fd, F_FREESP, &fl) < 0) + return -1; + + return 0; +} + + +#else +int +ftruncate(fd, length) +int fd; +off_t length; +{ + errno = EIO; + return -1; +} +#endif + +#endif + + + + +extern FILE *msg_file; + +#ifdef STDC_MSG +#include + +void +msg(char *str,...) +{ + va_list args; + + va_start(args,str); + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + vfprintf(stderr,str,args); + va_end(args); + putc('\n',stderr); + fflush(stderr); +} + +void +msg_perror(char *str,...) +{ + va_list args; + int save_e; + extern int errno; + + save_e=errno; + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + va_start(args,str); + vfprintf(stderr,str,args); + va_end(args); + errno=save_e; + perror(" "); + fflush(stderr); +} +#endif + +#ifdef VARARGS_MSG +#include +void msg(str,va_alist) +char *str; +va_dcl +{ + va_list args; + + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + va_start(args); + vfprintf(stderr,str,args); + va_end(args); + putc('\n',stderr); + fflush(stderr); +} + +void msg_perror(str,va_alist) +char *str; +va_dcl +{ + va_list args; + int save_e; + extern int errno; + + save_e=errno; + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + va_start(args); + vfprintf(stderr,str,args); + va_end(args); + errno=save_e; + perror(" "); + fflush(stderr); +} +#endif + +#ifdef DOPRNT_MSG +void msg(str,args) +char *str; +int args; +{ + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + _doprnt(str, &args, stderr); + putc('\n',stderr); + fflush(stderr); +} + +void msg_perror(str,args) +char *str; +{ + int save_e; + extern int errno; + + save_e=errno; + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + _doprnt(str, &args, stderr); + errno=save_e; + perror(" "); + fflush(stderr); +} + +#endif +#ifdef LOSING_MSG +void msg(str,a1,a2,a3,a4,a5,a6) +char *str; +{ + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + fprintf(stderr,str,a1,a2,a3,a4,a5,a6); + putc('\n',stderr); + fflush(stderr); +} + +void msg_perror(str,a1,a2,a3,a4,a5,a6) +char *str; +{ + int save_e; + extern int errno; + + save_e=errno; + fflush(msg_file); + fprintf(stderr,"%s: ",tar); + if(f_sayblock) + fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block)); + fprintf(stderr,str,a1,a2,a3,a4,a5,a6); + fprintf(stderr,": "); + errno=save_e; + perror(" "); +} + +#endif diff --git a/usr/src/usr.bin/tar/port.h b/usr/src/usr.bin/tar/port.h new file mode 100644 index 0000000000..b24643671a --- /dev/null +++ b/usr/src/usr.bin/tar/port.h @@ -0,0 +1,47 @@ +/* Portability declarations. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * Portability declarations for tar. + * + * @(#)port.h 1.3 87/11/11 by John Gilmore, 1986 + */ + +/* + * Everybody does wait() differently. There seem to be no definitions + * for this in V7 (e.g. you are supposed to shift and mask things out + * using constant shifts and masks.) So fuck 'em all -- my own non + * standard but portable macros. Don't change to a "union wait" + * based approach -- the ordering of the elements of the struct + * depends on the byte-sex of the machine. Foo! + */ +#define TERM_SIGNAL(status) ((status) & 0x7F) +#define TERM_COREDUMP(status) (((status) & 0x80) != 0) +#define TERM_VALUE(status) ((status) >> 8) + +#ifdef MSDOS +/* missing things from sys/stat.h */ +#define S_ISUID 0 +#define S_ISGID 0 +#define S_ISVTX 0 + +/* device stuff */ +#define makedev(ma, mi) ((ma << 8) | mi) +#define major(dev) (dev) +#define minor(dev) (dev) +#endif /* MSDOS */ diff --git a/usr/src/usr.bin/tar/regex.c b/usr/src/usr.bin/tar/regex.c new file mode 100644 index 0000000000..5df69ec8f1 --- /dev/null +++ b/usr/src/usr.bin/tar/regex.c @@ -0,0 +1,2783 @@ +/* Extended regular expression matching and search library. + Copyright (C) 1985, 1989-90 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. */ + + +/* To test, compile with -Dtest. This Dtestable feature turns this into + a self-contained program which reads a pattern, describes how it + compiles, then reads a string and searches for it. + + On the other hand, if you compile with both -Dtest and -Dcanned you + can run some tests we've already thought of. */ + + +#ifdef emacs + +/* The `emacs' switch turns on certain special matching commands + that make sense only in emacs. */ + +#include "config.h" +#include "lisp.h" +#include "buffer.h" +#include "syntax.h" + +#else /* not emacs */ + +#if defined (USG) || defined (STDC_HEADERS) +#ifndef BSTRING +#include +#define bcopy(s,d,n) memcpy((d),(s),(n)) +#define bcmp(s1,s2,n) memcmp((s1),(s2),(n)) +#define bzero(s,n) memset((s),0,(n)) +#endif +#endif + +#ifdef STDC_HEADERS +#include +#else +char *malloc (); +char *realloc (); +#endif + +/* Make alloca work the best possible way. */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include +#else +#ifdef _AIX +#pragma alloca +#else +char *alloca (); +#endif +#endif +#endif + + +/* Define the syntax stuff, so we can do the \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +#ifndef Sword +#define Sword 1 +#endif + +#define SYNTAX(c) re_syntax_table[c] + + +#ifdef SYNTAX_TABLE + +char *re_syntax_table; + +#else /* not SYNTAX_TABLE */ + +static char re_syntax_table[256]; + + +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; + + done = 1; +} + +#endif /* SYNTAX_TABLE */ +#endif /* emacs */ + +/* We write fatal error messages on standard error. */ +#include + +/* isalpha(3) etc. are used for the character classes. */ +#include +/* Sequents are missing isgraph. */ +#ifndef isgraph +#define isgraph(c) (isprint((c)) && !isspace((c))) +#endif + +/* Get the interface, including the syntax bits. */ +#include "regex.h" + + +/* These are the command codes that appear in compiled regular + expressions, one per byte. Some command codes 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. + + The value of `exactn' is needed in search.c (search_buffer) in emacs. + So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of + `exactn' we use here must also be 1. */ + +enum regexpcode + { + unused=0, + exactn=1, /* Followed by one byte giving n, then by n literal bytes. */ + begline, /* Fail unless at beginning of line. */ + endline, /* Fail unless at end of line. */ + jump, /* Followed by two bytes giving relative address to jump to. */ + on_failure_jump, /* Followed by two bytes giving relative address of + place to resume at in case of failure. */ + finalize_jump, /* Throw away latest failure point and then jump to + address. */ + maybe_finalize_jump, /* Like jump but finalize if safe to do so. + This is used to jump back to the beginning + of a repeat. If the command that follows + this jump is clearly incompatible with the + one at the beginning of the repeat, such that + we can be sure that there is no use backtracking + out of repetitions already completed, + then we finalize. */ + dummy_failure_jump, /* Jump, 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 + use it as an intermediary kind of jump when + compiling an or construct. */ + succeed_n, /* Used like on_failure_jump except has to succeed n times; + then gets turned into an on_failure_jump. The relative + address following it is useless until then. The + address is followed by two bytes containing n. */ + jump_n, /* Similar to jump, but jump n times only; also the relative + address following is in turn followed by yet two more bytes + containing n. */ + set_number_at, /* Set the following relative location to the + subsequent number. */ + anychar, /* Matches any (more or less) one character. */ + charset, /* 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_not, /* Same parameters as charset, but match any character + that is not one of those specified. */ + start_memory, /* Start remembering the text that is matched, for + storing in a memory register. Followed by one + byte containing the register number. Register numbers + must be in the range 0 through RE_NREGS. */ + stop_memory, /* Stop remembering the text that is matched + and store it in a memory register. Followed by + one byte containing the register number. Register + numbers must be in the range 0 through RE_NREGS. */ + duplicate, /* Match a duplicate of something remembered. + Followed by one byte containing the index of the memory + register. */ + before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ + begbuf, /* Succeeds if at beginning of buffer. */ + endbuf, /* Succeeds if at end of buffer. */ + 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. */ + syntaxspec, /* Matches any character whose syntax is specified. + followed by a byte which contains a syntax code, + e.g., Sword. */ + notsyntaxspec /* Matches any character whose syntax differs from + that specified. */ + }; + + +/* Number of failure points to allocate space for initially, + when matching. If this number is exceeded, more space is allocated, + so it is not a hard limit. */ + +#ifndef NFAILURES +#define NFAILURES 80 +#endif + +#ifdef CHAR_UNSIGNED +#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c)) /* for IBM RT */ +#endif +#ifndef SIGN_EXTEND_CHAR +#define SIGN_EXTEND_CHAR(x) (x) +#endif + + +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ +#define STORE_NUMBER(destination, number) \ + { (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; } + +/* Same as STORE_NUMBER, except increment the destination pointer to + the byte after where the number is stored. Watch out that values for + DESTINATION such as p + 1 won't work, whereas p will. */ +#define STORE_NUMBER_AND_INCR(destination, number) \ + { STORE_NUMBER(destination, number); \ + (destination) += 2; } + + +/* Put into DESTINATION a number stored in two contingous bytes starting + at SOURCE. */ +#define EXTRACT_NUMBER(destination, source) \ + { (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*(char *)((source) + 1)) << 8; } + +/* Same as EXTRACT_NUMBER, except increment the pointer for source to + point to second byte of SOURCE. Note that SOURCE has to be a value + such as p, not, e.g., p + 1. */ +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + { EXTRACT_NUMBER (destination, source); \ + (source) += 2; } + + +/* 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. */ + +int +re_set_syntax (syntax) + int syntax; +{ + int ret; + + ret = obscure_syntax; + obscure_syntax = syntax; + return ret; +} + +/* Set by re_set_syntax to the current regexp syntax to recognize. */ +int obscure_syntax = 0; + + + +/* Macros for re_compile_pattern, which is found below these definitions. */ + +#define CHAR_CLASS_MAX_LENGTH 6 + +/* Fetch the next character in the uncompiled pattern, translating it if + necessary. */ +#define PATFETCH(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (unsigned char *) p++; \ + if (translate) c = translate[c]; } + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + {if (p == pend) goto end_of_pattern; \ + c = * (unsigned char *) p++; } + +#define PATUNFETCH p-- + + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 28 + +/* 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 CH to it. */ +#define BUFPUSH(ch) \ + { \ + GET_BUFFER_SPACE (1); \ + *b++ = (char) (ch); \ + } + +/* Extend the buffer by twice its current size via reallociation and + reset the pointers that pointed into the old allocation to point to + the correct places in the new allocation. If extending the buffer + results in it being larger than 1 << 16, then flag memory exhausted. */ +#define EXTEND_BUFFER \ + { char *old_buffer = bufp->buffer; \ + if (bufp->allocated == (1L<<16)) goto too_big; \ + bufp->allocated *= 2; \ + if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \ + bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated); \ + if (bufp->buffer == 0) \ + goto memory_exhausted; \ + b = (b - old_buffer) + bufp->buffer; \ + if (fixup_jump) \ + fixup_jump = (fixup_jump - old_buffer) + bufp->buffer; \ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } + +/* Set the bit for character C in a character set list. */ +#define SET_LIST_BIT(c) (b[(c) / BYTEWIDTH] |= 1 << ((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); \ + } \ + } \ + } + +/* Subroutines for re_compile_pattern. */ +static void store_jump (), insert_jump (), store_jump_n (), + insert_jump_n (), insert_op_2 (); + + +/* re_compile_pattern takes a regular-expression string + and converts it into a buffer full of byte commands for matching. + + PATTERN is the address of the pattern string + SIZE is the length of it. + BUFP is a struct re_pattern_buffer * which points to the info + on where to store the byte commands. + This structure contains a char * which points to the + actual space, which should have been obtained with malloc. + re_compile_pattern may use realloc to grow the buffer space. + + The number of bytes of commands can be found out by looking in + the `struct re_pattern_buffer' that bufp pointed to, after + re_compile_pattern returns. */ + +char * +re_compile_pattern (pattern, size, bufp) + char *pattern; + int size; + struct re_pattern_buffer *bufp; +{ + register char *b = bufp->buffer; + register char *p = pattern; + char *pend = pattern + size; + register unsigned c, c1; + char *p1; + unsigned char *translate = (unsigned char *) bufp->translate; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell whether a new exact-match + character can be added to that command or requires a new `exactn' + command. */ + + char *pending_exact = 0; + + /* 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. */ + + char *fixup_jump = 0; + + /* Address of start of the most recently finished expression. + This tells postfix * where to find the start of its operand. */ + + char *laststart = 0; + + /* In processing a repeat, 1 means zero matches is allowed. */ + + char zero_times_ok; + + /* In processing a repeat, 1 means many matches is allowed. */ + + char many_times_ok; + + /* Address of beginning of regexp, or inside of last \(. */ + + char *begalt = b; + + /* In processing an interval, at least this many matches must be made. */ + int lower_bound; + + /* In processing an interval, at most this many matches can be made. */ + int upper_bound; + + /* Place in pattern (i.e., the {) to which to go back if the interval + is invalid. */ + char *beg_interval = 0; + + /* Stack of information saved by \( and restored by \). + Four stack elements are pushed by each \(: + First, the value of b. + Second, the value of fixup_jump. + Third, the value of regnum. + Fourth, the value of begalt. */ + + int stackb[40]; + int *stackp = stackb; + int *stacke = stackb + 40; + int *stackt; + + /* Counts \('s as they are encountered. Remembered for the matching \), + where it becomes the register number to put in the stop_memory + command. */ + + int regnum = 1; + + bufp->fastmap_accurate = 0; + +#ifndef emacs +#ifndef SYNTAX_TABLE + /* Initialize the syntax table. */ + init_syntax_once(); +#endif +#endif + + if (bufp->allocated == 0) + { + bufp->allocated = INIT_BUF_SIZE; + if (bufp->buffer) + /* EXTEND_BUFFER loses when bufp->allocated is 0. */ + bufp->buffer = (char *) realloc (bufp->buffer, INIT_BUF_SIZE); + else + /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = (char *) malloc (INIT_BUF_SIZE); + if (!bufp->buffer) goto memory_exhausted; + begalt = b = bufp->buffer; + } + + while (p != pend) + { + PATFETCH (c); + + switch (c) + { + case '$': + { + char *p1 = p; + /* When testing what follows the $, + look past the \-constructs that don't consume anything. */ + if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + while (p1 != pend) + { + if (*p1 == '\\' && p1 + 1 != pend + && (p1[1] == '<' || p1[1] == '>' + || p1[1] == '`' || p1[1] == '\'' +#ifdef emacs + || p1[1] == '=' +#endif + || p1[1] == 'b' || p1[1] == 'B')) + p1 += 2; + else + break; + } + if (obscure_syntax & RE_TIGHT_VBAR) + { + if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p1 != pend) + goto normal_char; + /* Make operand of last vbar end before this `$'. */ + if (fixup_jump) + store_jump (fixup_jump, jump, b); + fixup_jump = 0; + BUFPUSH (endline); + break; + } + /* $ means succeed if at end of line, but only in special contexts. + If validly in the middle of a pattern, it is a normal character. */ + + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && p1 != pend) + goto invalid_pattern; + if (p1 == pend || *p1 == '\n' + || (obscure_syntax & RE_CONTEXT_INDEP_OPS) + || (obscure_syntax & RE_NO_BK_PARENS + ? *p1 == ')' + : *p1 == '\\' && p1[1] == ')') + || (obscure_syntax & RE_NO_BK_VBAR + ? *p1 == '|' + : *p1 == '\\' && p1[1] == '|')) + { + BUFPUSH (endline); + break; + } + goto normal_char; + } + case '^': + /* ^ means succeed if at beg of line, but only if no preceding + pattern. */ + + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) && laststart) + goto invalid_pattern; + if (laststart && p - 2 >= pattern && p[-2] != '\n' + && !(obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + if (obscure_syntax & RE_TIGHT_VBAR) + { + if (p != pattern + 1 + && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + BUFPUSH (begline); + begalt = b; + } + else + BUFPUSH (begline); + break; + + case '+': + case '?': + if ((obscure_syntax & RE_BK_PLUS_QM) + || (obscure_syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern, char not special. */ + if (!laststart) + { + if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + /* If there is a sequence of repetition chars, + collapse it down to just one. */ + zero_times_ok = 0; + many_times_ok = 0; + while (1) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + if (p == pend) + break; + PATFETCH (c); + if (c == '*') + ; + else if (!(obscure_syntax & RE_BK_PLUS_QM) + && (c == '+' || c == '?')) + ; + else if ((obscure_syntax & RE_BK_PLUS_QM) + && c == '\\') + { + int c1; + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + c = c1; + } + else + { + PATUNFETCH; + break; + } + } + + /* 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) + { + /* If more than one repetition is allowed, 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). */ + GET_BUFFER_SPACE (3); + store_jump (b, maybe_finalize_jump, laststart - 3); + b += 3; /* Because store_jump put stuff here. */ + } + /* 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 (on_failure_jump, laststart, b + 3, b); + pending_exact = 0; + b += 3; + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + dummy-failure 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 (6); + insert_jump (dummy_failure_jump, laststart, laststart + 6, b); + b += 3; + } + break; + + case '.': + laststart = b; + BUFPUSH (anychar); + break; + + case '[': + if (p == pend) + goto invalid_pattern; + while (b - bufp->buffer + > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH) + EXTEND_BUFFER; + + laststart = b; + if (*p == '^') + { + BUFPUSH (charset_not); + p++; + } + else + BUFPUSH (charset); + p1 = p; + + BUFPUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + /* Clear the whole map */ + bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH); + + if ((obscure_syntax & RE_HAT_NOT_NEWLINE) && b[-2] == charset_not) + SET_LIST_BIT ('\n'); + + + /* Read in characters and ranges, setting map bits. */ + while (1) + { + /* Don't translate while fetching, in case it's a range bound. + When we set the bit for the character, we translate it. */ + PATFETCH_RAW (c); + + /* If set, \ escapes characters when inside [...]. */ + if ((obscure_syntax & RE_AWK_CLASS_HACK) && c == '\\') + { + PATFETCH(c1); + SET_LIST_BIT (c1); + continue; + } + if (c == ']') + { + if (p == p1 + 1) + { + /* If this is an empty bracket expression. */ + if ((obscure_syntax & RE_NO_EMPTY_BRACKETS) + && p == pend) + goto invalid_pattern; + } + else + /* Stop if this isn't merely a ] inside a bracket + expression, but rather the end of a bracket + expression. */ + break; + } + /* Get a range. */ + if (p[0] == '-' && p[1] != ']') + { + PATFETCH (c1); + /* Don't translate the range bounds while fetching them. */ + PATFETCH_RAW (c1); + + if ((obscure_syntax & RE_NO_EMPTY_RANGES) && c > c1) + goto invalid_pattern; + + if ((obscure_syntax & RE_NO_HYPHEN_RANGE_END) + && c1 == '-' && *p != ']') + goto invalid_pattern; + + while (c <= c1) + { + /* Translate each char that's in the range. */ + if (translate) + SET_LIST_BIT (translate[c]); + else + SET_LIST_BIT (c); + c++; + } + } + else if ((obscure_syntax & RE_CHAR_CLASSES) + && c == '[' && p[0] == ':') + { + /* Longest valid character class word has six characters. */ + char str[CHAR_CLASS_MAX_LENGTH]; + PATFETCH (c); + c1 = 0; + /* If no ] at end. */ + if (p == pend) + goto invalid_pattern; + while (1) + { + /* Don't translate the ``character class'' characters. */ + PATFETCH_RAW (c); + if (c == ':' || c == ']' || p == pend + || c1 == CHAR_CLASS_MAX_LENGTH) + break; + str[c1++] = c; + } + str[c1] = '\0'; + if (p == pend + || c == ']' /* End of the bracket expression. */ + || p[0] != ']' + || p + 1 == pend + || (strcmp (str, "alpha") != 0 + && strcmp (str, "upper") != 0 + && strcmp (str, "lower") != 0 + && strcmp (str, "digit") != 0 + && strcmp (str, "alnum") != 0 + && strcmp (str, "xdigit") != 0 + && strcmp (str, "space") != 0 + && strcmp (str, "print") != 0 + && strcmp (str, "punct") != 0 + && strcmp (str, "graph") != 0 + && strcmp (str, "cntrl") != 0)) + { + /* Undo the ending character, the letters, and leave + the leading : and [ (but set bits for them). */ + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + } + else + { + /* The ] at the end of the character class. */ + PATFETCH (c); + if (c != ']') + goto invalid_pattern; + for (c = 0; c < (1 << BYTEWIDTH); c++) + { + if ((strcmp (str, "alpha") == 0 && isalpha (c)) + || (strcmp (str, "upper") == 0 && isupper (c)) + || (strcmp (str, "lower") == 0 && islower (c)) + || (strcmp (str, "digit") == 0 && isdigit (c)) + || (strcmp (str, "alnum") == 0 && isalnum (c)) + || (strcmp (str, "xdigit") == 0 && isxdigit (c)) + || (strcmp (str, "space") == 0 && isspace (c)) + || (strcmp (str, "print") == 0 && isprint (c)) + || (strcmp (str, "punct") == 0 && ispunct (c)) + || (strcmp (str, "graph") == 0 && isgraph (c)) + || (strcmp (str, "cntrl") == 0 && iscntrl (c))) + SET_LIST_BIT (c); + } + } + } + else if (translate) + SET_LIST_BIT (translate[c]); + else + SET_LIST_BIT (c); + } + + /* Discard any character set/class bitmap bytes that are all + 0 at the end of the map. Decrement the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + break; + + case '(': + if (! (obscure_syntax & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_open; + + case ')': + if (! (obscure_syntax & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_close; + + case '\n': + if (! (obscure_syntax & RE_NEWLINE_OR)) + goto normal_char; + else + goto handle_bar; + + case '|': + if ((obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + && (! laststart || p == pend)) + goto invalid_pattern; + else if (! (obscure_syntax & RE_NO_BK_VBAR)) + goto normal_char; + else + goto handle_bar; + + case '{': + if (! ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + && (obscure_syntax & RE_INTERVALS))) + goto normal_char; + else + goto handle_interval; + + case '\\': + if (p == pend) goto invalid_pattern; + PATFETCH_RAW (c); + switch (c) + { + case '(': + if (obscure_syntax & RE_NO_BK_PARENS) + goto normal_backsl; + handle_open: + if (stackp == stacke) goto nesting_too_deep; + + /* Laststart should point to the start_memory that we are about + to push (unless the pattern has RE_NREGS or more ('s). */ + *stackp++ = b - bufp->buffer; + if (regnum < RE_NREGS) + { + BUFPUSH (start_memory); + BUFPUSH (regnum); + } + *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; + *stackp++ = regnum++; + *stackp++ = begalt - bufp->buffer; + fixup_jump = 0; + laststart = 0; + begalt = b; + break; + + case ')': + if (obscure_syntax & RE_NO_BK_PARENS) + goto normal_backsl; + handle_close: + if (stackp == stackb) goto unmatched_close; + begalt = *--stackp + bufp->buffer; + if (fixup_jump) + store_jump (fixup_jump, jump, b); + if (stackp[-1] < RE_NREGS) + { + BUFPUSH (stop_memory); + BUFPUSH (stackp[-1]); + } + stackp -= 2; + fixup_jump = *stackp ? *stackp + bufp->buffer - 1 : 0; + laststart = *--stackp + bufp->buffer; + break; + + case '|': + if ((obscure_syntax & RE_LIMITED_OPS) + || (obscure_syntax & RE_NO_BK_VBAR)) + goto normal_backsl; + handle_bar: + if (obscure_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 (6); + insert_jump (on_failure_jump, begalt, b + 6, b); + pending_exact = 0; + b += 3; + /* The alternative before the previous alternative has a + jump after it which gets executed if it gets matched. + Adjust that jump so it will jump to the previous + 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. */ + if (fixup_jump) + store_jump (fixup_jump, jump, b); + + /* Leave space for a jump after previous alternative---to be + filled in later. */ + fixup_jump = b; + b += 3; + + laststart = 0; + begalt = b; + break; + + case '{': + if (! (obscure_syntax & RE_INTERVALS) + /* Let \{ be a literal. */ + || ((obscure_syntax & RE_INTERVALS) + && (obscure_syntax & RE_NO_BK_CURLY_BRACES)) + /* If it's the string "\{". */ + || (p - 2 == pattern && p == pend)) + goto normal_backsl; + handle_interval: + beg_interval = p - 1; /* The {. */ + /* If there is no previous pattern, this isn't an interval. */ + if (!laststart) + { + if (obscure_syntax & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else + goto normal_backsl; + } + /* It also isn't an interval if not preceded by an re + matching a single character or subexpression, or if + the current type of intervals can't handle back + references and the previous thing is a back reference. */ + if (! (*laststart == anychar + || *laststart == charset + || *laststart == charset_not + || *laststart == start_memory + || (*laststart == exactn && laststart[1] == 1) + || (! (obscure_syntax & RE_NO_BK_REFS) + && *laststart == duplicate))) + { + if (obscure_syntax & RE_NO_BK_CURLY_BRACES) + goto normal_char; + + /* Posix extended syntax is handled in previous + statement; this is for Posix basic syntax. */ + if (obscure_syntax & RE_INTERVALS) + goto invalid_pattern; + + goto normal_backsl; + } + lower_bound = -1; /* So can see if are set. */ + upper_bound = -1; + GET_UNSIGNED_NUMBER (lower_bound); + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) + upper_bound = RE_DUP_MAX; + } + if (upper_bound < 0) + upper_bound = lower_bound; + if (! (obscure_syntax & RE_NO_BK_CURLY_BRACES)) + { + if (c != '\\') + goto invalid_pattern; + PATFETCH (c); + } + if (c != '}' || lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound + || ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + && p != pend && *p == '{')) + { + if (obscure_syntax & RE_NO_BK_CURLY_BRACES) + goto unfetch_interval; + else + goto invalid_pattern; + } + + /* If 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 this jump is inserted. */ + + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + insert_jump (jump, laststart, b + 3, b); + b += 3; + } + + /* Otherwise, after lower_bound number of succeeds, jump + to after the jump_n which will be inserted at the end + of the buffer, and insert that jump_n. */ + else + { /* Set to 5 if only one repetition is allowed and + hence no jump_n is inserted at the current end of + the buffer; then only space for the succeed_n is + needed. Otherwise, need space for both the + succeed_n and the jump_n. */ + + unsigned slots_needed = upper_bound == 1 ? 5 : 10; + + GET_BUFFER_SPACE (slots_needed); + /* Initialize the succeed_n to n, even though it will + be set by its attendant set_number_at, because + re_compile_fastmap will need to know it. Jump to + what the end of buffer will be after inserting + this succeed_n and possibly appending a jump_n. */ + insert_jump_n (succeed_n, laststart, b + slots_needed, + b, lower_bound); + b += 5; /* Just increment for the succeed_n here. */ + + /* More than one repetition is allowed, so put in at + the end of the buffer a backward jump from b to the + succeed_n we put in above. By the time we've gotten + to this jump when matching, we'll have matched once + already, so jump back only upper_bound - 1 times. */ + + if (upper_bound > 1) + { + store_jump_n (b, jump_n, laststart, upper_bound - 1); + b += 5; + /* When hit this when matching, reset the + preceding jump_n's n to upper_bound - 1. */ + BUFPUSH (set_number_at); + GET_BUFFER_SPACE (2); + STORE_NUMBER_AND_INCR (b, -5); + STORE_NUMBER_AND_INCR (b, upper_bound - 1); + } + /* When hit this when matching, set the succeed_n's n. */ + GET_BUFFER_SPACE (5); + insert_op_2 (set_number_at, laststart, b, 5, lower_bound); + b += 5; + } + pending_exact = 0; + beg_interval = 0; + break; + + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + if (beg_interval) + p = beg_interval; + else + { + fprintf (stderr, + "regex: no interval beginning to which to backtrack.\n"); + exit (1); + } + + beg_interval = 0; + PATFETCH (c); /* normal_char expects char in `c'. */ + goto normal_char; + break; + +#ifdef emacs + case '=': + BUFPUSH (at_dot); + break; + + case 's': + laststart = b; + BUFPUSH (syntaxspec); + PATFETCH (c); + BUFPUSH (syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + BUFPUSH (notsyntaxspec); + PATFETCH (c); + BUFPUSH (syntax_spec_code[c]); + break; +#endif /* emacs */ + + case 'w': + laststart = b; + BUFPUSH (wordchar); + break; + + case 'W': + laststart = b; + BUFPUSH (notwordchar); + break; + + case '<': + BUFPUSH (wordbeg); + break; + + case '>': + BUFPUSH (wordend); + break; + + case 'b': + BUFPUSH (wordbound); + break; + + case 'B': + BUFPUSH (notwordbound); + break; + + case '`': + BUFPUSH (begbuf); + break; + + case '\'': + BUFPUSH (endbuf); + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (obscure_syntax & RE_NO_BK_REFS) + goto normal_char; + c1 = c - '0'; + if (c1 >= regnum) + { + if (obscure_syntax & RE_NO_EMPTY_BK_REF) + goto invalid_pattern; + else + goto normal_char; + } + /* Can't back reference to a subexpression if inside of it. */ + for (stackt = stackp - 2; stackt > stackb; stackt -= 4) + if (*stackt == c1) + goto normal_char; + laststart = b; + BUFPUSH (duplicate); + BUFPUSH (c1); + break; + + case '+': + case '?': + if (obscure_syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backsl; + break; + + default: + normal_backsl: + /* 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. */ + if (translate) c = translate[c]; + goto normal_char; + } + break; + + default: + normal_char: /* Expects the character in `c'. */ + if (!pending_exact || pending_exact + *pending_exact + 1 != b + || *pending_exact == 0177 || *p == '*' || *p == '^' + || ((obscure_syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((obscure_syntax & RE_INTERVALS) + && ((obscure_syntax & RE_NO_BK_CURLY_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + laststart = b; + BUFPUSH (exactn); + pending_exact = b; + BUFPUSH (0); + } + BUFPUSH (c); + (*pending_exact)++; + } + } + + if (fixup_jump) + store_jump (fixup_jump, jump, b); + + if (stackp != stackb) goto unmatched_open; + + bufp->used = b - bufp->buffer; + return 0; + + invalid_pattern: + return "Invalid regular expression"; + + unmatched_open: + return "Unmatched \\("; + + unmatched_close: + return "Unmatched \\)"; + + end_of_pattern: + return "Premature end of regular expression"; + + nesting_too_deep: + return "Nesting too deep"; + + too_big: + return "Regular expression too big"; + + memory_exhausted: + return "Memory exhausted"; +} + + +/* Store a jump of the form . + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store. */ + +static void +store_jump (from, opcode, to) + char *from, *to; + char opcode; +{ + from[0] = opcode; + STORE_NUMBER(from + 1, to - (from + 3)); +} + + +/* Open up space before char FROM, and insert there a jump to TO. + CURRENT_END gives the end of the storage not in use, so we know + how much data to copy up. OP is the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_jump (op, from, to, current_end) + char op; + char *from, *to, *current_end; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 3; /* ...to here. */ + + while (pfrom != from) + *--pto = *--pfrom; + store_jump (from, op, to); +} + + +/* Store a jump of the form . + + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store, N is a number the + jump uses, say, to decide how many times to jump. + + If you call this function, you must zero out pending_exact. */ + +static void +store_jump_n (from, opcode, to, n) + char *from, *to; + char opcode; + unsigned n; +{ + from[0] = opcode; + STORE_NUMBER (from + 1, to - (from + 3)); + STORE_NUMBER (from + 3, n); +} + + +/* Similar to insert_jump, but handles a jump which needs an extra + number to handle minimum and maximum cases. Open up space at + location FROM, and insert there a jump to TO. CURRENT_END gives the + end of the storage in use, so we know how much data to copy up. OP is + the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_jump_n (op, from, to, current_end, n) + char op; + char *from, *to, *current_end; + unsigned n; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != from) + *--pto = *--pfrom; + store_jump_n (from, op, to, n); +} + + +/* Open up space at location THERE, and insert operation OP followed by + NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so + we know how much data to copy up. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_op_2 (op, there, current_end, num_1, num_2) + char op; + char *there, *current_end; + int num_1, num_2; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != there) + *--pto = *--pfrom; + + there[0] = op; + STORE_NUMBER (there + 1, num_1); + STORE_NUMBER (there + 3, num_2); +} + + + +/* Given a pattern, compute a fastmap from it. The 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 totally implausible text. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as bufp->fastmap. + The other components of bufp describe the pattern to be used. */ + +void +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *pattern = (unsigned char *) bufp->buffer; + int size = bufp->used; + register char *fastmap = bufp->fastmap; + register unsigned char *p = pattern; + register unsigned char *pend = pattern + size; + register int j, k; + unsigned char *translate = (unsigned char *) bufp->translate; + + unsigned char *stackb[NFAILURES]; + unsigned char **stackp = stackb; + + unsigned is_a_succeed_n; + + bzero (fastmap, (1 << BYTEWIDTH)); + bufp->fastmap_accurate = 1; + bufp->can_be_null = 0; + + while (p) + { + is_a_succeed_n = 0; + if (p == pend) + { + bufp->can_be_null = 1; + break; + } +#ifdef SWITCH_ENUM_BUG + switch ((int) ((enum regexpcode) *p++)) +#else + switch ((enum regexpcode) *p++) +#endif + { + case exactn: + if (translate) + fastmap[translate[p[1]]] = 1; + else + fastmap[p[1]] = 1; + break; + + case begline: + case before_dot: + case at_dot: + case after_dot: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + continue; + + case endline: + if (translate) + fastmap[translate['\n']] = 1; + else + fastmap['\n'] = 1; + + if (bufp->can_be_null != 1) + bufp->can_be_null = 2; + break; + + case jump_n: + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (j > 0) + continue; + /* Jump backward reached implies we just went through + the body of a loop and matched nothing. + Opcode jumped to should be an on_failure_jump. + Just treat it like an ordinary jump. + For a * loop, it has pushed its failure point already; + If so, discard that as redundant. */ + + if ((enum regexpcode) *p != on_failure_jump + && (enum regexpcode) *p != succeed_n) + continue; + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (stackp != stackb && *stackp == p) + stackp--; + continue; + + case on_failure_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + *++stackp = p + j; + if (is_a_succeed_n) + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + continue; + + case succeed_n: + is_a_succeed_n = 1; + /* 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; + goto handle_on_failure_jump; + } + continue; + + case set_number_at: + p += 4; + continue; + + case start_memory: + case stop_memory: + p++; + continue; + + case duplicate: + bufp->can_be_null = 1; + fastmap['\n'] = 1; + case anychar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (j != '\n') + fastmap[j] = 1; + if (bufp->can_be_null) + return; + /* Don't return; check the alternative paths + so we can set can_be_null if appropriate. */ + 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; + +#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; +#endif /* not emacs */ + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + { + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + } + break; + + case charset_not: + /* Chars beyond end of map must be allowed */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + { + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + } + break; + } + + /* Get here means we have successfully found the possible starting + characters of one path of the pattern. We need not follow this + path any farther. Instead, look at the next alternative + remembered in the stack. */ + if (stackp != stackb) + p = *stackp--; + else + break; + } +} + + + +/* Like re_search_2, below, but only one string is specified, and + doesn't let you say where to stop matching. */ + +int +re_search (pbufp, string, size, startpos, range, regs) + struct re_pattern_buffer *pbufp; + char *string; + int size, startpos, range; + struct re_registers *regs; +{ + return re_search_2 (pbufp, (char *) 0, 0, string, size, startpos, range, + regs, size); +} + + +/* Using the compiled pattern in PBUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. RANGE is the number of + places to try before giving up. If RANGE is negative, it searches + backwards, i.e., the starting positions tried are STARTPOS, STARTPOS + - 1, etc. STRING1 and STRING2 are of SIZE1 and SIZE2, respectively. + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire PBUFP->buffer and its contained + subexpressions. Do not consider matching one past the index MSTOP in + the virtual concatenation of STRING1 and STRING2. + + The value returned is the position in the strings at which the match + was found, or -1 if no match was found, or -2 if error (such as + failure stack overflow). */ + +int +re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, + regs, mstop) + struct re_pattern_buffer *pbufp; + char *string1, *string2; + int size1, size2; + int startpos; + register int range; + struct re_registers *regs; + int mstop; +{ + register char *fastmap = pbufp->fastmap; + register unsigned char *translate = (unsigned char *) pbufp->translate; + int total_size = size1 + size2; + int endpos = startpos + range; + int val; + + /* Check for out-of-range starting position. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up range if it would eventually take startpos outside of the + virtual concatenation of string1 and string2. */ + if (endpos < -1) + range = -1 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* Update the fastmap now if not correct already. */ + if (fastmap && !pbufp->fastmap_accurate) + re_compile_fastmap (pbufp); + + /* If the search isn't to be a backwards one, don't waste time in a + long search for a pattern that says it is anchored. */ + if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf + && range > 0) + { + if (startpos > 0) + return -1; + else + range = 1; + } + + while (1) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot possibly be the start of a match. Note, however, that + if the pattern can possibly match the null string, we must + test it at each starting point so that we take the first null + string we get. */ + + if (fastmap && startpos < total_size && pbufp->can_be_null != 1) + { + if (range > 0) /* Searching forwards. */ + { + register int lim = 0; + register unsigned char *p; + int irange = range; + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + + p = ((unsigned char *) + &(startpos >= size1 ? string2 - size1 : string1)[startpos]); + + while (range > lim && !fastmap[translate + ? translate[*p++] + : *p++]) + range--; + startpos += irange - range; + } + else /* Searching backwards. */ + { + register unsigned char c; + + if (string1 == 0 || startpos >= size1) + c = string2[startpos - size1]; + else + c = string1[startpos]; + + c &= 0xff; + if (translate ? !fastmap[translate[c]] : !fastmap[c]) + goto advance; + } + } + + if (range >= 0 && startpos == total_size + && fastmap && pbufp->can_be_null == 0) + return -1; + + val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, + regs, mstop); + if (val >= 0) + return startpos; + if (val == -2) + return -2; + +#ifdef C_ALLOCA + alloca (0); +#endif /* C_ALLOCA */ + + advance: + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } + } + return -1; +} + + + +#ifndef emacs /* emacs never uses this. */ +int +re_match (pbufp, string, size, pos, regs) + struct re_pattern_buffer *pbufp; + char *string; + int size, pos; + struct re_registers *regs; +{ + return re_match_2 (pbufp, (char *) 0, 0, string, size, pos, regs, size); +} +#endif /* not emacs */ + + +/* The following are used for re_match_2, defined below: */ + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always pushed MAX_NUM_FAILURE_ITEMS each time we failed. */ + +int re_max_failures = 2000; + +/* Routine used by re_match_2. */ +static int bcmp_translate (); + + +/* Structure and accessing macros used in re_match_2: */ + +struct register_info +{ + unsigned is_active : 1; + unsigned matched_something : 1; +}; + +#define IS_ACTIVE(R) ((R).is_active) +#define MATCHED_SOMETHING(R) ((R).matched_something) + + +/* Macros used by re_match_2: */ + + +/* I.e., regstart, regend, and reg_info. */ + +#define NUM_REG_ITEMS 3 + +/* We push at most this many things on the stack whenever we + fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are + arguments to the PUSH_FAILURE_POINT macro. */ + +#define MAX_NUM_FAILURE_ITEMS (RE_NREGS * NUM_REG_ITEMS + 2) + + +/* We push this many things on the stack whenever we fail. */ + +#define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + 2) + + +/* This pushes most of the information about the current state we will want + if we ever fail back to it. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place) \ + { \ + short last_used_reg, this_reg; \ + \ + /* Find out how many registers are active or have been matched. \ + (Aside from register zero, which is only set at the end.) */ \ + for (last_used_reg = RE_NREGS - 1; last_used_reg > 0; last_used_reg--)\ + if (regstart[last_used_reg] != (unsigned char *) -1) \ + break; \ + \ + if (stacke - stackp < NUM_FAILURE_ITEMS) \ + { \ + unsigned char **stackx; \ + unsigned int len = stacke - stackb; \ + if (len > re_max_failures * MAX_NUM_FAILURE_ITEMS) \ + return -2; \ + \ + /* Roughly double the size of the stack. */ \ + stackx = (unsigned char **) alloca (2 * len \ + * sizeof (unsigned char *));\ + /* Only copy what is in use. */ \ + bcopy (stackb, stackx, len * sizeof (char *)); \ + stackp = stackx + (stackp - stackb); \ + stackb = stackx; \ + stacke = stackb + 2 * len; \ + } \ + \ + /* Now push the info for each of those registers. */ \ + for (this_reg = 1; this_reg <= last_used_reg; this_reg++) \ + { \ + *stackp++ = regstart[this_reg]; \ + *stackp++ = regend[this_reg]; \ + *stackp++ = (unsigned char *) ®_info[this_reg]; \ + } \ + \ + /* Push how many registers we saved. */ \ + *stackp++ = (unsigned char *) last_used_reg; \ + \ + *stackp++ = pattern_place; \ + *stackp++ = string_place; \ + } + + +/* This pops what PUSH_FAILURE_POINT pushes. */ + +#define POP_FAILURE_POINT() \ + { \ + int temp; \ + stackp -= 2; /* Remove failure points. */ \ + temp = (int) *--stackp; /* How many regs pushed. */ \ + temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \ + stackp -= temp; /* Remove the register info. */ \ + } + + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Is true if there is a first string and if PTR is pointing anywhere + inside it or just past the end. */ + +#define IS_IN_FIRST_STRING(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* 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; \ + } + + +/* Call this when have matched something; it sets `matched' flags for the + registers corresponding to the subexpressions of which we currently + are inside. */ +#define SET_REGS_MATCHED \ + { unsigned this_reg; \ + for (this_reg = 0; this_reg < RE_NREGS; this_reg++) \ + { \ + if (IS_ACTIVE(reg_info[this_reg])) \ + MATCHED_SOMETHING(reg_info[this_reg]) = 1; \ + else \ + MATCHED_SOMETHING(reg_info[this_reg]) = 0; \ + } \ + } + +/* Test if at very beginning or at very end of the virtual concatenation + of string1 and string2. If there is only one string, we've put it in + string2. */ + +#define AT_STRINGS_BEG (d == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END (d == end2) + +#define AT_WORD_BOUNDARY \ + (AT_STRINGS_BEG || AT_STRINGS_END || IS_A_LETTER (d - 1) != IS_A_LETTER (d)) + +/* We have two special cases to check for: + 1) if we're past the end of string1, we have to look at the first + character in string2; + 2) if we're before the beginning of string2, we have to look at the + last character in string1; we assume there is a string1, so use + this in conjunction with AT_STRINGS_BEG. */ +#define IS_A_LETTER(d) \ + (SYNTAX ((d) == end1 ? *string2 : (d) == string2 - 1 ? *(end1 - 1) : *(d))\ + == Sword) + + +/* Match the pattern described by PBUFP against the virtual + concatenation of STRING1 and STRING2, which are of SIZE1 and SIZE2, + respectively. Start the match at index POS in the virtual + concatenation of STRING1 and STRING2. In REGS, return the indices of + the virtual concatenation of STRING1 and STRING2 that matched the + entire PBUFP->buffer and its contained subexpressions. Do not + consider matching one past the index MSTOP in the virtual + concatenation of STRING1 and STRING2. + + If pbufp->fastmap is nonzero, then it had better be up to date. + + The reason that the data to match are specified as two components + which are to be regarded as concatenated is so this function can be + used directly on the contents of an Emacs buffer. + + -1 is returned if there is no match. -2 is returned if there is an + error (such as match stack overflow). Otherwise the value is the + length of the substring which was matched. */ + +int +re_match_2 (pbufp, string1_arg, size1, string2_arg, size2, pos, regs, mstop) + struct re_pattern_buffer *pbufp; + char *string1_arg, *string2_arg; + int size1, size2; + int pos; + struct re_registers *regs; + int mstop; +{ + register unsigned char *p = (unsigned char *) pbufp->buffer; + + /* Pointer to beyond end of buffer. */ + register unsigned char *pend = p + pbufp->used; + + unsigned char *string1 = (unsigned char *) string1_arg; + unsigned char *string2 = (unsigned char *) string2_arg; + unsigned char *end1; /* Just past end of first string. */ + unsigned char *end2; /* Just past end of second string. */ + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ + unsigned char *end_match_1, *end_match_2; + + register unsigned char *d, *dend; + register int mcnt; /* Multipurpose. */ + unsigned char *translate = (unsigned char *) pbufp->translate; + unsigned is_a_jump_n = 0; + + /* 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. */ + + unsigned char *initial_stack[MAX_NUM_FAILURE_ITEMS * NFAILURES]; + unsigned char **stackb = initial_stack; + unsigned char **stackp = stackb; + unsigned char **stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES]; + + + /* 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.) */ + + unsigned char *regstart[RE_NREGS]; + unsigned char *regend[RE_NREGS]; + + /* 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. */ + + struct register_info reg_info[RE_NREGS]; + + + /* 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 = 0; + unsigned char *best_regstart[RE_NREGS]; + unsigned char *best_regend[RE_NREGS]; + + /* Initialize subexpression text positions to -1 to mark ones that no + \( or ( and \) or ) has been seen for. Also set all registers to + inactive and mark them as not having matched anything or ever + failed. */ + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + { + regstart[mcnt] = regend[mcnt] = (unsigned char *) -1; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + if (regs) + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; + + /* Set up pointers to ends of strings. + Don't allow the second string to be empty unless both are empty. */ + if (size2 == 0) + { + string2 = string1; + size2 = size1; + string1 = 0; + size1 = 0; + } + end1 = string1 + size1; + end2 = string2 + size2; + + /* Compute where to stop matching, within the two strings. */ + if (mstop <= size1) + { + end_match_1 = string1 + mstop; + end_match_2 = string2; + } + else + { + end_match_1 = end1; + end_match_2 = string2 + mstop - 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; + + + /* This loops over pattern commands. It exits by returning from the + function if match is complete, or it drops through if match fails + at this starting point in the input data. */ + + while (1) + { + is_a_jump_n = 0; + /* End of pattern means we might have succeeded. */ + if (p == pend) + { + /* If not end of string, try backtracking. Otherwise done. */ + if (d != end_match_2) + { + if (stackp != stackb) + { + /* More failure points to try. */ + + unsigned in_same_string = + IS_IN_FIRST_STRING (best_regend[0]) + == MATCHING_IN_FIRST_STRING; + + /* If exceeds best match so far, save it. */ + if (! best_regs_set + || (in_same_string && d > best_regend[0]) + || (! in_same_string && ! MATCHING_IN_FIRST_STRING)) + { + best_regs_set = 1; + best_regend[0] = d; /* Never use regstart[0]. */ + + for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + /* If no failure points, don't restore garbage. */ + else if (best_regs_set) + { + restore_best_regs: + /* Restore best match. */ + d = best_regend[0]; + + for (mcnt = 0; mcnt < RE_NREGS; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } + + /* If caller wants register contents data back, convert it + to indices. */ + if (regs) + { + regs->start[0] = pos; + if (MATCHING_IN_FIRST_STRING) + regs->end[0] = d - string1; + else + regs->end[0] = d - string2 + size1; + for (mcnt = 1; mcnt < RE_NREGS; mcnt++) + { + if (regend[mcnt] == (unsigned char *) -1) + { + regs->start[mcnt] = -1; + regs->end[mcnt] = -1; + continue; + } + if (IS_IN_FIRST_STRING (regstart[mcnt])) + regs->start[mcnt] = regstart[mcnt] - string1; + else + regs->start[mcnt] = regstart[mcnt] - string2 + size1; + + if (IS_IN_FIRST_STRING (regend[mcnt])) + regs->end[mcnt] = regend[mcnt] - string1; + else + regs->end[mcnt] = regend[mcnt] - string2 + size1; + } + } + return d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + } + + /* Otherwise match next pattern command. */ +#ifdef SWITCH_ENUM_BUG + switch ((int) ((enum regexpcode) *p++)) +#else + switch ((enum regexpcode) *p++) +#endif + { + + /* \( [or `(', as appropriate] is represented by start_memory, + \) by stop_memory. Both of those commands are followed by + a register number in the next byte. The text matched + within the \( and \) is recorded under that number. */ + case start_memory: + regstart[*p] = d; + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + p++; + break; + + case stop_memory: + regend[*p] = d; + IS_ACTIVE (reg_info[*p]) = 0; + + /* If just failed to match something this time around with a sub- + expression that's in a loop, try to force exit from the loop. */ + if ((! MATCHED_SOMETHING (reg_info[*p]) + || (enum regexpcode) p[-3] == start_memory) + && (p + 1) != pend) + { + register unsigned char *p2 = p + 1; + mcnt = 0; + switch (*p2++) + { + case jump_n: + is_a_jump_n = 1; + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p2); + if (is_a_jump_n) + p2 += 2; + break; + } + p2 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump, 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 && (enum regexpcode) *p2++ == on_failure_jump) + { + EXTRACT_NUMBER_AND_INCR (mcnt, p2); + PUSH_FAILURE_POINT (p2 + mcnt, d); + goto fail; + } + } + p++; + break; + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of as the register number. */ + case duplicate: + { + int regno = *p++; /* Get which register to match against */ + register unsigned char *d2, *dend2; + + /* 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 = ((IS_IN_FIRST_STRING (regstart[regno]) + == IS_IN_FIRST_STRING (regend[regno])) + ? regend[regno] : end_match_1); + while (1) + { + /* If necessary, advance to next segment in register + contents. */ + while (d2 == dend2) + { + if (dend2 == end_match_2) break; + if (dend2 == regend[regno]) break; + d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */ + } + /* 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; + + case anychar: + PREFETCH; /* Fetch a data character. */ + /* Match anything but a newline, maybe even a null. */ + if ((translate ? translate[*d] : *d) == '\n' + || ((obscure_syntax & RE_DOT_NOT_NULL) + && (translate ? translate[*d] : *d) == '\000')) + goto fail; + SET_REGS_MATCHED; + d++; + break; + + case charset: + case charset_not: + { + int not = 0; /* Nonzero for charset_not. */ + register int c; + if (*(p - 1) == (unsigned char) charset_not) + not = 1; + + PREFETCH; /* Fetch a data character. */ + + if (translate) + c = translate[*d]; + else + c = *d; + + if (c < *p * BYTEWIDTH + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + SET_REGS_MATCHED; + d++; + break; + } + + case begline: + if ((size1 != 0 && d == string1) + || (size1 == 0 && size2 != 0 && d == string2) + || (d && d[-1] == '\n') + || (size1 == 0 && size2 == 0)) + break; + else + goto fail; + + case endline: + if (d == end2 + || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n')) + break; + goto fail; + + /* `or' constructs are handled by starting each alternative with + an on_failure_jump that points to the start of the next + alternative. Each alternative except the last ends with a + jump to the joining point. (Actually, each jump except for + the last one really jumps to the following jump, because + tensioning the jumps is a hassle.) */ + + /* The start of a stupid repeat has an on_failure_jump that points + past the end of the repeat text. This makes a failure point so + that on failure to match a repetition, matching restarts past + as many repetitions have been found with no way to fail and + look for another one. */ + + /* A smart repeat is similar but loops back to the on_failure_jump + so that each repetition makes another failure point. */ + + case on_failure_jump: + on_failure: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + PUSH_FAILURE_POINT (p + mcnt, d); + break; + + /* The end of a smart repeat has a maybe_finalize_jump back. + Change it either to a finalize_jump or an ordinary jump. */ + case maybe_finalize_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + { + register unsigned char *p2 = p; + /* Compare what follows with the beginning of the repeat. + If we can establish that there is nothing that they would + both match, we can change to finalize_jump. */ + while (p2 + 1 != pend + && (*p2 == (unsigned char) stop_memory + || *p2 == (unsigned char) start_memory)) + p2 += 2; /* Skip over reg number. */ + if (p2 == pend) + p[-3] = (unsigned char) finalize_jump; + else if (*p2 == (unsigned char) exactn + || *p2 == (unsigned char) endline) + { + register int c = *p2 == (unsigned char) endline ? '\n' : p2[2]; + register unsigned char *p1 = p + mcnt; + /* p1[0] ... p1[2] are an on_failure_jump. + Examine what follows that. */ + if (p1[3] == (unsigned char) exactn && p1[5] != c) + p[-3] = (unsigned char) finalize_jump; + else if (p1[3] == (unsigned char) charset + || p1[3] == (unsigned char) charset_not) + { + int not = p1[3] == (unsigned char) charset_not; + if (c < p1[4] * BYTEWIDTH + && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + /* `not' is 1 if c would match. */ + /* That means it is not safe to finalize. */ + if (!not) + p[-3] = (unsigned char) finalize_jump; + } + } + } + p -= 2; /* Point at relative address again. */ + if (p[-1] != (unsigned char) finalize_jump) + { + p[-1] = (unsigned char) jump; + goto nofinalize; + } + /* Note fall through. */ + + /* The end of a stupid repeat has a finalize_jump back to the + start, where another failure point will be made which will + point to after all the repetitions found so far. */ + + /* Take off failure points put on by matching on_failure_jump + because didn't fail. Also remove the register information + put on by the on_failure_jump. */ + case finalize_jump: + POP_FAILURE_POINT (); + /* Note fall through. */ + + /* Jump without taking off any failure points. */ + case jump: + nofinalize: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p += mcnt; + break; + + case dummy_failure_jump: + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at finalize_jump. We will end up at + finalize_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 finalize_jump to pop. */ + PUSH_FAILURE_POINT (0, 0); + goto nofinalize; + + + /* Have to succeed matching what follows at least n times. Then + just handle like an on_failure_jump. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); + } + else if (mcnt == 0) + { + p[2] = unused; + p[3] = unused; + goto on_failure; + } + else + { + fprintf (stderr, "regex: the succeed_n's n is not set.\n"); + exit (1); + } + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER(p + 2, mcnt); + goto nofinalize; /* Do the jump without taking off + any failure points. */ + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + register unsigned char *p1; + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); + STORE_NUMBER (p1, mcnt); + break; + } + + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case unused: + break; + + case wordbound: + if (AT_WORD_BOUNDARY) + break; + goto fail; + + case notwordbound: + if (AT_WORD_BOUNDARY) + goto fail; + break; + + case wordbeg: + /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ + if (IS_A_LETTER (d) && (AT_STRINGS_BEG || !IS_A_LETTER (d - 1))) + break; + goto fail; + + case wordend: + /* Have to check if AT_STRINGS_BEG before looking at d - 1. */ + if (!AT_STRINGS_BEG && IS_A_LETTER (d - 1) + && (!IS_A_LETTER (d) || AT_STRINGS_END)) + break; + goto fail; + +#ifdef emacs + case before_dot: + if (PTR_CHAR_POS (d) >= point) + goto fail; + break; + + case at_dot: + if (PTR_CHAR_POS (d) != point) + goto fail; + break; + + case after_dot: + if (PTR_CHAR_POS (d) <= point) + goto fail; + break; + + case wordchar: + mcnt = (int) Sword; + goto matchsyntax; + + case syntaxspec: + mcnt = *p++; + matchsyntax: + PREFETCH; + if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail; + SET_REGS_MATCHED; + break; + + case notwordchar: + mcnt = (int) Sword; + goto matchnotsyntax; + + case notsyntaxspec: + mcnt = *p++; + matchnotsyntax: + PREFETCH; + if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail; + SET_REGS_MATCHED; + break; + +#else /* not emacs */ + + case wordchar: + PREFETCH; + if (!IS_A_LETTER (d)) + goto fail; + SET_REGS_MATCHED; + break; + + case notwordchar: + PREFETCH; + if (IS_A_LETTER (d)) + goto fail; + SET_REGS_MATCHED; + break; + +#endif /* not emacs */ + + case begbuf: + if (AT_STRINGS_BEG) + break; + goto fail; + + case endbuf: + if (AT_STRINGS_END) + break; + goto fail; + + case exactn: + /* Match the next few pattern characters exactly. + mcnt is how many characters to match. */ + mcnt = *p++; + /* 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[*d++] != *p++) goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH; + if (*d++ != *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED; + break; + } + continue; /* Successfully executed one pattern command; keep going. */ + + /* Jump here if any matching operation fails. */ + fail: + if (stackp != stackb) + /* A restart point is known. Restart there and pop it. */ + { + short last_used_reg, this_reg; + + /* If this failure point is from a dummy_failure_point, just + skip it. */ + if (!stackp[-2]) + { + POP_FAILURE_POINT (); + goto fail; + } + + d = *--stackp; + p = *--stackp; + if (d >= string1 && d <= end1) + dend = end_match_1; + /* Restore register info. */ + last_used_reg = (short) *--stackp; + + /* Make the ones that weren't saved -1 or 0 again. */ + for (this_reg = RE_NREGS - 1; this_reg > last_used_reg; this_reg--) + { + regend[this_reg] = (unsigned char *) -1; + regstart[this_reg] = (unsigned char *) -1; + IS_ACTIVE (reg_info[this_reg]) = 0; + MATCHED_SOMETHING (reg_info[this_reg]) = 0; + } + + /* And restore the rest from the stack. */ + for ( ; this_reg > 0; this_reg--) + { + reg_info[this_reg] = *(struct register_info *) *--stackp; + regend[this_reg] = *--stackp; + regstart[this_reg] = *--stackp; + } + } + else + break; /* Matching at this starting point really fails. */ + } + + if (best_regs_set) + goto restore_best_regs; + return -1; /* Failure to match. */ +} + + +static int +bcmp_translate (s1, s2, len, translate) + unsigned char *s1, *s2; + register int len; + unsigned char *translate; +{ + register unsigned char *p1 = s1, *p2 = s2; + while (len) + { + if (translate [*p1++] != translate [*p2++]) return 1; + len--; + } + return 0; +} + + + +/* Entry points compatible with 4.2 BSD regex library. */ + +#ifndef emacs + +static struct re_pattern_buffer re_comp_buf; + +char * +re_comp (s) + char *s; +{ + if (!s) + { + if (!re_comp_buf.buffer) + return "No previous regular expression"; + return 0; + } + + if (!re_comp_buf.buffer) + { + if (!(re_comp_buf.buffer = (char *) malloc (200))) + return "Memory exhausted"; + re_comp_buf.allocated = 200; + if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH))) + return "Memory exhausted"; + } + return re_compile_pattern (s, strlen (s), &re_comp_buf); +} + +int +re_exec (s) + char *s; +{ + int len = strlen (s); + return 0 <= re_search (&re_comp_buf, s, len, 0, len, + (struct re_registers *) 0); +} +#endif /* not emacs */ + + + +#ifdef test + +#include + +/* Indexed by a character, gives the upper case equivalent of the + character. */ + +char upcase[0400] = + { 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 012, 013, 014, 015, 016, 017, + 020, 021, 022, 023, 024, 025, 026, 027, + 030, 031, 032, 033, 034, 035, 036, 037, + 040, 041, 042, 043, 044, 045, 046, 047, + 050, 051, 052, 053, 054, 055, 056, 057, + 060, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 072, 073, 074, 075, 076, 077, + 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, + 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 + }; + +#ifdef canned + +#include "tests.h" + +typedef enum { extended_test, basic_test } test_type; + +/* Use this to run the tests we've thought of. */ + +void +main () +{ + test_type t = extended_test; + + if (t == basic_test) + { + printf ("Running basic tests:\n\n"); + test_posix_basic (); + } + else if (t == extended_test) + { + printf ("Running extended tests:\n\n"); + test_posix_extended (); + } +} + +#else /* not canned */ + +/* Use this to run interactive tests. */ + +void +main (argc, argv) + int argc; + char **argv; +{ + char pat[80]; + struct re_pattern_buffer buf; + int i; + char c; + char fastmap[(1 << BYTEWIDTH)]; + + /* Allow a command argument to specify the style of syntax. */ + if (argc > 1) + obscure_syntax = atoi (argv[1]); + + buf.allocated = 40; + buf.buffer = (char *) malloc (buf.allocated); + buf.fastmap = fastmap; + buf.translate = upcase; + + while (1) + { + gets (pat); + + if (*pat) + { + re_compile_pattern (pat, strlen(pat), &buf); + + for (i = 0; i < buf.used; i++) + printchar (buf.buffer[i]); + + putchar ('\n'); + + printf ("%d allocated, %d used.\n", buf.allocated, buf.used); + + re_compile_fastmap (&buf); + printf ("Allowed by fastmap: "); + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (fastmap[i]) printchar (i); + putchar ('\n'); + } + + gets (pat); /* Now read the string to match against */ + + i = re_match (&buf, pat, strlen (pat), 0, 0); + printf ("Match value %d.\n", i); + } +} + +#endif + + +#ifdef NOTDEF +print_buf (bufp) + struct re_pattern_buffer *bufp; +{ + int i; + + printf ("buf is :\n----------------\n"); + for (i = 0; i < bufp->used; i++) + printchar (bufp->buffer[i]); + + printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used); + + printf ("Allowed by fastmap: "); + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (bufp->fastmap[i]) + printchar (i); + printf ("\nAllowed by translate: "); + if (bufp->translate) + for (i = 0; i < (1 << BYTEWIDTH); i++) + if (bufp->translate[i]) + printchar (i); + printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't"); + printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not"); +} +#endif /* NOTDEF */ + +printchar (c) + char c; +{ + if (c < 040 || c >= 0177) + { + putchar ('\\'); + putchar (((c >> 6) & 3) + '0'); + putchar (((c >> 3) & 7) + '0'); + putchar ((c & 7) + '0'); + } + else + putchar (c); +} + +error (string) + char *string; +{ + puts (string); + exit (1); +} +#endif /* test */ diff --git a/usr/src/usr.bin/tar/regex.h b/usr/src/usr.bin/tar/regex.h new file mode 100644 index 0000000000..81f953a047 --- /dev/null +++ b/usr/src/usr.bin/tar/regex.h @@ -0,0 +1,257 @@ +/* Definitions for data structures callers pass the regex library. + + Copyright (C) 1985, 1989-90 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 __GNUC__ + #pragma once +#endif + +#ifndef __REGEXP_LIBRARY +#define __REGEXP_LIBRARY + +/* Define number of parens for which we record the beginnings and ends. + This affects how much space the `struct re_registers' type takes up. */ +#ifndef RE_NREGS +#define RE_NREGS 10 +#endif + +#define BYTEWIDTH 8 + + +/* Maximum number of duplicates an interval can allow. */ +#define RE_DUP_MAX ((1 << 15) - 1) + + +/* This defines the various regexp syntaxes. */ +extern int obscure_syntax; + + +/* The following bits are used in the obscure_syntax variable to choose among + alternative regexp syntaxes. */ + +/* If this bit is set, plain parentheses serve as grouping, and backslash + parentheses are needed for literal searching. + If not set, backslash-parentheses are grouping, and plain parentheses + are for literal searching. */ +#define RE_NO_BK_PARENS 1 + +/* If this bit is set, plain | serves as the `or'-operator, and \| is a + literal. + If not set, \| serves as the `or'-operator, and | is a literal. */ +#define RE_NO_BK_VBAR (1 << 1) + +/* If this bit is not set, plain + or ? serves as an operator, and \+, \? are + literals. + If set, \+, \? are operators and plain +, ? are literals. */ +#define RE_BK_PLUS_QM (1 << 2) + +/* If this bit is set, | binds tighter than ^ or $. + If not set, the contrary. */ +#define RE_TIGHT_VBAR (1 << 3) + +/* If this bit is set, then treat newline as an OR operator. + If not set, treat it as a normal character. */ +#define RE_NEWLINE_OR (1 << 4) + +/* If this bit is set, then special characters may act as normal + characters in some contexts. Specifically, this applies to: + ^ -- only special at the beginning, or after ( or |; + $ -- only special at the end, or before ) or |; + *, +, ? -- only special when not after the beginning, (, or |. + If this bit is not set, special characters (such as *, ^, and $) + always have their special meaning regardless of the surrounding + context. */ +#define RE_CONTEXT_INDEP_OPS (1 << 5) + +/* If this bit is not set, then \ before anything inside [ and ] is taken as + a real \. + If set, then such a \ escapes the following character. This is a + special case for awk. */ +#define RE_AWK_CLASS_HACK (1 << 6) + +/* If this bit is set, then \{ and \} or { and } serve as interval operators. + If not set, then \{ and \} and { and } are treated as literals. */ +#define RE_INTERVALS (1 << 7) + +/* If this bit is not set, then \{ and \} serve as interval operators and + { and } are literals. + If set, then { and } serve as interval operators and \{ and \} are + literals. */ +#define RE_NO_BK_CURLY_BRACES (1 << 8) + +/* 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 (1 << 9) + +/* If this bit is set, then the dot re doesn't match a null byte. + If not set, it does. */ +#define RE_DOT_NOT_NULL (1 << 10) + +/* If this bit is set, then [^...] doesn't match a newline. + If not set, it does. */ +#define RE_HAT_NOT_NEWLINE (1 << 11) + +/* If this bit is set, back references are recognized. + If not set, they aren't. */ +#define RE_NO_BK_REFS (1 << 12) + +/* If this bit is set, back references must refer to a preceding + subexpression. If not set, a back reference to a nonexistent + subexpression is treated as literal characters. */ +#define RE_NO_EMPTY_BK_REF (1 << 13) + +/* If this bit is set, bracket expressions can't be empty. + If it is set, they can be empty. */ +#define RE_NO_EMPTY_BRACKETS (1 << 14) + +/* If this bit is set, then *, +, ? and { cannot be first in an re or + immediately after a |, or a (. Furthermore, a | cannot be first or + last in an re, or immediately follow another | or a (. Also, a ^ + cannot appear in a nonleading position and a $ cannot appear in a + nontrailing position (outside of bracket expressions, that is). */ +#define RE_CONTEXTUAL_INVALID_OPS (1 << 15) + +/* If this bit is set, then +, ? and | aren't recognized as operators. + If it's not, they are. */ +#define RE_LIMITED_OPS (1 << 16) + +/* If this bit is set, then an ending range point has to collate higher + or equal to the starting range point. + If it's not set, then when the ending range point collates higher + than the starting range point, the range is just considered empty. */ +#define RE_NO_EMPTY_RANGES (1 << 17) + +/* If this bit is set, then a hyphen (-) can't be an ending range point. + If it isn't, then it can. */ +#define RE_NO_HYPHEN_RANGE_END (1 << 18) + + +/* Define combinations of bits for the standard possibilities. */ +#define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS) +#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS | RE_AWK_CLASS_HACK) +#define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR) +#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) +#define RE_SYNTAX_EMACS 0 +#define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \ + | RE_CHAR_CLASSES | RE_DOT_NOT_NULL \ + | RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \ + | RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \ + | RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END) + +#define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \ + | RE_NO_BK_VBAR | RE_NO_BK_PARENS \ + | RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \ + | RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \ + | RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \ + | RE_NO_HYPHEN_RANGE_END) + + +/* This data structure is used to represent a compiled pattern. */ + +struct re_pattern_buffer + { + char *buffer; /* Space holding the compiled pattern commands. */ + long allocated; /* Size of space that `buffer' points to. */ + long used; /* Length of portion of buffer actually occupied */ + char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ + /* re_search uses the fastmap, if there is one, + to skip over totally implausible characters. */ + char *translate; /* Translate table to apply to all characters before + comparing, or zero for no translation. + The translation is applied to a pattern when it is + compiled and to data when it is matched. */ + char fastmap_accurate; + /* Set to zero when a new pattern is stored, + set to one when the fastmap is updated from it. */ + char can_be_null; /* Set to one by compiling fastmap + if this pattern might match the null string. + It does not necessarily match the null string + in that case, but if this is zero, it cannot. + 2 as value means can match null string + but at end of range or before a character + listed in the fastmap. */ + }; + + +/* search.c (search_buffer) needs this one value. It is defined both in + regex.c and here. */ +#define RE_EXACTN_VALUE 1 + + +/* Structure to store register contents data in. + + Pass the address of such a structure as an argument to re_match, etc., + if you want this information back. + + For i from 1 to RE_NREGS - 1, start[i] records the starting index in + the string of where the ith subexpression matched, and end[i] records + one after the ending index. start[0] and end[0] are analogous, for + the entire pattern. */ + +struct re_registers + { + int start[RE_NREGS]; + int end[RE_NREGS]; + }; + + + +#ifdef __STDC__ + +extern char *re_compile_pattern (char *, int, struct re_pattern_buffer *); +/* Is this really advertised? */ +extern void re_compile_fastmap (struct re_pattern_buffer *); +extern int re_search (struct re_pattern_buffer *, char*, int, int, int, + struct re_registers *); +extern int re_search_2 (struct re_pattern_buffer *, char *, int, + char *, int, int, int, + struct re_registers *, int); +extern int re_match (struct re_pattern_buffer *, char *, int, int, + struct re_registers *); +extern int re_match_2 (struct re_pattern_buffer *, char *, int, + char *, int, int, struct re_registers *, int); + +/* 4.2 bsd compatibility. */ +extern char *re_comp (char *); +extern int re_exec (char *); + +#else /* !__STDC__ */ + +extern char *re_compile_pattern (); +/* Is this really advertised? */ +extern void re_compile_fastmap (); +extern int re_search (), re_search_2 (); +extern int re_match (), re_match_2 (); + +/* 4.2 bsd compatibility. */ +extern char *re_comp (); +extern int re_exec (); + +#endif /* __STDC__ */ + + +#ifdef SYNTAX_TABLE +extern char *re_syntax_table; +#endif + +#endif /* !__REGEXP_LIBRARY */ diff --git a/usr/src/usr.bin/tar/rmt.h b/usr/src/usr.bin/tar/rmt.h new file mode 100644 index 0000000000..19d5e53076 --- /dev/null +++ b/usr/src/usr.bin/tar/rmt.h @@ -0,0 +1,77 @@ +/* Remote tape drive defines for tar. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef NO_REMOTE +#define _isrmt(f) 0 +#define rmtopen open +#define rmtaccess access +#define rmtstat stat +#define rmtcreat creat +#define rmtlstat lstat +#define rmtread read +#define rmtwrite write +#define rmtlseek lseek +#define rmtclose close +#define rmtioctl ioctl +#define rmtdup dup +#define rmtfstat fstat +#define rmtfcntl fcntl +#define rmtisatty isatty + +extern long lseek(); +#else +#ifndef USG +#define strchr index +#endif + +#define __REM_BIAS 128 +#define RMTIOCTL + +#ifndef O_CREAT +#define O_CREAT 01000 +#endif +extern char *__rmt_path; +extern char *strchr(); + +#define _remdev(path) ((__rmt_path=strchr(path, ':')) && strncmp(__rmt_path, ":/dev/", 6)==0) +#define _isrmt(fd) ((fd) >= __REM_BIAS) + +#define rmtopen(path,oflag,mode) (_remdev(path) ? __rmt_open(path, oflag, mode, __REM_BIAS) : open(path, oflag, mode)) +#define rmtaccess(path, amode) (_remdev(path) ? 0 : access(path, amode)) +#define rmtstat(path, buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : stat(path, buf)) +#define rmtcreat(path, mode) (_remdev(path) ? __rmt_open (path, 1 | O_CREAT, mode, __REM_BIAS) : creat(path, mode)) +#define rmtlstat(path,buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : lstat(path,buf)) + +#define rmtread(fd, buf, n) (_isrmt(fd) ? __rmt_read(fd - __REM_BIAS, buf, n) : read(fd, buf, n)) +#define rmtwrite(fd, buf, n) (_isrmt(fd) ? __rmt_write(fd - __REM_BIAS, buf, n) : write(fd, buf, n)) +#define rmtlseek(fd, off, wh) (_isrmt(fd) ? __rmt_lseek(fd - __REM_BIAS, off, wh) : lseek(fd, off, wh)) +#define rmtclose(fd) (_isrmt(fd) ? __rmt_close(fd - __REM_BIAS) : close(fd)) +#ifdef RMTIOCTL +#define rmtioctl(fd,req,arg) (_isrmt(fd) ? __rmt_ioctl(fd - __REM_BIAS, req, arg) : ioctl(fd, req, arg)) +#else +#define rmtioctl(fd,req,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : ioctl(fd, req, arg)) +#endif +#define rmtdup(fd) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : dup(fd)) +#define rmtfstat(fd, buf) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fstat(fd, buf)) +#define rmtfcntl(fd,cmd,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fcntl (fd, cmd, arg)) +#define rmtisatty(fd) (_isrmt(fd) ? 0 : isatty(fd)) + +#undef RMTIOCTL +extern long lseek(),__rmt_lseek(); +#endif diff --git a/usr/src/usr.bin/tar/rtape_lib.c b/usr/src/usr.bin/tar/rtape_lib.c new file mode 100644 index 0000000000..bec91eb512 --- /dev/null +++ b/usr/src/usr.bin/tar/rtape_lib.c @@ -0,0 +1,649 @@ +/* Remote tape emulator subroutines. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* JF: modified to make all rmtXXX calls into macros for speed */ + +#ifndef lint +static char *RCSid = "$Header: /usr/src/local/usr.lib/librmt/RCS/rmtlib.c,v 1.7 89/03/23 14:09:51 root Exp Locker: root $"; +#endif + +/* + * $Log: rmtlib.c,v $ + * Revision 1.7 89/03/23 14:09:51 root + * Fix from haynes@ucscc.ucsc.edu for use w/compat. ADR. + * + * Revision 1.6 88/10/25 17:04:29 root + * rexec code and a bug fix from srs!dan, miscellanious cleanup. ADR. + * + * Revision 1.5 88/10/25 16:30:17 root + * Fix from jeff@gatech.edu for getting user@host:dev right. ADR. + * + * Revision 1.4 87/10/30 10:36:12 root + * Made 4.2 syntax a compile time option. ADR. + * + * Revision 1.3 87/04/22 11:16:48 root + * Two fixes from parmelee@wayback.cs.cornell.edu to correctly + * do fd biasing and rmt protocol on 'S' command. ADR. + * + * Revision 1.2 86/10/09 16:38:53 root + * Changed to reflect 4.3BSD rcp syntax. ADR. + * + * Revision 1.1 86/10/09 16:17:35 root + * Initial revision + * + */ + +/* + * rmt --- remote tape emulator subroutines + * + * Originally written by Jeff Lee, modified some by Arnold Robbins + * + * WARNING: The man page rmt(8) for /etc/rmt documents the remote mag + * tape protocol which rdump and rrestore use. Unfortunately, the man + * page is *WRONG*. The author of the routines I'm including originally + * wrote his code just based on the man page, and it didn't work, so he + * went to the rdump source to figure out why. The only thing he had to + * change was to check for the 'F' return code in addition to the 'E', + * and to separate the various arguments with \n instead of a space. I + * personally don't think that this is much of a problem, but I wanted to + * point it out. + * -- Arnold Robbins + * + * Redone as a library that can replace open, read, write, etc, by + * Fred Fish, with some additional work by Arnold Robbins. + */ + +/* Use -DUSE_REXEC for rexec code, courtesy of Dan Kegel, srs!dan */ + +#if defined(USG) && !defined(HAVE_MTIO) +#define NO_RMTIOCTL +#endif + +#include +#include +#include + +#ifndef NO_RMTIOCTL +#include +#include +#endif + +#ifdef USE_REXEC +#include +#endif + +#include +#include +#include + +#define BUFMAGIC 64 /* a magic number for buffer sizes */ + +/* + * MAXUNIT --- Maximum number of remote tape file units + */ +#define MAXUNIT 4 + +/* + * READ --- Return the number of the read side file descriptor + * WRITE --- Return the number of the write side file descriptor + */ +#define READ(fd) (Ctp[fd][0]) +#define WRITE(fd) (Ptc[fd][1]) + +static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; +static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 }; + +extern int errno; + +char *__rmt_path; + +/* + * _rmt_panic --- close off a remote tape connection + */ + +static void _rmt_panic(fildes) +int fildes; +{ + close(READ(fildes)); + close(WRITE(fildes)); + READ(fildes) = -1; + WRITE(fildes) = -1; +} + + + +/* + * command --- attempt to perform a remote tape command + */ + +static int command(fildes, buf) +int fildes; +char *buf; +{ + register int blen; +#ifdef SIGNAL_VOID + void (*pstat)(); +#else + int (*pstat)(); +#endif + +/* + * save current pipe status and try to make the request + */ + + blen = strlen(buf); + pstat = signal(SIGPIPE, SIG_IGN); + if (write(WRITE(fildes), buf, blen) == blen) + { + signal(SIGPIPE, pstat); + return(0); + } + +/* + * something went wrong. close down and go home + */ + + signal(SIGPIPE, pstat); + _rmt_panic(fildes); + + errno = EIO; + return(-1); +} + + + +/* + * status --- retrieve the status from the pipe + */ + +static int status(fildes) +int fildes; +{ + int i; + char c, *cp; + char buffer[BUFMAGIC]; + +/* + * read the reply command line + */ + + for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++) + { + if (read(READ(fildes), cp, 1) != 1) + { + _rmt_panic(fildes); + errno = EIO; + return(-1); + } + if (*cp == '\n') + { + *cp = 0; + break; + } + } + + if (i == BUFMAGIC) + { + _rmt_panic(fildes); + errno = EIO; + return(-1); + } + +/* + * check the return status + */ + + for (cp = buffer; *cp; cp++) + if (*cp != ' ') + break; + + if (*cp == 'E' || *cp == 'F') + { + errno = atoi(cp + 1); + while (read(READ(fildes), &c, 1) == 1) + if (c == '\n') + break; + + if (*cp == 'F') + _rmt_panic(fildes); + + return(-1); + } + +/* + * check for mis-synced pipes + */ + + if (*cp != 'A') + { + _rmt_panic(fildes); + errno = EIO; + return(-1); + } + + return(atoi(cp + 1)); +} + +#ifdef USE_REXEC + +/* + * _rmt_rexec + * + * execute /etc/rmt on a remote system using rexec(). + * Return file descriptor of bidirectional socket for stdin and stdout + * If username is NULL, or an empty string, uses current username. + * + * ADR: By default, this code is not used, since it requires that + * the user have a .netrc file in his/her home directory, or that the + * application designer be willing to have rexec prompt for login and + * password info. This may be unacceptable, and .rhosts files for use + * with rsh are much more common on BSD systems. + */ + +static int +_rmt_rexec(host, user) +char *host; +char *user; /* may be NULL */ +{ + struct servent *rexecserv; + int save_stdin = dup(fileno(stdin)); + int save_stdout = dup(fileno(stdout)); + int tape_fd; /* Return value. */ + + /* + * When using cpio -o < filename, stdin is no longer the tty. + * But the rexec subroutine reads the login and the passwd on stdin, + * to allow remote execution of the command. + * So, reopen stdin and stdout on /dev/tty before the rexec and + * give them back their original value after. + */ + if (freopen("/dev/tty", "r", stdin) == NULL) + freopen("/dev/null", "r", stdin); + if (freopen("/dev/tty", "w", stdout) == NULL) + freopen("/dev/null", "w", stdout); + + rexecserv = getservbyname("exec", "tcp"); + if (NULL == rexecserv) { + fprintf (stderr, "? exec/tcp: service not available."); + exit (-1); + } + if ((user != NULL) && *user == '\0') + user = (char *) NULL; + tape_fd = rexec (&host, rexecserv->s_port, user, NULL, + "/etc/rmt", (int *)NULL); + fclose(stdin); + fdopen(save_stdin, "r"); + fclose(stdout); + fdopen(save_stdout, "w"); + + return tape_fd; +} +#endif /* USE_REXEC */ + +/* + * _rmt_open --- open a magtape device on system specified, as given user + * + * file name has the form [user@]system:/dev/???? +#ifdef COMPAT + * file name has the form system[.user]:/dev/???? +#endif + */ + +#define MAXHOSTLEN 257 /* BSD allows very long host names... */ + +int __rmt_open (path, oflag, mode, bias) +char *path; +int oflag; +int mode; +int bias; +{ + int i, rc; + char buffer[BUFMAGIC]; + char system[MAXHOSTLEN]; + char device[BUFMAGIC]; + char login[BUFMAGIC]; + char *sys, *dev, *user; + + sys = system; + dev = device; + user = login; + +/* + * first, find an open pair of file descriptors + */ + + for (i = 0; i < MAXUNIT; i++) + if (READ(i) == -1 && WRITE(i) == -1) + break; + + if (i == MAXUNIT) + { + errno = EMFILE; + return(-1); + } + +/* + * pull apart system and device, and optional user + * don't munge original string + * if COMPAT is defined, also handle old (4.2) style person.site notation. + */ + + while (*path != '@' +#ifdef COMPAT + && *path != '.' +#endif + && *path != ':') { + *sys++ = *path++; + } + *sys = '\0'; + path++; + + if (*(path - 1) == '@') + { + (void) strcpy (user, system); /* saw user part of user@host */ + sys = system; /* start over */ + while (*path != ':') { + *sys++ = *path++; + } + *sys = '\0'; + path++; + } +#ifdef COMPAT + else if (*(path - 1) == '.') + { + while (*path != ':') { + *user++ = *path++; + } + *user = '\0'; + path++; + } +#endif + else + *user = '\0'; + + while (*path) { + *dev++ = *path++; + } + *dev = '\0'; + +#ifdef USE_REXEC +/* + * Execute the remote command using rexec + */ + READ(i) = WRITE(i) = _rmt_rexec(system, login); + if (READ(i) < 0) + return -1; +#else +/* + * setup the pipes for the 'rsh' command and fork + */ + + if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1) + return(-1); + + if ((rc = fork()) == -1) + return(-1); + + if (rc == 0) + { + close(0); + dup(Ptc[i][0]); + close(Ptc[i][0]); close(Ptc[i][1]); + close(1); + dup(Ctp[i][1]); + close(Ctp[i][0]); close(Ctp[i][1]); + (void) setuid (getuid ()); + (void) setgid (getgid ()); + if (*login) + { + execl("/usr/ucb/rsh", "rsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl("/usr/bin/remsh", "remsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl("/usr/bin/rsh", "rsh", system, "-l", login, + "/etc/rmt", (char *) 0); + execl("/usr/bsd/rsh", "rsh", system, "-l", login, + "/etc/rmt", (char *)0); + execl("/usr/bin/nsh", "nsh", system, "-l", login, + "/etc/rmt", (char *)0); + } + else + { + execl("/usr/ucb/rsh", "rsh", system, + "/etc/rmt", (char *) 0); + execl("/usr/bin/remsh", "remsh", system, + "/etc/rmt", (char *) 0); + execl("/usr/bin/rsh", "rsh", system, + "/etc/rmt", (char *) 0); + execl("/usr/bsd/rsh", "rsh", system, + "/etc/rmt", (char *) 0); + execl("/usr/bin/nsh", "nsh", system, + "/etc/rmt", (char *)0); + } + +/* + * bad problems if we get here + */ + + perror("remote shell exec"); + exit(1); + } + + close(Ptc[i][0]); close(Ctp[i][1]); +#endif + +/* + * now attempt to open the tape device + */ + + sprintf(buffer, "O%s\n%d\n", device, oflag); + if (command(i, buffer) == -1 || status(i) == -1) + return(-1); + + return(i+bias); +} + + + +/* + * _rmt_close --- close a remote magtape unit and shut down + */ + + int __rmt_close(fildes) +int fildes; +{ + int rc; + + if (command(fildes, "C\n") != -1) + { + rc = status(fildes); + + _rmt_panic(fildes); + return(rc); + } + + return(-1); +} + + + +/* + * _rmt_read --- read a buffer from a remote tape + */ + +int __rmt_read(fildes, buf, nbyte) +int fildes; +char *buf; +unsigned int nbyte; +{ + int rc, i; + char buffer[BUFMAGIC]; + + sprintf(buffer, "R%d\n", nbyte); + if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1) + return(-1); + + for (i = 0; i < rc; i += nbyte, buf += nbyte) + { + nbyte = read(READ(fildes), buf, rc); + if (nbyte <= 0) + { + _rmt_panic(fildes); + errno = EIO; + return(-1); + } + } + + return(rc); +} + + + +/* + * _rmt_write --- write a buffer to the remote tape + */ + +int __rmt_write(fildes, buf, nbyte) +int fildes; +char *buf; +unsigned int nbyte; +{ + char buffer[BUFMAGIC]; +#ifdef SIGNAL_VOID + void (*pstat)(); +#else + int (*pstat)(); +#endif + + sprintf(buffer, "W%d\n", nbyte); + if (command(fildes, buffer) == -1) + return(-1); + + pstat = signal(SIGPIPE, SIG_IGN); + if (write(WRITE(fildes), buf, nbyte) == nbyte) + { + signal (SIGPIPE, pstat); + return(status(fildes)); + } + + signal (SIGPIPE, pstat); + _rmt_panic(fildes); + errno = EIO; + return(-1); +} + + + +/* + * _rmt_lseek --- perform an imitation lseek operation remotely + */ + +long __rmt_lseek(fildes, offset, whence) +int fildes; +long offset; +int whence; +{ + char buffer[BUFMAGIC]; + + sprintf(buffer, "L%d\n%d\n", offset, whence); + if (command(fildes, buffer) == -1) + return(-1); + + return(status(fildes)); +} + + +/* + * _rmt_ioctl --- perform raw tape operations remotely + */ + +#ifndef NO_RMTIOCTL +__rmt_ioctl(fildes, op, arg) +int fildes, op; +char *arg; +{ + char c; + int rc, cnt; + char buffer[BUFMAGIC]; + +/* + * MTIOCOP is the easy one. nothing is transfered in binary + */ + + if (op == MTIOCTOP) + { + sprintf(buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op, + ((struct mtop *) arg)->mt_count); + if (command(fildes, buffer) == -1) + return(-1); + return(status(fildes)); + } + +/* + * we can only handle 2 ops, if not the other one, punt + */ + + if (op != MTIOCGET) + { + errno = EINVAL; + return(-1); + } + +/* + * grab the status and read it directly into the structure + * this assumes that the status buffer is (hopefully) not + * padded and that 2 shorts fit in a long without any word + * alignment problems, ie - the whole struct is contiguous + * NOTE - this is probably NOT a good assumption. + */ + + if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1) + return(-1); + + for (; rc > 0; rc -= cnt, arg += cnt) + { + cnt = read(READ(fildes), arg, rc); + if (cnt <= 0) + { + _rmt_panic(fildes); + errno = EIO; + return(-1); + } + } + +/* + * now we check for byte position. mt_type is a small integer field + * (normally) so we will check its magnitude. if it is larger than + * 256, we will assume that the bytes are swapped and go through + * and reverse all the bytes + */ + + if (((struct mtget *) arg)->mt_type < 256) + return(0); + + for (cnt = 0; cnt < rc; cnt += 2) + { + c = arg[cnt]; + arg[cnt] = arg[cnt+1]; + arg[cnt+1] = c; + } + + return(0); + } +#endif /* NO_RMTIOCTL */ diff --git a/usr/src/usr.bin/tar/update.c b/usr/src/usr.bin/tar/update.c new file mode 100644 index 0000000000..eefd5cb629 --- /dev/null +++ b/usr/src/usr.bin/tar/update.c @@ -0,0 +1,534 @@ +/* Update a tar archive. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* JF implement the 'r' 'u' and 'A' options for tar. */ +/* The 'A' option is my own invention: It means that the file-names are + tar files, and they should simply be appended to the end of the archive. + No attempt is made to block the reads from the args; if they're on raw + tape or something like that, it'll probably lose. . . */ + +#include +#include +#include +#include + +/* JF these includes are copied from create.c I'm not sure if they're right + or not. */ +#ifndef V7 +#include +#endif + +#ifndef MSDOS +#include +#include +#endif + +#ifdef USG +#include /* major() and minor() defined here */ +#endif + +/* + * V7 doesn't have a #define for this. + */ +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +/* + * Most people don't have a #define for this. + */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#define STDIN 0 +#define STDOUT 1 + +#include "tar.h" +#include "port.h" +#include "rmt.h" + +int time_to_start_writing = 0; /* We've hit the end of the old stuff, + and its time to start writing new stuff + to the tape. This involves seeking + back one block and re-writing the current + block (which has been changed). */ + +char *output_start; /* Pointer to where we started to write in + the first block we write out. This is used + if we can't backspace the output and have + to null out the first part of the block */ + +extern void skip_file(); +extern void skip_extended_headers(); + +extern union record *head; +extern struct stat hstat; + +struct name *name_scan(); +char *name_from_list(); + +/* Implement the 'r' (add files to end of archive), and 'u' (add files to + end of archive if they arent there, or are more up to date than the + version in the archive.) commands.*/ +void +update_archive() +{ + int found_end = 0; + int status = 3; + int prev_status; + char *p; + struct name *name; + extern void dump_file(); + + name_gather(); + if(cmd_mode==CMD_UPDATE) + name_expand(); + open_archive(2); /* Open for updating */ + + do { + prev_status=status; + status=read_header(); + switch(status) { + case EOF: + found_end=1; + break; + + case 0: /* A bad record */ + userec(head); + switch(prev_status) { + case 3: + msg("This doesn't look like a tar archive."); + /* FALL THROUGH */ + case 2: + case 1: + msg("Skipping to next header"); + case 0: + break; + } + break; + + /* A good record */ + case 1: + /* printf("File %s\n",head->header.name); */ + /* head->header.name[NAMSIZ-1]='\0'; */ + if(cmd_mode==CMD_UPDATE && (name=name_scan(head->header.name))) { + /* struct stat hstat; */ + struct stat nstat; + int head_standard; + + decode_header(head,&hstat,&head_standard,0); + if(stat(head->header.name,&nstat)<0) { + msg_perror("can't stat %s:",head->header.name); + } else { + if(hstat.st_mtime>=nstat.st_mtime) + name->found++; + } + } + userec(head); + if (head->header.isextended) + skip_extended_headers(); + skip_file((long)hstat.st_size); + break; + + case 2: + ar_record=head; + found_end = 1; + break; + } + } while(!found_end); + + reset_eof(); + time_to_start_writing = 1; + output_start=ar_record->charptr; + + while(p=name_from_list()) { + if(f_confirm && !confirm("add", p)) + continue; + if(cmd_mode==CMD_CAT) + append_file(p); + else + dump_file(p,-1); + } + + write_eot(); + close_archive(); + names_notfound(); +} + +/* Catenate file p to the archive without creating a header for it. It had + better be a tar file or the archive is screwed */ + +append_file(p) +char *p; +{ + int fd; + struct stat statbuf; + long bytes_left; + union record *start; + long bufsiz,count; + + if(0 != stat(p,&statbuf) || (fd=open(p,O_RDONLY|O_BINARY))<0) { + msg_perror("can't open file %s",p); + errors++; + return; + } + + bytes_left = statbuf.st_size; + + while(bytes_left>0) { + start=findrec(); + bufsiz=endofrecs()->charptr - start->charptr; + if(bytes_left < bufsiz) { + bufsiz = bytes_left; + count = bufsiz % RECORDSIZE; + if(count) + bzero(start->charptr + bytes_left,(int)(RECORDSIZE-count)); + } + count=read(fd,start->charptr,bufsiz); + if(count<0) { + msg_perror("read error at byte %ld reading %d bytes in file %s",statbuf.st_size-bytes_left,bufsiz,p); + exit(EX_ARGSBAD); /* FOO */ + } + bytes_left-=count; + userec(start+(count-1)/RECORDSIZE); + if(count!=bufsiz) { + msg("%s: file shrunk by %d bytes, yark!",p,bytes_left); + abort(); + } + } + (void)close(fd); +} + +#ifdef DONTDEF +bprint(fp,buf,num) +FILE *fp; +char *buf; +{ + int c; + + if(num==0 || num==-1) + return; + fputs(" '",fp); + while(num--) { + c= *buf++; + if(c=='\\') fputs("\\\\",fp); + else if(c>=' ' && c<='~') + putc(c,fp); + else switch(c) { + case '\n': + fputs("\\n",fp); + break; + case '\r': + fputs("\\r",fp); + break; + case '\b': + fputs("\\b",fp); + break; + case '\0': + /* fputs("\\-",fp); */ + break; + default: + fprintf(fp,"\\%03o",c); + break; + } + } + fputs("'\n",fp); +} +#endif + +int number_of_blocks_read = 0; + +int number_of_new_records = 0; +int number_of_records_needed = 0; + +union record *new_block = 0; +union record *save_block = 0; + +void +junk_archive() +{ + int found_stuff = 0; + int status = 3; + int prev_status; + struct name *name; + + /* int dummy_head; */ + int number_of_records_to_skip = 0; + int number_of_records_to_keep = 0; + int number_of_kept_records_in_block; + int sub_status; + extern int write_archive_to_stdout; + +/* fprintf(stderr,"Junk files\n"); */ + name_gather(); + open_archive(2); + + while(!found_stuff) { + prev_status=status; + status=read_header(); + switch(status) { + case EOF: + found_stuff = 1; + break; + + case 0: + userec(head); + switch(prev_status) { + case 3: + msg("This doesn't look like a tar archive."); + /* FALL THROUGH */ + case 2: + case 1: + msg("Skipping to next header"); + /* FALL THROUGH */ + case 0: + break; + } + break; + + case 1: + /* head->header.name[NAMSIZ-1] = '\0'; */ + /* fprintf(stderr,"file %s\n",head->header.name); */ + if((name=name_scan(head->header.name))==(struct name *)0) { + userec(head); + /* fprintf(stderr,"Skip %ld\n",(long)(hstat.st_size)); */ + if (head->header.isextended) + skip_extended_headers(); + skip_file((long)(hstat.st_size)); + break; + } + name->found = 1; + found_stuff = 2; + break; + + case 2: + found_stuff = 1; + break; + } + } + /* fprintf(stderr,"Out of first loop\n"); */ + + if(found_stuff!=2) { + write_eot(); + close_archive(); + names_notfound(); + return; + } + + if(write_archive_to_stdout) + write_archive_to_stdout = 0; + new_block = (union record *)malloc(blocksize); + if(new_block==0) { + msg("Can't allocate secondary block of %d bytes",blocksize); + exit(EX_SYSTEM); + } + + /* Save away records before this one in this block */ + number_of_new_records=ar_record-ar_block; + number_of_records_needed = blocking - number_of_new_records; + if(number_of_new_records) + bcopy((void *)ar_block,(void *)new_block,(number_of_new_records)*RECORDSIZE); + + /* fprintf(stderr,"Saved %d recs, need %d more\n",number_of_new_records,number_of_records_needed); */ + userec(head); + if (head->header.isextended) + skip_extended_headers(); + skip_file((long)(hstat.st_size)); + found_stuff=0; + /* goto flush_file; */ + + for(;;) { + /* Fill in a block */ + /* another_file: */ + if(ar_record==ar_last) { + /* fprintf(stderr,"New block\n"); */ + flush_archive(); + number_of_blocks_read++; + } + sub_status = read_header(); + /* fprintf(stderr,"Header type %d\n",sub_status); */ + + if(sub_status==2 && f_ignorez) { + userec(head); + continue; + } + if(sub_status==EOF || sub_status==2) { + found_stuff = 1; + bzero(new_block[number_of_new_records].charptr,RECORDSIZE*number_of_records_needed); + number_of_new_records+=number_of_records_needed; + number_of_records_needed = 0; + write_block(0); + break; + } + + if(sub_status==0) { + msg("Deleting non-header from archive."); + userec(head); + continue; + } + + /* Found another header. Yipee! */ + /* head->header.name[NAMSIZ-1] = '\0'; */ + /* fprintf(stderr,"File %s ",head->header.name); */ + if(name=name_scan(head->header.name)) { + name->found = 1; + /* fprintf(stderr,"Flush it\n"); */ + /* flush_file: */ + /* decode_header(head,&hstat,&dummy_head,0); */ + userec(head); + number_of_records_to_skip=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE; + /* fprintf(stderr,"Flushing %d recs from %s\n",number_of_records_to_skip,head->header.name); */ + + while(ar_last-ar_record<=number_of_records_to_skip) { + + /* fprintf(stderr,"Block: %d <= %d ",ar_last-ar_record,number_of_records_to_skip); */ + number_of_records_to_skip -= (ar_last - ar_record); + flush_archive(); + number_of_blocks_read++; + /* fprintf(stderr,"Block %d left\n",number_of_records_to_skip); */ + } + ar_record+=number_of_records_to_skip; + /* fprintf(stderr,"Final %d\n",number_of_records_to_skip); */ + number_of_records_to_skip = 0; + continue; + } + + /* copy_header: */ + new_block[number_of_new_records]= *head; + number_of_new_records++; + number_of_records_needed--; + number_of_records_to_keep=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE; + userec(head); + if(number_of_records_needed==0) + write_block(1); + /* copy_data: */ + number_of_kept_records_in_block = ar_last - ar_record; + if(number_of_kept_records_in_block > number_of_records_to_keep) + number_of_kept_records_in_block = number_of_records_to_keep; + + /* fprintf(stderr,"Need %d kept_in %d keep %d\n",blocking,number_of_kept_records_in_block,number_of_records_to_keep); */ + + while(number_of_records_to_keep) { + int n; + + if(ar_record==ar_last) { + /* fprintf(stderr,"Flush. . .\n"); */ + fl_read(); + number_of_blocks_read++; + ar_record=ar_block; + number_of_kept_records_in_block = blocking; + if(number_of_kept_records_in_block > number_of_records_to_keep) + number_of_kept_records_in_block = number_of_records_to_keep; + } + n = number_of_kept_records_in_block; + if(n>number_of_records_needed) + n = number_of_records_needed; + + /* fprintf(stderr,"Copying %d\n",n); */ + bcopy((void *)ar_record, (void *)(new_block+number_of_new_records), n*RECORDSIZE); + number_of_new_records += n; + number_of_records_needed -= n; + ar_record += n; + number_of_records_to_keep -= n; + number_of_kept_records_in_block -= n; + /* fprintf(stderr,"Now new %d need %d keep %d keep_in %d rec %d/%d\n", + number_of_new_records,number_of_records_needed,number_of_records_to_keep, + number_of_kept_records_in_block,ar_record-ar_block,ar_last-ar_block); */ + + if(number_of_records_needed == 0) { + write_block(1); + } + } + } + + write_eot(); + close_archive(); + names_notfound(); +} + +write_block(f) +{ + /* fprintf(stderr,"Write block\n"); */ + /* We've filled out a block. Write it out. */ + + /* Backspace back to where we started. . . */ + if(archive!=STDIN) + (void)move_arch(-(number_of_blocks_read+1)); + + save_block = ar_block; + ar_block = new_block; + + if(archive==STDIN) + archive=STDOUT; + fl_write(); + + if(archive==STDOUT) + archive=STDIN; + ar_block = save_block; + + if(f) { + /* Move the tape head back to where we were */ + if(archive!=STDIN) + (void)move_arch(number_of_blocks_read); + number_of_blocks_read--; + } + + number_of_records_needed = blocking; + number_of_new_records = 0; +} + +/* Move archive descriptor by n blocks worth. If n is positive we move + forward, else we move negative. If its a tape, MTIOCTOP had better + work. If its something else, we try to seek on it. If we can't + seek, we lose! */ +move_arch(n) +{ + long cur; + extern int errno; + +#ifdef MTIOCTOP + struct mtop t; + int er; + + if(n>0) { + t.mt_op = MTFSR; + t.mt_count = n; + } else { + t.mt_op = MTBSR; + t.mt_count = -n; + } + if((er=rmtioctl(archive,MTIOCTOP,&t))>=0) + return 1; + if(errno==EIO && (er=rmtioctl(archive,MTIOCTOP,&t))>=0) + return 1; +#endif + + cur=rmtlseek(archive,0L,1); + cur+=blocksize*n; + + /* fprintf(stderr,"Fore to %x\n",cur); */ + if(rmtlseek(archive,cur,0)!=cur) { + /* Lseek failed. Try a different method */ + msg("Couldn't re-position archive file."); + exit(EX_BADARCH); + } + return 3; +} + diff --git a/usr/src/usr.bin/tar/version.c b/usr/src/usr.bin/tar/version.c new file mode 100644 index 0000000000..d3023db6ec --- /dev/null +++ b/usr/src/usr.bin/tar/version.c @@ -0,0 +1,20 @@ +/* Version info for tar. + Copyright (C) 1989, Free Software Foundation. + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +char version_string[] = "GNU tar version 1.10"; diff --git a/usr/src/usr.bin/tar/wildmat.c b/usr/src/usr.bin/tar/wildmat.c new file mode 100644 index 0000000000..7e9da7802e --- /dev/null +++ b/usr/src/usr.bin/tar/wildmat.c @@ -0,0 +1,151 @@ +/* Wildcard matching routines. + Copyright (C) 1988 Free Software Foundation + +This file is part of GNU Tar. + +GNU Tar is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Tar 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 GNU Tar; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * @(#)wildmat.c 1.3 87/11/06 + * +From: rs@mirror.TMC.COM (Rich Salz) +Newsgroups: net.sources +Subject: Small shell-style pattern matcher +Message-ID: <596@mirror.TMC.COM> +Date: 27 Nov 86 00:06:40 GMT + +There have been several regular-expression subroutines and one or two +filename-globbing routines in mod.sources. They handle lots of +complicated patterns. This small piece of code handles the *?[]\ +wildcard characters the way the standard Unix(tm) shells do, with the +addition that "[^.....]" is an inverse character class -- it matches +any character not in the range ".....". Read the comments for more +info. + +For my application, I had first ripped off a copy of the "glob" routine +from within the find source, but that code is bad news: it recurses +on every character in the pattern. I'm putting this replacement in the +public domain. It's small, tight, and iterative. Compile with -DTEST +to get a test driver. After you're convinced it works, install in +whatever way is appropriate for you. + +I would like to hear of bugs, but am not interested in additions; if I +were, I'd use the code I mentioned above. +*/ +/* +** Do shell-style pattern matching for ?, \, [], and * characters. +** Might not be robust in face of malformed patterns; e.g., "foo[a-" +** could cause a segmentation violation. +** +** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. +*/ + +/* + * Modified 6Nov87 by John Gilmore (hoptoad!gnu) to return a "match" + * if the pattern is immediately followed by a "/", as well as \0. + * This matches what "tar" does for matching whole subdirectories. + * + * The "*" code could be sped up by only recursing one level instead + * of two for each trial pattern, perhaps, and not recursing at all + * if a literal match of the next 2 chars would fail. + */ +#define TRUE 1 +#define FALSE 0 + + +static int +Star(s, p) + register char *s; + register char *p; +{ + while (wildmat(s, p) == FALSE) + if (*++s == '\0') + return(FALSE); + return(TRUE); +} + + +int +wildmat(s, p) + register char *s; + register char *p; +{ + register int last; + register int matched; + register int reverse; + + for ( ; *p; s++, p++) + switch (*p) { + case '\\': + /* Literal match with following character; fall through. */ + p++; + default: + if (*s != *p) + return(FALSE); + continue; + case '?': + /* Match anything. */ + if (*s == '\0') + return(FALSE); + continue; + case '*': + /* Trailing star matches everything. */ + return(*++p ? Star(s, p) : TRUE); + case '[': + /* [^....] means inverse character class. */ + if (reverse = p[1] == '^') + p++; + for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p) + /* This next line requires a good C compiler. */ + if (*p == '-' ? *s <= *++p && *s >= last : *s == *p) + matched = TRUE; + if (matched == reverse) + return(FALSE); + continue; + } + + /* For "tar" use, matches that end at a slash also work. --hoptoad!gnu */ + return(*s == '\0' || *s == '/'); +} + + +#ifdef TEST +#include + +extern char *gets(); + + +main() +{ + char pattern[80]; + char text[80]; + + while (TRUE) { + printf("Enter pattern: "); + if (gets(pattern) == NULL) + break; + while (TRUE) { + printf("Enter text: "); + if (gets(text) == NULL) + exit(0); + if (text[0] == '\0') + /* Blank line; go back and get a new pattern. */ + break; + printf(" %d\n", wildmat(text, pattern)); + } + } + exit(0); +} +#endif /* TEST */ -- 2.20.1