summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/tar
diff options
context:
space:
mode:
authorJordan K. Hubbard <jkh@FreeBSD.org>1993-06-18 04:22:21 +0000
committerJordan K. Hubbard <jkh@FreeBSD.org>1993-06-18 04:22:21 +0000
commitb76095a4307cc94ec7cd722853f9b032e45e6ea4 (patch)
tree890f91d43eec35dc2f71a54410491f6503ca5b38 /gnu/usr.bin/tar
parent7c434002a4e47486e9a2d7b2f32b1ddf42d37e2a (diff)
downloadsrc-test2-b76095a4307cc94ec7cd722853f9b032e45e6ea4.tar.gz
src-test2-b76095a4307cc94ec7cd722853f9b032e45e6ea4.zip
Notes
Diffstat (limited to 'gnu/usr.bin/tar')
-rw-r--r--gnu/usr.bin/tar/COPYING339
-rw-r--r--gnu/usr.bin/tar/ChangeLog1732
-rw-r--r--gnu/usr.bin/tar/Makefile14
-rw-r--r--gnu/usr.bin/tar/Makefile.gnu185
-rw-r--r--gnu/usr.bin/tar/README40
-rw-r--r--gnu/usr.bin/tar/buffer.c1584
-rw-r--r--gnu/usr.bin/tar/create.c1454
-rw-r--r--gnu/usr.bin/tar/diffarch.c759
-rw-r--r--gnu/usr.bin/tar/extract.c907
-rw-r--r--gnu/usr.bin/tar/fnmatch.c173
-rw-r--r--gnu/usr.bin/tar/fnmatch.h62
-rw-r--r--gnu/usr.bin/tar/getdate.y969
-rw-r--r--gnu/usr.bin/tar/getoldopt.c96
-rw-r--r--gnu/usr.bin/tar/getopt.c712
-rw-r--r--gnu/usr.bin/tar/getopt.h125
-rw-r--r--gnu/usr.bin/tar/getopt1.c161
-rw-r--r--gnu/usr.bin/tar/getpagesize.h38
-rw-r--r--gnu/usr.bin/tar/gnu.c677
-rw-r--r--gnu/usr.bin/tar/list.c881
-rw-r--r--gnu/usr.bin/tar/mangle.c270
-rw-r--r--gnu/usr.bin/tar/msd_dir.h44
-rw-r--r--gnu/usr.bin/tar/names.c149
-rw-r--r--gnu/usr.bin/tar/open3.h67
-rw-r--r--gnu/usr.bin/tar/pathmax.h53
-rw-r--r--gnu/usr.bin/tar/port.c1256
-rw-r--r--gnu/usr.bin/tar/port.h215
-rw-r--r--gnu/usr.bin/tar/regex.c4932
-rw-r--r--gnu/usr.bin/tar/regex.h490
-rw-r--r--gnu/usr.bin/tar/rmt.h98
-rw-r--r--gnu/usr.bin/tar/rtapelib.c582
-rw-r--r--gnu/usr.bin/tar/tar.c1504
-rw-r--r--gnu/usr.bin/tar/tar.h291
-rw-r--r--gnu/usr.bin/tar/update.c585
-rw-r--r--gnu/usr.bin/tar/version.c1
-rw-r--r--gnu/usr.bin/tar/y.tab.h18
35 files changed, 21463 insertions, 0 deletions
diff --git a/gnu/usr.bin/tar/COPYING b/gnu/usr.bin/tar/COPYING
new file mode 100644
index 000000000000..a43ea2126fb6
--- /dev/null
+++ b/gnu/usr.bin/tar/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/tar/ChangeLog b/gnu/usr.bin/tar/ChangeLog
new file mode 100644
index 000000000000..7934f2b1b2e3
--- /dev/null
+++ b/gnu/usr.bin/tar/ChangeLog
@@ -0,0 +1,1732 @@
+Thu Mar 25 13:32:40 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * version.c: Released version 1.11.2.
+
+ * Makefile.in (dist): Do the link differently; some of the
+ files have changed filesystems which makes it more complex.
+
+ * Makefile.in (dist, shar): Use gzip instead of compress.
+
+ * create.c (dump_file): Test for curdev==-1, not curdev<0.
+ Some losing NFS systems give negative device numbers
+ sometimes.
+
+Thu Mar 25 11:55:15 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1 (TAR_PART1): Use `--block-size', not just
+ `--block', which is now ambiguous.
+
+Wed Mar 24 22:12:51 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * backup-specs (TAR): New variable.
+
+ * level-0, level-1 (TAR_PART1): Get path of GNU tar from `TAR'
+ variable, don't hardcode it.
+
+Sat Mar 20 00:20:05 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * backup-specs (SLEEP_MESSAGE): put backslashes in front of nested
+ double quotes.
+
+ * level-0, level-1 (BACKUP_DIRS): Don't put in quotes.
+ (LOGFILE): Use sed to construct name, not awk.
+
+ * dump-remind (recipients): Replaced inefficient pipeline with a
+ single, simple sed script.
+ (volno): Deal with the possibility that VOLNO_FILE may not be
+ created yet.
+
+Fri Mar 19 15:05:15 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * backup-specs (VOLNO_FILE): Removed abusive comment by Noah.
+
+ * buffer.c (new_volume): Write the global volume number to the
+ volno file before running the info script, so that the script
+ can look at it.
+
+Thu Mar 18 20:11:54 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * Makefile.in (AUX): Include `dump-remind' in distribution.
+
+ * backup-specs (SLEEP_MESSAGE): New variable.
+ level-0, level-1: Use it instead of external `dont_touch' file.
+
+ * level-0, level-1: Put most of the script in () and pipe
+ everything from the subshell through tee -a $LOGFILE. Since you
+ really want most of the output to go to the logfile anyway, and
+ since all those pipelines were preventing one from getting the
+ exit status of most commands, this seems like the right idea.
+
+ * level-0, level-1 (LOGFILE): Use YYYY-MM-DD (all numeric) format
+ for log file name, since that makes the file names sortable in a
+ coherent way. Suffix should always be `level-n' where n is the
+ dump level. level-0 script was just using `-full' instead.
+
+ * level-0, level-1 (DUMP_LEVEL): New variable. Set to `0' or `1'
+ in each script as appropriate.
+
+ * level-0, level-1 (HOST): Renamed to `localhost' for clarity.
+ (host): renamed to `remotehost' for clarity.
+
+ * level-0, level-1 (startdate): New variable. Use it in Subject
+ line of mailed report.
+
+ * level-0, level-1: Fixed all instances where sed is called with a
+ script on the command line to use `-e' option.
+
+ * level-0, level-1: Don't try to call logfile.sed to filter
+ LOGFILE. It's not distributed with tar and was never really used
+ anyway.
+
+ * level-0, level-1: Put quotes around most variable names (barring
+ those that are known to intentionally contain text that should be
+ expanded into multiple words, like `TAR_PART1').
+
+ * level-0, level-1: Got rid of annoying trailing backslashes in awk
+ scripts. They were gratuitous. Made them a little more readable
+ by adding some whitespace.
+
+Wed Mar 17 10:30:58 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c (describe, long_options): Changed --compress-block to
+ --block-compress.
+ (options): Fixed f_compress_block sanity check error message
+ to give the correct name of the option.
+
+Tue Mar 16 14:52:40 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * extract.c (extract_archive): case LF_DIR: Do chown when
+ necessary. Don't bother jumping to set_filestat for
+ f_modified; repeat the chmod code here. Replace `break',
+ deleted on 2 September 1992.
+
+ * tar.c (describe, long_options, options): Added gzip options
+ and use-compress-program option.
+ * tar.h: Added new compression options.
+ * buffer.c (child_open, open_archive): Use new compression options.
+
+ * create.c (start_header): Only mask off high bits when
+ creating old-style archives.
+ * list.c (decode_header): Mask off potentially misleading
+ high bits from the mode when reading headers.
+
+Mon Mar 15 11:34:34 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * extract.c (extract_archive): Put arguments in the right
+ order for error message.
+
+ * create.c (deal_with_sparse): if the last byte was null, we
+ didn't write it out.
+
+ * gnu.c, create.c, extract.c, diffarch.c, list.c throughout:
+ Replace malloc calls with ck_malloc and realloc with ck_realloc.
+
+ * tar.c (describe): Improve doc for -L.
+
+ * tar.c (name_next): Don't apply exclusion to explicitly named
+ files.
+
+ * tar.c (long_options, describe): Added new-volume-script as
+ an alias for info-script.
+
+ * extract.c (extract_archive): LF_DUMPDIR case; misplaced paren.
+
+ * extract.c (extract_archive): extract_file case, first if,
+ include space for null in namelen computation.
+
+ * extract.c (extract_sparse_file): Use value returned by write
+ to properly create error message.
+
+ * create.c (create_archive): Don't assume we have anything to
+ dump.
+
+ * buffer.c (open_archive): Set current_file_name for the
+ volume header so that verbose listings work properly.
+
+ * Makefile.in (realclean): Added getdate.c.
+
+Thu Jan 14 23:38:44 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu)
+
+ * tar.c: Include fnmatch.h after port.h to make sure we get our FNM_*
+ (e.g. on HPUX 8).
+
+Tue Nov 24 08:30:54 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * tar.c (addname), gnu.c (read_dir_file): Use HAVE_GETCWD, not USG.
+
+ * port.h, rmt.h: Use HAVE_STRING_H, not USG.
+
+ * port.h: Add dir header decls.
+ * create.c, gnu.c: Use SYSNDIR, SYSDIR, and NDIR
+ instead of BSD42 and USG. Rename DP_NAMELEN to NLENGTH.
+ Use `struct dirent' instead of `struct direct'.
+ * create.c, gnu.c, tar.c: Remove dir header decls.
+
+Wed Nov 18 15:31:30 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * tar.c: Change FNM_TARPATH to FNM_LEADING_DIR to match change
+ in fnmatch.[ch].
+
+Wed Oct 21 00:52:24 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1: put curly braces around variables for clarity.
+
+ * backup-specs (DUMP_REMIND_SCRIPT): define it (but commented out
+ so that distributed dump scripts won't use it by default).
+ level-0, level-1 (TAR_PART1): use --info-script if
+ DUMP_REMIND_SCRIPT is defined.
+ dump-remind: new file (intended as an example).
+
+Thu Oct 15 03:33:28 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1: remove $LOGFILE.tmp files before exiting.
+
+Fri Oct 2 00:28:01 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * tar.c (describe): Fix some tab alignments.
+
+ * Makefile.in (SRC3): Add getdate.c, for systems without bison/yacc
+ (like MS-DOS).
+
+ * diffarch.c (diff_sparse_files): Add missing arg to fprintf calls.
+
+ * extract.c (extract_archive, restore_saved_dir_info),
+ buffer.c (child_open), list.c (decode_header, print_header):
+ Delete unused vars.
+
+ * port.c [__MSDOS__]: Have strstr, rename, and mkdir. Don't
+ define ck_pipe.
+
+ * buffer.c, tar.c (init_volume_number, closeout_volume_number),
+ create.c (write_long): Declare as void, not int, since they
+ don't return a value.
+
+Thu Sep 24 00:06:02 1992 Michael I Bushnell (mib@churchy.gnu.ai.mit.edu)
+
+ * level-0, level-1 (TAR_PART1): remove --atime-preserve
+ because of a total screw.
+
+Tue Sep 22 14:15:48 1992 Michael I Bushnell (mib@wookumz.gnu.ai.mit.edu)
+
+ * buffer.c (close_archive): Removed leftover `break' from when
+ this was a switch.
+
+Tue Sep 22 08:33:16 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * create.c, port.h: indented #pragma directives with 1 space.
+
+Fri Sep 18 14:15:17 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * All source files: re indented using GNU indent.
+
+ * rtapelib.c (__rmt_read): Only read the amount left in the
+ buffer; otherwise a broken rmt server (which puts too much
+ data out) could overwrite past our buffer.
+
+Thu Sep 17 14:08:58 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c: Throughout, use struct utimbuf rather than array
+ of longs.
+
+ * configure.in: Check for getpwuid and getgrgid.
+
+ * Makefile.in (SRC3, AUX): Move alloca.c to SRC3.
+ (OBJ3): Add @ALLOCA@.
+
+ * Makefile.in (getdate.c): Look in srcdir for getdate.y.
+
+ * buffer.c (close_archive): We can't check WTERMSIG
+ meaningfully unless we already know tha WIFSIGNALED is true.
+ (There is no guarantee it WTERMSIG will return zero when
+ WIFSIGNALED is false.)
+ * port.c (rmdir, mkdir): Check WIFSIGNALED rather than
+ WTERMSIG.
+
+ * Makefile.in (getdate.c): Use $(YACC) instead of `yacc'.
+
+Tue Sep 15 14:49:48 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * version.c: Released version 1.11.1.
+
+ * Makefile (AUX): Added NEWS.
+
+ * Makefile.in (rmt): Added $(LIBS).
+ * configure.in: Added tests for libraries needed on Solaris.
+
+ * mangle.c (extract_mangle): Null terminate link name for
+ losing archives missing it.
+
+ * Makefile.in: added target and rule for getdate.c: getdate.y;
+ some makes don't have one built in.
+
+Mon Sep 14 16:23:15 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c (options, main): Advise use of --help rather than
+ +help.
+
+ * create.c (write_long): Using hstat here is a Bad Idea, and
+ totally unnecessary at that.
+
+ * list.c (read_header): Compute both signed and normal
+ checksums.
+
+ * configure.in: Define BSD in the presence of /sdmach or
+ /../../mach.
+
+ * diffarch.c, buffer.c: Declare valloc as void* rather than
+ char*.
+
+ * Makefile.in: Don't install info files.
+
+ * configure.in: Check for malloc was scrambled.
+
+ * port.h: Undefine index and rindex if necessary; some
+ string.h's define them for us.
+
+ * tar.c (addname): Missing braces after if.
+ * gnu.c (read_dir_file): Missing braces after if.
+
+ * names.c: Add include of <stdio.h>,
+
+ * create.c (start_header): Set current_file_name so that
+ print_header (used for verbose create) works properly.
+ (dump_file): Set current_link_name when setting up symlink
+ and hardlink records.
+
+Fri Sep 11 01:05:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * fnmatch.[ch]: New files.
+ * wildmat.c: File removed.
+ * tar.c: Include fnmatch.h and use fnmatch instead of wildmat.
+ * Makefile.in, makefile.pc: Replace wildmat.o(bj) with fnmatch.
+
+Thu Sep 10 23:19:30 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * buffer.c, tar.c: Remove redundant decls of getenv, rindex.
+
+ * Makefile.in: Add uninstall target.
+ Define libdir instead of hardcoding /etc for installing rmt.
+
+Thu Sep 10 13:06:03 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * list.c (read_header): On second thought, that doesn't work
+ either, so just store the names in malloced areas. Sigh.
+
+ * NEWS: New file.
+ * README: Removed things that belong in NEWS; point to it.
+
+ * list.c (read_header): current_file_name and
+ current_link_name need to be set to the arrays in head rather
+ than header; header is the actual read buffer and will change.
+
+ * extract.c (extract_archive):
+ * buffer.c (new_volume): `#' directives need to start in
+ column 1.
+
+Thu Sep 10 06:09:18 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1 (TAR_PART1): put --atime-preserve inside quotes.
+
+Wed Sep 9 13:34:26 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * Makefile.in (AUX): Add getpagesize.h.
+ (AUX): Comment out manuals.
+ (all): Comment out dependency on tar.info.
+
+ * version.c: Release of version 1.11.
+
+ * level-0, level-1 (TAR_PART1): Use --atime-preserve.
+
+ * Makefile, configure.in: Arrange to use local malloc on HP-UX.
+
+ * port.h Use the canonical Autoconf chunk for alloca instead
+ of just looking for gcc.
+
+Wed Sep 9 03:16:58 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * port.h: If compiling with gcc, use __builtin_alloca.
+
+Tue Sep 8 16:13:41 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * extract.c: Removed long name support from here.
+ * list.c (read_header): Understand and skip longname/longlink
+ headers here. Names for current file are stored in new global
+ variables. All source files except create.c changed to refer
+ to current_file_name and current_link_name instead of fields
+ directly from the current header.
+
+Thu Sep 3 12:41:08 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c (write_long): New function.
+ (dump_file): When writing link records or symlink records, use
+ new write_long function instead of mangling when the link
+ target is too long.
+ (start_header): Use write_long instead of mangling for long
+ names.
+ * extract.c (saverec): Recognize LF_LONGNAME and LF_LONGLINK.
+ (saverec): Throughout, use longname and longlink if they are set.
+
+Wed Sep 2 14:41:13 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * mangle.c: This is now deprecated; retain extract_mangle for
+ backward compatability.
+
+ * list.c (print_header): patch from Chris Arthur to prevent
+ printing 0 when the gid or uid is null.
+
+ * list.c (decode_header): patch from Chris Arthur to use the
+ gid field when the gid is empty, and similarly for uid.
+
+ * extract.c: saved_dir_info, saved_dir_info_head: new type and
+ var.
+ (extract_archive): When extracting directories, now save info
+ in saved_dir_info_head.
+ (restore_saved_dir_info): New function.
+ * list.c (read_and): Call restore_saved_dir_info at the end of
+ the run.
+ This patch is from Chris Arthur (csa@pennies.sw.stratus.com).
+
+Mon Aug 31 15:39:55 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c (create_archive): If there are no names specified,
+ write nothing on the archive instead of dumping ".".
+
+ * buffer.c (open_archive): Useful error message.
+
+ * tar.c, tar.h: Recognize f_atime_preserve.
+ * create.c (dump_file): Implement f_atime_preserve.
+
+ * rmt.h (_remdev): Don't require /dev/ to be in remote archive
+ names; obey new force-local flag.
+ * tar.c, tar.h: Implement new force-local flag.
+
+ * tar.c (describe): same-owner and same-order were confused.
+
+ * create.c (dump_file): Check for toplevel had sense reversed.
+
+ * buffer.c (new_archive): Don't free old_name...when these
+ come from the command line, they aren't malloced, and it isn't
+ important to save this trivial amount of memory.
+
+ * tar.h: replace ar_file with ar_files, n_ar_files,
+ cur_ar_files.
+ * buffer.c (open_archive): multi-volume compressed archives
+ never worked; give an appropriate error. Change open of
+ ar_file to open of ar_files[0].
+ (writeerror, readerror, flush_archive): use
+ ar_files[cur_ar_file] instead of ar_file.
+ (new_archive): Necessary changes to support ar_files.
+ * tar.c (options): handle multiple tape drive arguments.
+
+Fri Aug 28 17:42:13 1992 Michael I Bushnell (mib@wookumz.gnu.ai.mit.edu)
+
+ * list.c (decode_header), create.c (start_header), tar.h (TMAGIC):
+ Undo djm's changes below; tar does not support the final
+ Posix.1 format; it's bad to make it look like it does.
+
+Sun Jul 19 02:13:46 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * port.h: Try to prevent redefining major.
+ * port.c: HAVE_BZERO -> minix. Fix a typo.
+
+ * list.c (decode_header): Recognize the final POSIX.1 magic as
+ well as the early draft magic for ustar.
+ * create.c (start_header): Create a final POSIX.1 magic string
+ instead of an early draft string for ustar.
+ * tar.h (TMAGIC): Remove the trailing blanks.
+
+ * rmt.c, rtapelib.c: Use POSIX and STDC headers if available.
+ * rmt.h: Declare the external functions defined in rtapelib.c.
+
+Tue Jul 14 00:44:37 1992 David J. MacKenzie (djm@apple-gunkies.gnu.ai.mit.edu)
+
+ * pathmax.h: New file.
+ * port.h: Include it.
+ * create.c (create_archive): Allocate PATH_MAX instead of
+ NAME_MAX for temporary buffer so we don't have to figure out
+ what NAME_MAX is (portably).
+
+Fri Jul 10 08:30:42 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * gnu.c (collect_and_sort_names): write_dir_file has no argument.
+
+ * level-0, level-1: Avoid silly Sun awk lossage.
+
+Mon Jul 6 20:11:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * port.c (rename): If unlinking the source at the end fails,
+ unlink the destination instead to avoid leaving a mess.
+
+Fri Jul 3 15:16:42 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * buffer.c, diffarch.c, update.c, rtapelib.c: Change NO_MTIO to
+ HAVE_SYS_MTIO_H.
+
+ * port.c, tar.h: Change FOO_MISSING to HAVE_FOO.
+
+Tue Jun 23 23:39:02 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * rmt.c: Add #ifdefs to work on ISC.
+
+Wed May 20 00:12:27 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * port.h: Define major, minor, makedev if the system doesn't.
+
+Wed May 13 21:16:38 1992 Michael I Bushnell (mib@apple-gunkies.gnu.ai.mit.edu)
+
+ * gnu.c (add_dir_name): Store legitimate value into
+ dir_contents when get_dir_contents returns NULL.
+
+Thu May 7 23:44:35 1992 Michael I Bushnell (mib@apple-gunkies.gnu.ai.mit.edu)
+
+ * gnu.c (add_dir_name): Check for return of NULL from get_dir_contents;
+ see djm's change of Fri Jul 26 01:12:58 1991.
+
+Mon May 4 22:50:57 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * tar.h: Make comments for option names say -- instead of +.
+
+Thu Apr 30 03:09:16 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-1: Added `$' before VOLNO_FILE in definition of TAR_PART1.
+ Added line to remove $VOLNO_FILE from any previous dump before
+ starting.
+
+ * level-0, level-1: Change long options to use `--' instead of `+'
+ (support for `+' will go away soon)
+
+Wed Apr 29 14:23:10 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c, tar.t: Added +volno-file option.
+ buffer.c: New functions init_volume_number,
+ closeout_volume_number.
+ tar.c (main): Call new functions in the right place.
+
+ * buffer.c (fl_write, fl_read): Mod to allow losing tape
+ drives which use short counts to indicate end of tape
+ correctly handle the multi-tape stuff. The read half won't
+ co-exist with f_reblock; there's no way to fix that, of
+ course.
+
+ * tar.c, tar.h: Added new option +show-omitted-dirs, from
+ Karl Berry.
+ list.c (read_and): Implemented show-omitted-dirs.
+
+ * tar.c, tar.h: Added new option +checkpoint.
+ buffer.c (fl_read, fl_write): Implemented +checkpoint lazily.
+
+ * create.c (dump_file): Added toplevel argument; some devices
+ can be negative, so the old method was bogus. All callers
+ changed.
+
+ * tar.c, tar.h: Added new option +ignore-failed-read.
+ create.c (dump_file): Implemented +ignore-failed-read.
+
+ * create.c (finish_sparse_file): Commented out debugging printf.
+
+ * tar.c, tar.h: Added new option +remove-files to delete files
+ after they are added to the archive.
+ create.c (dump_file): Implemented +remove-files for
+ everything but directories. I don't think they need it.
+
+Tue Apr 28 13:21:42 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c: (dump_file): save_name needs to be set equal to p,
+ not something inside the header, because the header changes at
+ the first buffer flush.
+
+Fri Apr 24 10:41:13 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c: Djm incorrectly moved the include of port.h to
+ precede the include of sys/file.h; restored.
+
+ * tar.c (main): Cases CMD_EXTRACT and CMD_LIST: declare error
+ string with const.
+
+ * gnu.c (collect_and_sort_names): Leave if around
+ write_dir_file in place.
+
+Wed Apr 22 02:16:14 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * rtapelib.c: SIGTYPE -> RETSIGTYPE.
+
+Mon Mar 9 22:42:05 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * rtapelib.c: Reformat and make comments more complete.
+ Rename a few variables for clarity.
+
+Thu Mar 5 14:07:34 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * tar.c (describe): Document long options as starting with --.
+
+Thu Jan 23 22:54:41 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tar.c (options): Check get_date return value for error indication.
+
+Tue Dec 24 00:03:03 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tar.c, gnu.c, extract.c, create.c, port.h, rmt.h: Change
+ POSIX ifdefs to HAVE_UNISTD_H and _POSIX_VERSION.
+
+Fri Dec 20 13:50:38 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * testpad.c (main): flush stderr so perror and fprintf
+ cooperate right.
+
+Wed Dec 18 16:52:42 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * port.h: Check MAJOR_IN_MKDEV and MAJOR_IN_SYSMACROS to find
+ where to get major, minor and makedev.
+ * create.c, list.c, update.c: Don't check USG to include
+ sys/sysmacros.h.
+
+Thu Dec 12 21:57:10 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * mangle.c (extract_mangle): Correctly null terminate name of
+ link target.
+
+Thu Nov 21 07:44:18 1991 Michael I Bushnell (mib at nutrimat)
+
+ * create.c (dump_file, at start of ISREG output loop): use
+ filename from header instead of real name to make sure that we
+ get the mangled version and not one that is too long and
+ overflows buffers.
+
+Sat Nov 16 01:37:45 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tar.h: Use new criteria for STDC version of msg.
+
+Sat Nov 2 21:31:57 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * create.c, gnu.c, tar.c: Use DIRENT instead of NDIR to select
+ between dirent.h and ndir.h for USG.
+
+ * port.c: Rename WANT_FOO to FOO_MISSING to make sharing code
+ and configure script with other utilities easier. Use
+ VPRINTF_MISSING and DOPRNT_MISSING instead of FOO_MSG to
+ select error reporting routines.
+
+Thu Oct 17 20:19:02 1991 Michael I Bushnell (mib at churchy.gnu.ai.mit.edu)
+
+ * level-0: Repair damage from previous mod: stdin to rsh must
+ be the terminal or tar's questions lose.
+
+Sat Aug 31 15:05:27 1991 Noah Friedman (friedman at nutrimat.gnu.ai.mit.edu)
+
+ * level-0: Fixed several syntax errors associated with
+ stdout/stderr redirection.
+ Made sure remote host executes commands from sh where redirection
+ is necessary, since root's shell might be csh in some places and
+ the redirect syntax differs.
+
+Thu Aug 29 00:54:01 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c (long_options). Fixed info-script long option.
+
+Mon Aug 26 16:53:50 1991 David J. MacKenzie (djm at pogo.gnu.ai.mit.edu)
+
+ * configure, Makefile.in: Only put $< in Makefiles if VPATH
+ is being used, because older makes don't understand it.
+
+Mon Aug 19 01:47:57 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * create.c: Indent '#pragma alloca' so non-ANSI compilers
+ don't choke on it.
+
+Wed Aug 14 14:10:43 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * list.c (UGSWIDTH): Increase from 11 (sort of like Unix tar) to
+ 18, so that with normal user and group names of <= 8 chars,
+ the columns never shift in a tar -t listing.
+
+Fri Aug 2 00:41:08 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * Makefile.in (dist): Include texinfo.tex and tar.info*.
+ (install): Install tar.info*.
+ * configure: Set INSTALLDATA.
+
+ * configure: Create config.status. Remove it and Makefile if
+ interrupted while creating them.
+
+ * configure: Check for +srcdir etc. arg and look for
+ Makefile.in in that directory. Set VPATH if srcdir is not `.'.
+ * Makefile.in: Add `prefix'.
+ (tar.info): New target.
+
+Tue Jul 30 17:08:04 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * configure: NEED_TZSET has become FTIME_MISSING.
+
+Mon Jul 29 19:23:10 1991 David J. MacKenzie (djm at wombat.gnu.ai.mit.edu)
+
+ * port.c [F_CHSIZE]: Additional version.
+
+Sat Jul 27 22:27:47 1991 David J. MacKenzie (djm at wombat.gnu.ai.mit.edu)
+
+ * rmt.h: Clean up ifdefs.
+
+ * makefile.pc: Fix typo.
+ port.h: Change MSDOS to __MSDOS__.
+ [__MSDOS__]: Define off_t. Include io.h and not sys/param.h.
+ [__TURBOC__]: Use void * and don't define const.
+
+Fri Jul 26 01:12:58 1991 David J. MacKenzie (djm at bleen)
+
+ * buffer.c: Rename `eof' to `hit_eof' to avoid conflict with an
+ MSDOS function.
+ * gnu.c (get_dir_contents): Return NULL, not "\0\0\0\0", on error.
+ * diffarch.c (diff_archive): Open files in binary mode.
+ Don't use or free a non-malloc'd return value from get_dir_contents.
+ * msd_dir.c [__TURBOC__]: Include stdlib.h.
+ * rmt.h: lseek returns off_t, not long.
+
+ * tar.c (describe): -X is +exclude-from, not +exclude.
+ (names_notfound): Free memory only if amiga, not !unix.
+
+ * tar.h, tar.c: Add +null option to make -T read
+ null-terminated filenames (such as those produced by GNU find
+ -print0), and disable -C option.
+ This guarantees that odd filenames will get archived.
+ * tar.c (read_name_from_file): New function.
+ (name_next): Call it instead of fgets.
+
+Wed Jul 24 11:17:48 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * create.c [_AIX]: Declare alloca.
+
+ * buffer.c (open_archive): Check for successful open before,
+ not after, fstatting the fd.
+
+Tue Jul 23 20:51:31 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * configure: Only define BSD42 if sys/file.h exists.
+ If alloca is missing and /usr/ucblib exists (SVR4), use it
+ instead of -lPW.
+
+ * port.h [!__STDC__]: #define const.
+ * gnu.c (dirent_cmp): Fix args to agree with ANSI C prototype.
+ * create.c: Declare ck_realloc.
+ * gnu.c, diffarch.c: Move check for symlinks to after port.h include.
+
+Sat Jul 20 00:03:54 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * msd_dir.[ch]: Use POSIX-style `struct dirent' instead of
+ `struct direct'.
+ * create.c, gnu.c, tar.c: Adjust callers.
+
+Thu Jul 18 00:05:01 1991 David J. MacKenzie (djm at bleen)
+
+ * port.c (ck_malloc, ck_realloc): Return PTR, not char *.
+ * gnu.c, create.c, tar.c: Fix decls.
+
+ * port.c: Don't use the preprocessor to guess missing
+ functions on Unix; let configure do it.
+ [WANT_GETWD] (getwd): Function removed; not needed because
+ getcwd is used if needed.
+ * gnu.c, tar.c: Use getcwd if POSIX.
+
+ * rtapelib.c: Use SIGTYPE instead of testing SIGNAL_VOID.
+ Default to void (more common these days) instead of int.
+
+ * tar.c, gnu.c, mangle.c: Remove VOIDSTAR defn. Use PTR instead.
+ * port.h: Define PTR.
+
+ * gnu.c, tar.c [__MSDOS__ || USG]: Remove incorrect getcwd
+ decl; put correct one in port.h [!POSIX].
+
+ * tar.c (describe): Print on stdout instead of stderr; it's
+ not so much a usage message (since you have to ask for it
+ explicitly) as on-line help, and you really need to be able to
+ page it because it's more than a screen long.
+
+ * Make #ifdefs for sys/file.h or fcntl.h, directory header,
+ sys/mtio.h consistent between files. Use NO_MTIO instead of
+ tricks with USG and HAVE_MTIO and NO_RMTIOCTL.
+ * Move decls of ANSI C and POSIX functions to port.h and
+ use standard headers to declare them if available
+ [STDC_HEADERS or POSIX].
+ * Add many missing function declarations and return types.
+ * Some places used __MSDOS__, some MSDOS; standardize on __MSDOS__.
+ * Change S_IF macros to S_IS for POSIX.
+ * port.h: Define appropriate S_IS macros if missing.
+ * port.h: Rename macros for testing exit status to conform to
+ POSIX; use the system's versions if available [POSIX].
+ * Use POSIX PATH_MAX and NAME_MAX instead of MAXPATHLEN and MAXNAMLEN.
+ * port.h: Define PATH_MAX and NAME_MAX.
+ * create.c, gnu.c, tar.c: Use ck_malloc and free instead of
+ auto arrays of size PATH_MAX or NAME_MAX, since with pathconf
+ they might not be constants.
+ * Move all definitions of O_* to port.h to reduce redundancy.
+ * Make all source files that now need to include port.h do so.
+ * port.c: Remove #undefs of WANT_* so you can use -DWANT_*
+ when compiling, instead of having to edit port.c.
+ [WANT_DUMB_GET_DATE] (get_date): Function removed.
+ Even systems without bison can get bison output and compile it.
+ [WANT_STRING] (index, rindex, bcopy, bzero, bcmp): Functions
+ removed; the translation is now done by macros in port.h.
+ * wildmat.c (wildmat): Use POSIX.2 '!' instead of '^' to negate
+ character classes.
+
+Mon Jul 15 13:47:45 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * testpad.c (main): Return type void.
+
+ * port.c [WANT_STRING]: Don't include memory.h if NO_MEMORY_H.
+
+ * create.c (dump_file) [AIX]: Fix typo, `allocate' for `alloca'.
+ * gnu.c (collect_and_sort_names): Move misplaced brace out of #ifdef.
+ From: Minh Tran-Le <TRANLE@intellicorp.com>.
+
+ * configure: Also look in sys/signal.h for signal decl.
+
+Wed Jul 10 01:42:55 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Rename rtape_server.c to rmt.c and rtape_lib.c to rtapelib.c.
+
+ * configure, Makefile.in: $(INSTALLPROG) -> $(INSTALL).
+
+Tue Jul 9 01:38:37 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Most files: Refer to GPL version 2.
+ * COPYING: Use version 2.
+
+ * port.c [__TURBOC__] (utime): New function.
+
+ * xmalloc: New function (just calls ck_malloc), for alloca.c
+ and bison.simple (in getdate.y output).
+
+ * Makefile.in (AUX): Include alloca.c and tcexparg.c, a
+ command line globber for Turbo C.
+
+Mon Jul 8 14:30:52 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * testpad.c: Open and write to testpad.h instead of stdout,
+ because some MS-DOS makes (Borland's at least) can't do
+ redirection in commands.
+ * Makefile.in: Don't redirect testpad output.
+
+Mon Jul 8 12:56:35 1991 Michael I Bushnell (mib at churchy.gnu.ai.mit.edu)
+
+ * buffer.c (fl_read): Missing \n in printf.
+
+Mon Jul 8 03:40:28 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * create.c, extract.c, gnu.c, diffarch.c, tar.c: Comment out
+ unused variables.
+
+ * tar.c (options): Cast get_date arg to VOIDSTAR instead of
+ `struct timeb *', since on some non-BSD systems the latter is
+ undefined.
+
+Sat Jul 6 04:53:14 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * Replace Makefile with configure, Makefile.in, and makefile.pc.
+ Update README with current compilation instructions.
+
+ * port.c [WANT_RENAME] (rename): New function.
+
+Wed Jul 3 18:10:52 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * testpad.c (main): Avoid warning from some compilers on array
+ address.
+
+ * rtape_server.c (sys_errlist): Should be declared extern.
+
+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 <return> 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/gnu/usr.bin/tar/Makefile b/gnu/usr.bin/tar/Makefile
new file mode 100644
index 000000000000..810fe3b7a74d
--- /dev/null
+++ b/gnu/usr.bin/tar/Makefile
@@ -0,0 +1,14 @@
+PROG= tar
+SRCS= buffer.c create.c diffarch.c extract.c fnmatch.c getdate.y \
+ getoldopt.c getopt.c getopt1.c gnu.c list.c mangle.c names.c port.c \
+ regex.c rtapelib.c tar.c update.c version.c
+CFLAGS+= -DRETSIGTYPE=void -DDIRENT=1 -DHAVE_SYS_MTIO_H=1 -DHAVE_UNISTD_H=1
+CFLAGS+= -DHAVE_GETGRGID=1 -DHAVE_GETPWUID=1 -DHAVE_STRING_H=1
+CFLAGS+= -DHAVE_LIMITS_H=1 -DHAVE_STRSTR=1 -DHAVE_VALLOC=1 -DHAVE_MKDIR=1
+CFLAGS+= -DHAVE_MKNOD=1 -DHAVE_RENAME=1 -DHAVE_FTRUNCATE=1 -DHAVE_GETCWD=1
+CFLAGS+= -DHAVE_VPRINTF=1 -DNEEDPAD -I${.CURDIR}
+CFLAGS+= -DDEF_AR_FILE=\"/dev/rst0\" -DDEFBLOCKING=20
+NOMAN=noman
+
+.include <bsd.prog.mk>
+.include "../../usr.bin/Makefile.inc"
diff --git a/gnu/usr.bin/tar/Makefile.gnu b/gnu/usr.bin/tar/Makefile.gnu
new file mode 100644
index 000000000000..a03617ae3666
--- /dev/null
+++ b/gnu/usr.bin/tar/Makefile.gnu
@@ -0,0 +1,185 @@
+# Generated automatically from Makefile.in by configure.
+# Un*x Makefile for GNU tar program.
+# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#### Start of system configuration section. ####
+
+srcdir = .
+VPATH = .
+
+# If you use gcc, you should either run the fixincludes script that
+# comes with it or else use gcc with the -traditional option. Otherwise
+# ioctl calls will be compiled incorrectly on some systems.
+CC = gcc
+YACC = bison -y
+INSTALL = /usr/local/bin/install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL) -m 644
+
+# Things you might add to DEFS:
+# -DSTDC_HEADERS If you have ANSI C headers and libraries.
+# -DHAVE_UNISTD_H If you have unistd.h.
+# -DHAVE_STRING_H If you don't have ANSI C headers but have string.h.
+# -DHAVE_LIMITS_H If you have limits.h.
+# -DBSD42 If you have sys/dir.h (unless you use -DPOSIX),
+# sys/file.h, and st_blocks in `struct stat'.
+# -DDIRENT If you have dirent.h.
+# -DSYSNDIR Old Xenix systems (sys/ndir.h).
+# -DSYSDIR Old BSD systems (sys/dir.h).
+# -DNDIR Old System V systems (ndir.h).
+# -DMAJOR_IN_MKDEV If major, minor, makedev defined in sys/mkdev.h.
+# -DMAJOR_IN_SYSMACROS If major, minor, makedev defined in sys/sysmacros.h.
+# -DRETSIGTYPE=int If your signal handlers return int, not void.
+# -DHAVE_SYS_MTIO_H If you have sys/mtio.h (magtape ioctls).
+# -DHAVE_SYS_GENTAPE_H If you have sys/gentape.h (ISC magtape ioctls).
+# -DHAVE_NETDB_H To use rexec for remote tape operations
+# instead of forking rsh or remsh.
+# -DNO_REMOTE If you have neither a remote shell nor rexec.
+# -DHAVE_VPRINTF If you have vprintf function.
+# -DHAVE_DOPRNT If you have _doprnt function (but lack vprintf).
+# -DHAVE_FTIME If you have ftime system call.
+# -DHAVE_STRSTR If you have strstr function.
+# -DHAVE_VALLOC If you have valloc function.
+# -DHAVE_MKDIR If you have mkdir and rmdir system calls.
+# -DHAVE_MKNOD If you have mknod system call.
+# -DHAVE_RENAME If you have rename system call.
+# -DHAVE_GETCWD If not POSIX.1 but have getcwd function.
+# -DHAVE_FTRUNCATE If you have ftruncate system call.
+# -DV7 On Version 7 Unix (not tested in a long time).
+# -DEMUL_OPEN3 If you lack a 3-argument version of open, and want
+# to emulate it with system calls you do have.
+# -DNO_OPEN3 If you lack the 3-argument open and want to
+# disable the tar -k option instead of emulating open.
+# -DXENIX If you have sys/inode.h and need it to be included.
+
+DEF_AR_FILE = /dev/rst0
+DEFBLOCKING = 20
+DEFS = -DRETSIGTYPE=void -DDIRENT=1 -DHAVE_SYS_MTIO_H=1 -DHAVE_UNISTD_H=1 -DHAVE_GETGRGID=1 -DHAVE_GETPWUID=1 -DHAVE_STRING_H=1 -DHAVE_LIMITS_H=1 -DHAVE_STRSTR=1 -DHAVE_VALLOC=1 -DHAVE_MKDIR=1 -DHAVE_MKNOD=1 -DHAVE_RENAME=1 -DHAVE_FTRUNCATE=1 -DHAVE_GETCWD=1 -DHAVE_VPRINTF=1 -DDEF_AR_FILE=\"$(DEF_AR_FILE)\" -DDEFBLOCKING=$(DEFBLOCKING)
+
+# Set this to rtapelib.o unless you defined NO_REMOTE, in which case
+# make it empty.
+RTAPELIB = rtapelib.o
+LIBS =
+
+CFLAGS = -g
+LDFLAGS = -g
+
+prefix = /usr/bin
+exec_prefix = $(prefix)
+
+# Prefix for each installed program, normally empty or `g'.
+binprefix =
+
+# The directory to install tar in.
+bindir = $(exec_prefix)/bin
+
+# Where to put the rmt executable.
+libdir = /sbin
+
+# The directory to install the info files in.
+infodir = $(prefix)/info
+
+#### End of system configuration section. ####
+
+SHELL = /bin/sh
+
+SRC1 = tar.c create.c extract.c buffer.c getoldopt.c update.c gnu.c mangle.c
+SRC2 = version.c list.c names.c diffarch.c port.c fnmatch.c getopt.c malloc.c
+SRC3 = getopt1.c regex.c getdate.y getdate.c alloca.c
+SRCS = $(SRC1) $(SRC2) $(SRC3)
+OBJ1 = tar.o create.o extract.o buffer.o getoldopt.o update.o gnu.o mangle.o
+OBJ2 = version.o list.o names.o diffarch.o port.o fnmatch.o getopt.o
+OBJ3 = getopt1.o regex.o getdate.o $(RTAPELIB)
+OBJS = $(OBJ1) $(OBJ2) $(OBJ3)
+AUX = README INSTALL NEWS COPYING ChangeLog Makefile.in makefile.pc \
+ configure configure.in \
+ tar.h fnmatch.h pathmax.h port.h open3.h getopt.h regex.h \
+ rmt.h rmt.c rtapelib.c \
+ msd_dir.h msd_dir.c tcexparg.c \
+ level-0 level-1 backup-specs dump-remind getpagesize.h
+# tar.texinfo tar.info* texinfo.tex \
+
+all: tar rmt
+# tar.info
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I. $<
+
+tar: $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+rmt: rmt.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(srcdir)/rmt.c $(LIBS)
+
+tar.info: tar.texinfo
+ makeinfo $(srcdir)/tar.texinfo
+
+install: all
+ $(INSTALL_PROGRAM) tar $(bindir)/$(binprefix)tar
+ -test ! -f rmt || $(INSTALL_PROGRAM) rmt $(libdir)/rmt
+# for file in $(srcdir)/tar.info*; \
+# do $(INSTALL_DATA) $$file $(infodir)/$$file; \
+# done
+
+uninstall:
+ rm -f $(bindir)/$(binprefix)tar $(infodir)/tar.info*
+ -rm -f $(libdir)/rmt
+
+$(OBJS): tar.h pathmax.h port.h
+regex.o buffer.o tar.o: regex.h
+tar.o fnmatch.o: fnmatch.h
+
+getdate.c: getdate.y
+ $(YACC) $(srcdir)/getdate.y
+ mv y.tab.c getdate.c
+# getdate.y has 8 shift/reduce conflicts.
+
+TAGS: $(SRCS)
+ etags $(SRCS)
+
+clean:
+ rm -f *.o tar rmt core
+mostlyclean: clean
+
+distclean: clean
+ rm -f Makefile config.status
+
+realclean: distclean
+ rm -f TAGS *.info* getdate.c y.tab.c
+
+shar: $(SRCS) $(AUX)
+ shar $(SRCS) $(AUX) | gzip > tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c`.shar.z
+
+dist: $(SRCS) $(AUX)
+ echo tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c` > .fname
+ -rm -rf `cat .fname`
+ mkdir `cat .fname`
+ for file in $(SRCS) $(AUX); do \
+ ln $$file `cat .fname` || cp $$file `cat .fname`; done
+ tar chzf `cat .fname`.tar.z `cat .fname`
+ -rm -rf `cat .fname` .fname
+
+tar.zoo: $(SRCS) $(AUX)
+ -rm -rf tmp.dir
+ -mkdir tmp.dir
+ -rm tar.zoo
+ for X in $(SRCS) $(AUX) ; do echo $$X ; sed 's/$$/ /' $$X > tmp.dir/$$X ; done
+ cd tmp.dir ; zoo aM ../tar.zoo *
+ -rm -rf tmp.dir
+
+# Prevent GNU make v3 from overflowing arg limit on SysV.
+.NOEXPORT:
diff --git a/gnu/usr.bin/tar/README b/gnu/usr.bin/tar/README
new file mode 100644
index 000000000000..4b577e78eb70
--- /dev/null
+++ b/gnu/usr.bin/tar/README
@@ -0,0 +1,40 @@
+Hey! Emacs! Yo! This is -*- Text -*- !!!
+
+This GNU tar 1.11.2. Please send bug reports, etc., to
+bug-gnu-utils@prep.ai.mit.edu. This is a beta-test release. Please
+try it out. There is no manual; the release of version 1.12 will
+contain a manual.
+
+GNU tar is based heavily on John Gilmore's public domain tar, but with
+added features. The manual is currently being written.
+
+This distribution also includes rmt, the remote tape server (which
+normally must reside in /etc). The mt tape drive control program is
+in the GNU cpio distribution.
+
+See the file INSTALL for compilation and installation instructions for Unix.
+See the file NEWS for information on all that is new in this version
+of tar.
+
+makefile.pc is a makefile for Turbo C 2.0 on MS-DOS.
+
+Various people have been having problems using floppies on a NeXT. In
+order to have them work right, you need to kill the automounting
+program which tries to monut floppies as soon as they are added.
+
+If you want to do incremental dumps, use the distributed backup
+scripts. They are what we use at the FSF to do all our backups. Most
+importantly, do not use --incremental (-G) or --after-date (-N) or
+--newer-mtime to do incremental dumps. The only option that works
+correctly for this purpose is --listed-incremental. (When extracting
+incremental dumps, use --incremental (-G).)
+
+If your system needs to link with -lPW to get alloca, but has
+rename in the C library (so HAVE_RENAME is defined), -lPW might
+give you an incorrect version of rename. On HP-UX this manifests
+itself as an undefined data symbol called "Error" when linking cp, ln,
+and mv. If this happens, use `ar x' to extract alloca.o from libPW.a
+and `ar rc' to put it in a library liballoca.a, and put that in LIBS
+instead of -lPW. This problem does not occur when using gcc, which
+has alloca built in.
+
diff --git a/gnu/usr.bin/tar/buffer.c b/gnu/usr.bin/tar/buffer.c
new file mode 100644
index 000000000000..e0ffc2d28654
--- /dev/null
+++ b/gnu/usr.bin/tar/buffer.c
@@ -0,0 +1,1584 @@
+/* Buffer management for tar.
+ Copyright (C) 1988, 1992, 1993 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 2, 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. */
+
+/*
+ * Buffer management for tar.
+ *
+ * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <sys/types.h> /* For non-Berkeley systems */
+#include <signal.h>
+#include <time.h>
+time_t time ();
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifdef __MSDOS__
+#include <process.h>
+#endif
+
+#ifdef XENIX
+#include <sys/inode.h>
+#endif
+
+#include "tar.h"
+#include "port.h"
+#include "rmt.h"
+#include "regex.h"
+
+/* Either stdout or stderr: The thing we write messages (standard msgs, not
+ errors) to. Stdout unless we're writing a pipe, in which case stderr */
+FILE *msg_file = stdout;
+
+#define STDIN 0 /* Standard input file descriptor */
+#define STDOUT 1 /* Standard output file descriptor */
+
+#define PREAD 0 /* Read file descriptor from pipe() */
+#define PWRITE 1 /* Write file descriptor from pipe() */
+
+#define MAGIC_STAT 105 /* Magic status returned by child, if
+ it can't exec. We hope compress/sh
+ never return this status! */
+
+void *valloc ();
+
+void writeerror ();
+void readerror ();
+
+void ck_pipe ();
+void ck_close ();
+
+int backspace_output ();
+extern void finish_header ();
+void flush_archive ();
+int isfile ();
+int new_volume ();
+void verify_volume ();
+extern void to_oct ();
+
+#ifndef __MSDOS__
+/* Obnoxious test to see if dimwit is trying to dump the archive */
+dev_t ar_dev;
+ino_t ar_ino;
+#endif
+
+/*
+ * The record pointed to by save_rec should not be overlaid
+ * when reading in a new tape block. Copy it to record_save_area first, and
+ * change the pointer in *save_rec to point to record_save_area.
+ * Saved_recno records the record number at the time of the save.
+ * This is used by annofile() to print the record number of a file's
+ * header record.
+ */
+static union record **save_rec;
+union record record_save_area;
+static long saved_recno;
+
+/*
+ * PID of child program, if f_compress or remote archive access.
+ */
+static int childpid = 0;
+
+/*
+ * Record number of the start of this block of records
+ */
+long baserec;
+
+/*
+ * Error recovery stuff
+ */
+static int r_error_count;
+
+/*
+ * Have we hit EOF yet?
+ */
+static int hit_eof;
+
+/* Checkpointing counter */
+static int checkpoint;
+
+/* JF we're reading, but we just read the last record and its time to update */
+extern time_to_start_writing;
+int file_to_switch_to = -1; /* If remote update, close archive, and use
+ this descriptor to write to */
+
+static int volno = 1; /* JF which volume of a multi-volume tape
+ we're on */
+static int global_volno = 1; /* Volume number to print in external messages. */
+
+char *save_name = 0; /* Name of the file we are currently writing */
+long save_totsize; /* total size of file we are writing. Only
+ valid if save_name is non_zero */
+long save_sizeleft; /* Where we are in the file we are writing.
+ Only valid if save_name is non-zero */
+
+int write_archive_to_stdout;
+
+/* Used by fl_read and fl_write to store the real info about saved names */
+static char real_s_name[NAMSIZ];
+static long real_s_totsize;
+static long real_s_sizeleft;
+
+/* Reset the EOF flag (if set), and re-set ar_record, etc */
+
+void
+reset_eof ()
+{
+ if (hit_eof)
+ {
+ hit_eof = 0;
+ ar_record = ar_block;
+ ar_last = ar_block + blocking;
+ ar_reading = 0;
+ }
+}
+
+/*
+ * Return the location of the next available input or output record.
+ * Return NULL for EOF. Once we have returned NULL, we just keep returning
+ * it, to avoid accidentally going on to the next file on the "tape".
+ */
+union record *
+findrec ()
+{
+ if (ar_record == ar_last)
+ {
+ if (hit_eof)
+ return (union record *) NULL; /* EOF */
+ flush_archive ();
+ if (ar_record == ar_last)
+ {
+ hit_eof++;
+ return (union record *) NULL; /* EOF */
+ }
+ }
+ return ar_record;
+}
+
+
+/*
+ * Indicate that we have used all records up thru the argument.
+ * (should the arg have an off-by-1? XXX FIXME)
+ */
+void
+userec (rec)
+ union record *rec;
+{
+ while (rec >= ar_record)
+ ar_record++;
+ /*
+ * Do NOT flush the archive here. If we do, the same
+ * argument to userec() could mean the next record (if the
+ * input block is exactly one record long), which is not what
+ * is intended.
+ */
+ if (ar_record > ar_last)
+ abort ();
+}
+
+
+/*
+ * Return a pointer to the end of the current records buffer.
+ * All the space between findrec() and endofrecs() is available
+ * for filling with data, or taking data from.
+ */
+union record *
+endofrecs ()
+{
+ return ar_last;
+}
+
+
+/*
+ * Duplicate a file descriptor into a certain slot.
+ * Equivalent to BSD "dup2" with error reporting.
+ */
+void
+dupto (from, to, msg)
+ int from, to;
+ char *msg;
+{
+ int err;
+
+ if (from != to)
+ {
+ err = close (to);
+ if (err < 0 && errno != EBADF)
+ {
+ msg_perror ("Cannot close descriptor %d", to);
+ exit (EX_SYSTEM);
+ }
+ err = dup (from);
+ if (err != to)
+ {
+ msg_perror ("cannot dup %s", msg);
+ exit (EX_SYSTEM);
+ }
+ ck_close (from);
+ }
+}
+
+#ifdef __MSDOS__
+void
+child_open ()
+{
+ fprintf (stderr, "MS-DOS %s can't use compressed or remote archives\n", tar);
+ exit (EX_ARGSBAD);
+}
+
+#else
+void
+child_open ()
+{
+ int pipe[2];
+ int err = 0;
+
+ int kidpipe[2];
+ int kidchildpid;
+
+#define READ 0
+#define WRITE 1
+
+ ck_pipe (pipe);
+
+ childpid = fork ();
+ if (childpid < 0)
+ {
+ msg_perror ("cannot fork");
+ exit (EX_SYSTEM);
+ }
+ if (childpid > 0)
+ {
+ /* We're the parent. Clean up and be happy */
+ /* This, at least, is easy */
+
+ if (ar_reading)
+ {
+ f_reblock++;
+ archive = pipe[READ];
+ ck_close (pipe[WRITE]);
+ }
+ else
+ {
+ archive = pipe[WRITE];
+ ck_close (pipe[READ]);
+ }
+ return;
+ }
+
+ /* We're the kid */
+ if (ar_reading)
+ {
+ dupto (pipe[WRITE], STDOUT, "(child) pipe to stdout");
+ ck_close (pipe[READ]);
+ }
+ else
+ {
+ dupto (pipe[READ], STDIN, "(child) pipe to stdin");
+ ck_close (pipe[WRITE]);
+ }
+
+ /* We need a child tar only if
+ 1: we're reading/writing stdin/out (to force reblocking)
+ 2: the file is to be accessed by rmt (compress doesn't know how)
+ 3: the file is not a plain file */
+#ifdef NO_REMOTE
+ if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && isfile (ar_files[0]))
+#else
+ if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && !_remdev (ar_files[0]) && isfile (ar_files[0]))
+#endif
+ {
+ /* We don't need a child tar. Open the archive */
+ if (ar_reading)
+ {
+ archive = open (ar_files[0], O_RDONLY | O_BINARY, 0666);
+ if (archive < 0)
+ {
+ msg_perror ("can't open archive %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+ dupto (archive, STDIN, "archive to stdin");
+ /* close(archive); */
+ }
+ else
+ {
+ archive = creat (ar_files[0], 0666);
+ if (archive < 0)
+ {
+ msg_perror ("can't open archive %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+ dupto (archive, STDOUT, "archive to stdout");
+ /* close(archive); */
+ }
+ }
+ else
+ {
+ /* We need a child tar */
+ ck_pipe (kidpipe);
+
+ kidchildpid = fork ();
+ if (kidchildpid < 0)
+ {
+ msg_perror ("child can't fork");
+ exit (EX_SYSTEM);
+ }
+
+ if (kidchildpid > 0)
+ {
+ /* About to exec compress: set up the files */
+ if (ar_reading)
+ {
+ dupto (kidpipe[READ], STDIN, "((child)) pipe to stdin");
+ ck_close (kidpipe[WRITE]);
+ /* dup2(pipe[WRITE],STDOUT); */
+ }
+ else
+ {
+ /* dup2(pipe[READ],STDIN); */
+ dupto (kidpipe[WRITE], STDOUT, "((child)) pipe to stdout");
+ ck_close (kidpipe[READ]);
+ }
+ /* ck_close(pipe[READ]); */
+ /* ck_close(pipe[WRITE]); */
+ /* ck_close(kidpipe[READ]);
+ ck_close(kidpipe[WRITE]); */
+ }
+ else
+ {
+ /* Grandchild. Do the right thing, namely sit here and
+ read/write the archive, and feed stuff back to compress */
+ tar = "tar (child)";
+ if (ar_reading)
+ {
+ dupto (kidpipe[WRITE], STDOUT, "[child] pipe to stdout");
+ ck_close (kidpipe[READ]);
+ }
+ else
+ {
+ dupto (kidpipe[READ], STDIN, "[child] pipe to stdin");
+ ck_close (kidpipe[WRITE]);
+ }
+
+ if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
+ {
+ if (ar_reading)
+ archive = STDIN;
+ else
+ archive = STDOUT;
+ }
+ else /* This can't happen if (ar_reading==2)
+ archive = rmtopen(ar_files[0], O_RDWR|O_CREAT|O_BINARY, 0666);
+ else */ if (ar_reading)
+ archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
+ else
+ archive = rmtcreat (ar_files[0], 0666);
+
+ if (archive < 0)
+ {
+ msg_perror ("can't open archive %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+
+ if (ar_reading)
+ {
+ for (;;)
+ {
+ char *ptr;
+ int max, count;
+
+ r_error_count = 0;
+ error_loop:
+ err = rmtread (archive, ar_block->charptr, (int) (blocksize));
+ if (err < 0)
+ {
+ readerror ();
+ goto error_loop;
+ }
+ if (err == 0)
+ break;
+ ptr = ar_block->charptr;
+ max = err;
+ while (max)
+ {
+ count = (max < RECORDSIZE) ? max : RECORDSIZE;
+ err = write (STDOUT, ptr, count);
+ if (err != count)
+ {
+ if (err < 0)
+ {
+ msg_perror ("can't write to compression program");
+ exit (EX_SYSTEM);
+ }
+ else
+ msg ("write to compression program short %d bytes",
+ count - err);
+ count = (err < 0) ? 0 : err;
+ }
+ ptr += count;
+ max -= count;
+ }
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ int n;
+ char *ptr;
+
+ n = blocksize;
+ ptr = ar_block->charptr;
+ while (n)
+ {
+ err = read (STDIN, ptr, (n < RECORDSIZE) ? n : RECORDSIZE);
+ if (err <= 0)
+ break;
+ n -= err;
+ ptr += err;
+ }
+ /* EOF */
+ if (err == 0)
+ {
+ if (!f_compress_block)
+ blocksize -= n;
+ else
+ bzero (ar_block->charptr + blocksize - n, n);
+ err = rmtwrite (archive, ar_block->charptr, blocksize);
+ if (err != (blocksize))
+ writeerror (err);
+ if (!f_compress_block)
+ blocksize += n;
+ break;
+ }
+ if (n)
+ {
+ msg_perror ("can't read from compression program");
+ exit (EX_SYSTEM);
+ }
+ err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
+ if (err != blocksize)
+ writeerror (err);
+ }
+ }
+
+ /* close_archive(); */
+ exit (0);
+ }
+ }
+ /* So we should exec compress (-d) */
+ if (ar_reading)
+ execlp (f_compressprog, f_compressprog, "-d", (char *) 0);
+ else
+ execlp (f_compressprog, f_compressprog, (char *) 0);
+ msg_perror ("can't exec %s", f_compressprog);
+ _exit (EX_SYSTEM);
+}
+
+
+/* return non-zero if p is the name of a directory */
+int
+isfile (p)
+ char *p;
+{
+ struct stat stbuf;
+
+ if (stat (p, &stbuf) < 0)
+ return 1;
+ if (S_ISREG (stbuf.st_mode))
+ return 1;
+ return 0;
+}
+
+#endif
+
+/*
+ * Open an archive file. The argument specifies whether we are
+ * reading or writing.
+ */
+/* JF if the arg is 2, open for reading and writing. */
+void
+open_archive (reading)
+ int reading;
+{
+ msg_file = f_exstdout ? stderr : stdout;
+
+ if (blocksize == 0)
+ {
+ msg ("invalid value for blocksize");
+ exit (EX_ARGSBAD);
+ }
+
+ if (n_ar_files == 0)
+ {
+ msg ("No archive name given, what should I do?");
+ exit (EX_BADARCH);
+ }
+
+ /*NOSTRICT*/
+ if (f_multivol)
+ {
+ ar_block = (union record *) valloc ((unsigned) (blocksize + (2 * RECORDSIZE)));
+ if (ar_block)
+ ar_block += 2;
+ }
+ else
+ ar_block = (union record *) valloc ((unsigned) blocksize);
+ if (!ar_block)
+ {
+ msg ("could not allocate memory for blocking factor %d",
+ blocking);
+ exit (EX_ARGSBAD);
+ }
+
+ ar_record = ar_block;
+ ar_last = ar_block + blocking;
+ ar_reading = reading;
+
+ if (f_multivol && f_verify)
+ {
+ msg ("cannot verify multi-volume archives");
+ exit (EX_ARGSBAD);
+ }
+
+ if (f_compressprog)
+ {
+ if (reading == 2 || f_verify)
+ {
+ msg ("cannot update or verify compressed archives");
+ exit (EX_ARGSBAD);
+ }
+ if (f_multivol)
+ {
+ msg ("cannot use multi-volume compressed archives");
+ exit (EX_ARGSBAD);
+ }
+ child_open ();
+ if (!reading && ar_files[0][0] == '-' && ar_files[0][1] == '\0')
+ msg_file = stderr;
+ /* child_open(rem_host, rem_file); */
+ }
+ else if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
+ {
+ f_reblock++; /* Could be a pipe, be safe */
+ if (f_verify)
+ {
+ msg ("can't verify stdin/stdout archive");
+ exit (EX_ARGSBAD);
+ }
+ if (reading == 2)
+ {
+ archive = STDIN;
+ msg_file = stderr;
+ write_archive_to_stdout++;
+ }
+ else if (reading)
+ archive = STDIN;
+ else
+ {
+ archive = STDOUT;
+ msg_file = stderr;
+ }
+ }
+ else if (reading == 2 || f_verify)
+ {
+ archive = rmtopen (ar_files[0], O_RDWR | O_CREAT | O_BINARY, 0666);
+ }
+ else if (reading)
+ {
+ archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
+ }
+ else
+ {
+ archive = rmtcreat (ar_files[0], 0666);
+ }
+ if (archive < 0)
+ {
+ msg_perror ("can't open %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+#ifndef __MSDOS__
+ if (!_isrmt (archive))
+ {
+ struct stat tmp_stat;
+
+ fstat (archive, &tmp_stat);
+ if (S_ISREG (tmp_stat.st_mode))
+ {
+ ar_dev = tmp_stat.st_dev;
+ ar_ino = tmp_stat.st_ino;
+ }
+ }
+#endif
+
+#ifdef __MSDOS__
+ setmode (archive, O_BINARY);
+#endif
+
+ if (reading)
+ {
+ ar_last = ar_block; /* Set up for 1st block = # 0 */
+ (void) findrec (); /* Read it in, check for EOF */
+
+ if (f_volhdr)
+ {
+ union record *head;
+#if 0
+ char *ptr;
+
+ if (f_multivol)
+ {
+ ptr = malloc (strlen (f_volhdr) + 20);
+ sprintf (ptr, "%s Volume %d", f_volhdr, 1);
+ }
+ else
+ ptr = f_volhdr;
+#endif
+ head = findrec ();
+ if (!head)
+ {
+ msg ("Archive not labelled to match %s", f_volhdr);
+ exit (EX_BADVOL);
+ }
+ if (re_match (label_pattern, head->header.arch_name,
+ strlen (head->header.arch_name), 0, 0) < 0)
+ {
+ msg ("Volume mismatch! %s!=%s", f_volhdr,
+ head->header.arch_name);
+ exit (EX_BADVOL);
+ }
+#if 0
+ if (strcmp (ptr, head->header.name))
+ {
+ msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
+ exit (EX_BADVOL);
+ }
+ if (ptr != f_volhdr)
+ free (ptr);
+#endif
+ }
+ }
+ else if (f_volhdr)
+ {
+ bzero ((void *) ar_block, RECORDSIZE);
+ if (f_multivol)
+ sprintf (ar_block->header.arch_name, "%s Volume 1", f_volhdr);
+ else
+ strcpy (ar_block->header.arch_name, f_volhdr);
+ current_file_name = ar_block->header.arch_name;
+ ar_block->header.linkflag = LF_VOLHDR;
+ to_oct (time (0), 1 + 12, ar_block->header.mtime);
+ finish_header (ar_block);
+ /* ar_record++; */
+ }
+}
+
+
+/*
+ * Remember a union record * as pointing to something that we
+ * need to keep when reading onward in the file. Only one such
+ * thing can be remembered at once, and it only works when reading
+ * an archive.
+ *
+ * We calculate "offset" then add it because some compilers end up
+ * adding (baserec+ar_record), doing a 9-bit shift of baserec, then
+ * subtracting ar_block from that, shifting it back, losing the top 9 bits.
+ */
+void
+saverec (pointer)
+ union record **pointer;
+{
+ long offset;
+
+ save_rec = pointer;
+ offset = ar_record - ar_block;
+ saved_recno = baserec + offset;
+}
+
+/*
+ * Perform a write to flush the buffer.
+ */
+
+/*send_buffer_to_file();
+ if(new_volume) {
+ deal_with_new_volume_stuff();
+ send_buffer_to_file();
+ }
+ */
+
+void
+fl_write ()
+{
+ int err;
+ int copy_back;
+ static long bytes_written = 0;
+
+ if (f_checkpoint && !(++checkpoint % 10))
+ msg ("Write checkpoint %d\n", checkpoint);
+ if (tape_length && bytes_written >= tape_length * 1024)
+ {
+ errno = ENOSPC;
+ err = 0;
+ }
+ else
+ err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
+ if (err != blocksize && !f_multivol)
+ writeerror (err);
+ else if (f_totals)
+ tot_written += blocksize;
+
+ if (err > 0)
+ bytes_written += err;
+ if (err == blocksize)
+ {
+ if (f_multivol)
+ {
+ if (!save_name)
+ {
+ real_s_name[0] = '\0';
+ real_s_totsize = 0;
+ real_s_sizeleft = 0;
+ return;
+ }
+#ifdef __MSDOS__
+ if (save_name[1] == ':')
+ save_name += 2;
+#endif
+ while (*save_name == '/')
+ save_name++;
+
+ strcpy (real_s_name, save_name);
+ real_s_totsize = save_totsize;
+ real_s_sizeleft = save_sizeleft;
+ }
+ return;
+ }
+
+ /* We're multivol Panic if we didn't get the right kind of response */
+ /* ENXIO is for the UNIX PC */
+ if (err < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
+ writeerror (err);
+
+ /* If error indicates a short write, we just move to the next tape. */
+
+ if (new_volume (0) < 0)
+ return;
+ bytes_written = 0;
+ if (f_volhdr && real_s_name[0])
+ {
+ copy_back = 2;
+ ar_block -= 2;
+ }
+ else if (f_volhdr || real_s_name[0])
+ {
+ copy_back = 1;
+ ar_block--;
+ }
+ else
+ copy_back = 0;
+ if (f_volhdr)
+ {
+ bzero ((void *) ar_block, RECORDSIZE);
+ sprintf (ar_block->header.arch_name, "%s Volume %d", f_volhdr, volno);
+ to_oct (time (0), 1 + 12, ar_block->header.mtime);
+ ar_block->header.linkflag = LF_VOLHDR;
+ finish_header (ar_block);
+ }
+ if (real_s_name[0])
+ {
+ int tmp;
+
+ if (f_volhdr)
+ ar_block++;
+ bzero ((void *) ar_block, RECORDSIZE);
+ strcpy (ar_block->header.arch_name, real_s_name);
+ ar_block->header.linkflag = LF_MULTIVOL;
+ to_oct ((long) real_s_sizeleft, 1 + 12,
+ ar_block->header.size);
+ to_oct ((long) real_s_totsize - real_s_sizeleft,
+ 1 + 12, ar_block->header.offset);
+ tmp = f_verbose;
+ f_verbose = 0;
+ finish_header (ar_block);
+ f_verbose = tmp;
+ if (f_volhdr)
+ ar_block--;
+ }
+
+ err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
+ if (err != blocksize)
+ writeerror (err);
+ else if (f_totals)
+ tot_written += blocksize;
+
+
+ bytes_written = blocksize;
+ if (copy_back)
+ {
+ ar_block += copy_back;
+ bcopy ((void *) (ar_block + blocking - copy_back),
+ (void *) ar_record,
+ copy_back * RECORDSIZE);
+ ar_record += copy_back;
+
+ if (real_s_sizeleft >= copy_back * RECORDSIZE)
+ real_s_sizeleft -= copy_back * RECORDSIZE;
+ else if ((real_s_sizeleft + RECORDSIZE - 1) / RECORDSIZE <= copy_back)
+ real_s_name[0] = '\0';
+ else
+ {
+#ifdef __MSDOS__
+ if (save_name[1] == ':')
+ save_name += 2;
+#endif
+ while (*save_name == '/')
+ save_name++;
+
+ strcpy (real_s_name, save_name);
+ real_s_sizeleft = save_sizeleft;
+ real_s_totsize = save_totsize;
+ }
+ copy_back = 0;
+ }
+}
+
+/* Handle write errors on the archive. Write errors are always fatal */
+/* Hitting the end of a volume does not cause a write error unless the write
+* was the first block of the volume */
+
+void
+writeerror (err)
+ int err;
+{
+ if (err < 0)
+ {
+ msg_perror ("can't write to %s", ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+ else
+ {
+ msg ("only wrote %u of %u bytes to %s", err, blocksize, ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+}
+
+/*
+ * Handle read errors on the archive.
+ *
+ * If the read should be retried, readerror() returns to the caller.
+ */
+void
+readerror ()
+{
+# define READ_ERROR_MAX 10
+
+ read_error_flag++; /* Tell callers */
+
+ msg_perror ("read error on %s", ar_files[cur_ar_file]);
+
+ if (baserec == 0)
+ {
+ /* First block of tape. Probably stupidity error */
+ exit (EX_BADARCH);
+ }
+
+ /*
+ * Read error in mid archive. We retry up to READ_ERROR_MAX times
+ * and then give up on reading the archive. We set read_error_flag
+ * for our callers, so they can cope if they want.
+ */
+ if (r_error_count++ > READ_ERROR_MAX)
+ {
+ msg ("Too many errors, quitting.");
+ exit (EX_BADARCH);
+ }
+ return;
+}
+
+
+/*
+ * Perform a read to flush the buffer.
+ */
+void
+fl_read ()
+{
+ int err; /* Result from system call */
+ int left; /* Bytes left */
+ char *more; /* Pointer to next byte to read */
+
+ if (f_checkpoint && !(++checkpoint % 10))
+ msg ("Read checkpoint %d\n", checkpoint);
+
+ /*
+ * Clear the count of errors. This only applies to a single
+ * call to fl_read. We leave read_error_flag alone; it is
+ * only turned off by higher level software.
+ */
+ r_error_count = 0; /* Clear error count */
+
+ /*
+ * If we are about to wipe out a record that
+ * somebody needs to keep, copy it out to a holding
+ * area and adjust somebody's pointer to it.
+ */
+ if (save_rec &&
+ *save_rec >= ar_record &&
+ *save_rec < ar_last)
+ {
+ record_save_area = **save_rec;
+ *save_rec = &record_save_area;
+ }
+ if (write_archive_to_stdout && baserec != 0)
+ {
+ err = rmtwrite (1, ar_block->charptr, blocksize);
+ if (err != blocksize)
+ writeerror (err);
+ }
+ if (f_multivol)
+ {
+ if (save_name)
+ {
+ if (save_name != real_s_name)
+ {
+#ifdef __MSDOS__
+ if (save_name[1] == ':')
+ save_name += 2;
+#endif
+ while (*save_name == '/')
+ save_name++;
+
+ strcpy (real_s_name, save_name);
+ save_name = real_s_name;
+ }
+ real_s_totsize = save_totsize;
+ real_s_sizeleft = save_sizeleft;
+
+ }
+ else
+ {
+ real_s_name[0] = '\0';
+ real_s_totsize = 0;
+ real_s_sizeleft = 0;
+ }
+ }
+
+error_loop:
+ err = rmtread (archive, ar_block->charptr, (int) blocksize);
+ if (err == blocksize)
+ return;
+
+ if ((err == 0 || (err < 0 && errno == ENOSPC) || (err > 0 && !f_reblock)) && f_multivol)
+ {
+ union record *head;
+
+ try_volume:
+ if (new_volume ((cmd_mode == CMD_APPEND || cmd_mode == CMD_CAT || cmd_mode == CMD_UPDATE) ? 2 : 1) < 0)
+ return;
+ vol_error:
+ err = rmtread (archive, ar_block->charptr, (int) blocksize);
+ if (err < 0)
+ {
+ readerror ();
+ goto vol_error;
+ }
+ if (err != blocksize)
+ goto short_read;
+
+ head = ar_block;
+
+ if (head->header.linkflag == LF_VOLHDR)
+ {
+ if (f_volhdr)
+ {
+#if 0
+ char *ptr;
+
+ ptr = (char *) malloc (strlen (f_volhdr) + 20);
+ sprintf (ptr, "%s Volume %d", f_volhdr, volno);
+#endif
+ if (re_match (label_pattern, head->header.arch_name,
+ strlen (head->header.arch_name),
+ 0, 0) < 0)
+ {
+ msg ("Volume mismatch! %s!=%s", f_volhdr,
+ head->header.arch_name);
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+
+#if 0
+ if (strcmp (ptr, head->header.name))
+ {
+ msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
+ --volno;
+ --global_volno;
+ free (ptr);
+ goto try_volume;
+ }
+ free (ptr);
+#endif
+ }
+ if (f_verbose)
+ fprintf (msg_file, "Reading %s\n", head->header.arch_name);
+ head++;
+ }
+ else if (f_volhdr)
+ {
+ msg ("Warning: No volume header!");
+ }
+
+ if (real_s_name[0])
+ {
+ long from_oct ();
+
+ if (head->header.linkflag != LF_MULTIVOL || strcmp (head->header.arch_name, real_s_name))
+ {
+ msg ("%s is not continued on this volume!", real_s_name);
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+ if (real_s_totsize != from_oct (1 + 12, head->header.size) + from_oct (1 + 12, head->header.offset))
+ {
+ msg ("%s is the wrong size (%ld!=%ld+%ld)",
+ head->header.arch_name, save_totsize,
+ from_oct (1 + 12, head->header.size),
+ from_oct (1 + 12, head->header.offset));
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+ if (real_s_totsize - real_s_sizeleft != from_oct (1 + 12, head->header.offset))
+ {
+ msg ("This volume is out of sequence");
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+ head++;
+ }
+ ar_record = head;
+ return;
+ }
+ else if (err < 0)
+ {
+ readerror ();
+ goto error_loop; /* Try again */
+ }
+
+short_read:
+ more = ar_block->charptr + err;
+ left = blocksize - err;
+
+again:
+ if (0 == (((unsigned) left) % RECORDSIZE))
+ {
+ /* FIXME, for size=0, multi vol support */
+ /* On the first block, warn about the problem */
+ if (!f_reblock && baserec == 0 && f_verbose && err > 0)
+ {
+ /* msg("Blocksize = %d record%s",
+ err / RECORDSIZE, (err > RECORDSIZE)? "s": "");*/
+ msg ("Blocksize = %d records", err / RECORDSIZE);
+ }
+ ar_last = ar_block + ((unsigned) (blocksize - left)) / RECORDSIZE;
+ return;
+ }
+ if (f_reblock)
+ {
+ /*
+ * User warned us about this. Fix up.
+ */
+ if (left > 0)
+ {
+ error2loop:
+ err = rmtread (archive, more, (int) left);
+ if (err < 0)
+ {
+ readerror ();
+ goto error2loop; /* Try again */
+ }
+ if (err == 0)
+ {
+ msg ("archive %s EOF not on block boundary", ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+ left -= err;
+ more += err;
+ goto again;
+ }
+ }
+ else
+ {
+ msg ("only read %d bytes from archive %s", err, ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+}
+
+
+/*
+ * Flush the current buffer to/from the archive.
+ */
+void
+flush_archive ()
+{
+ int c;
+
+ baserec += ar_last - ar_block;/* Keep track of block #s */
+ ar_record = ar_block; /* Restore pointer to start */
+ ar_last = ar_block + blocking;/* Restore pointer to end */
+
+ if (ar_reading)
+ {
+ if (time_to_start_writing)
+ {
+ time_to_start_writing = 0;
+ ar_reading = 0;
+
+ if (file_to_switch_to >= 0)
+ {
+ if ((c = rmtclose (archive)) < 0)
+ msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
+
+ archive = file_to_switch_to;
+ }
+ else
+ (void) backspace_output ();
+ fl_write ();
+ }
+ else
+ fl_read ();
+ }
+ else
+ {
+ fl_write ();
+ }
+}
+
+/* Backspace the archive descriptor by one blocks worth.
+ If its a tape, MTIOCTOP will work. If its something else,
+ we try to seek on it. If we can't seek, we lose! */
+int
+backspace_output ()
+{
+ long cur;
+ /* int er; */
+ extern char *output_start;
+
+#ifdef MTIOCTOP
+ struct mtop t;
+
+ t.mt_op = MTBSR;
+ t.mt_count = 1;
+ if ((rmtioctl (archive, MTIOCTOP, &t)) >= 0)
+ return 1;
+ if (errno == EIO && (rmtioctl (archive, MTIOCTOP, &t)) >= 0)
+ return 1;
+#endif
+
+ cur = rmtlseek (archive, 0L, 1);
+ cur -= blocksize;
+ /* Seek back to the beginning of this block and
+ start writing there. */
+
+ if (rmtlseek (archive, cur, 0) != cur)
+ {
+ /* Lseek failed. Try a different method */
+ msg ("Couldn't backspace archive file. It may be unreadable without -i.");
+ /* Replace the first part of the block with nulls */
+ if (ar_block->charptr != output_start)
+ bzero (ar_block->charptr, output_start - ar_block->charptr);
+ return 2;
+ }
+ return 3;
+}
+
+
+/*
+ * Close the archive file.
+ */
+void
+close_archive ()
+{
+ int child;
+ int status;
+ int c;
+
+ if (time_to_start_writing || !ar_reading)
+ flush_archive ();
+ if (cmd_mode == CMD_DELETE)
+ {
+ off_t pos;
+
+ pos = rmtlseek (archive, 0L, 1);
+#ifndef __MSDOS__
+ (void) ftruncate (archive, pos);
+#else
+ (void) rmtwrite (archive, "", 0);
+#endif
+ }
+ if (f_verify)
+ verify_volume ();
+
+ if ((c = rmtclose (archive)) < 0)
+ msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
+
+#ifndef __MSDOS__
+ if (childpid)
+ {
+ /*
+ * Loop waiting for the right child to die, or for
+ * no more kids.
+ */
+ while (((child = wait (&status)) != childpid) && child != -1)
+ ;
+
+ if (child != -1)
+ {
+ if (WIFSIGNALED (status))
+ {
+ /* SIGPIPE is OK, everything else is a problem. */
+ if (WTERMSIG (status) != SIGPIPE)
+ msg ("child died with signal %d%s", WTERMSIG (status),
+ WIFCOREDUMPED (status) ? " (core dumped)" : "");
+ }
+ else
+ {
+ /* Child voluntarily terminated -- but why? */
+ if (WEXITSTATUS (status) == MAGIC_STAT)
+ {
+ exit (EX_SYSTEM); /* Child had trouble */
+ }
+ if (WEXITSTATUS (status) == (SIGPIPE + 128))
+ {
+ /*
+ * /bin/sh returns this if its child
+ * dies with SIGPIPE. 'Sok.
+ */
+ /* Do nothing. */
+ }
+ else if (WEXITSTATUS (status))
+ msg ("child returned status %d",
+ WEXITSTATUS (status));
+ }
+ }
+ }
+#endif /* __MSDOS__ */
+}
+
+
+#ifdef DONTDEF
+/*
+ * Message management.
+ *
+ * anno writes a message prefix on stream (eg stdout, stderr).
+ *
+ * The specified prefix is normally output followed by a colon and a space.
+ * However, if other command line options are set, more output can come
+ * out, such as the record # within the archive.
+ *
+ * If the specified prefix is NULL, no output is produced unless the
+ * command line option(s) are set.
+ *
+ * If the third argument is 1, the "saved" record # is used; if 0, the
+ * "current" record # is used.
+ */
+void
+anno (stream, prefix, savedp)
+ FILE *stream;
+ char *prefix;
+ int savedp;
+{
+# define MAXANNO 50
+ char buffer[MAXANNO]; /* Holds annorecment */
+# define ANNOWIDTH 13
+ int space;
+ long offset;
+ int save_e;
+
+ save_e = errno;
+ /* Make sure previous output gets out in sequence */
+ if (stream == stderr)
+ fflush (stdout);
+ if (f_sayblock)
+ {
+ if (prefix)
+ {
+ fputs (prefix, stream);
+ putc (' ', stream);
+ }
+ offset = ar_record - ar_block;
+ (void) sprintf (buffer, "rec %d: ",
+ savedp ? saved_recno :
+ baserec + offset);
+ fputs (buffer, stream);
+ space = ANNOWIDTH - strlen (buffer);
+ if (space > 0)
+ {
+ fprintf (stream, "%*s", space, "");
+ }
+ }
+ else if (prefix)
+ {
+ fputs (prefix, stream);
+ fputs (": ", stream);
+ }
+ errno = save_e;
+}
+
+#endif
+
+/* Called to initialize the global volume number. */
+void
+init_volume_number ()
+{
+ FILE *vf;
+
+ vf = fopen (f_volno_file, "r");
+ if (!vf && errno != ENOENT)
+ msg_perror ("%s", f_volno_file);
+
+ if (vf)
+ {
+ fscanf (vf, "%d", &global_volno);
+ fclose (vf);
+ }
+}
+
+/* Called to write out the closing global volume number. */
+void
+closeout_volume_number ()
+{
+ FILE *vf;
+
+ vf = fopen (f_volno_file, "w");
+ if (!vf)
+ msg_perror ("%s", f_volno_file);
+ else
+ {
+ fprintf (vf, "%d\n", global_volno);
+ fclose (vf);
+ }
+}
+
+/* We've hit the end of the old volume. Close it and open the next one */
+/* Values for type: 0: writing 1: reading 2: updating */
+int
+new_volume (type)
+ int type;
+{
+ int c;
+ char inbuf[80];
+ char *p;
+ static FILE *read_file = 0;
+ extern int now_verifying;
+ extern char TTY_NAME[];
+ static int looped = 0;
+
+ if (!read_file && !f_run_script_at_end)
+ read_file = (archive == 0) ? fopen (TTY_NAME, "r") : stdin;
+
+ if (now_verifying)
+ return -1;
+ if (f_verify)
+ verify_volume ();
+ if ((c = rmtclose (archive)) < 0)
+ msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
+
+ global_volno++;
+ volno++;
+ cur_ar_file++;
+ if (cur_ar_file == n_ar_files)
+ {
+ cur_ar_file = 0;
+ looped = 1;
+ }
+
+tryagain:
+ if (looped)
+ {
+ /* We have to prompt from now on. */
+ if (f_run_script_at_end)
+ {
+ closeout_volume_number ();
+ system (info_script);
+ }
+ else
+ for (;;)
+ {
+ fprintf (msg_file, "\007Prepare volume #%d for %s and hit return: ", global_volno, ar_files[cur_ar_file]);
+ fflush (msg_file);
+ if (fgets (inbuf, sizeof (inbuf), read_file) == 0)
+ {
+ fprintf (msg_file, "EOF? What does that mean?");
+ if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
+ msg ("Warning: Archive is INCOMPLETE!");
+ exit (EX_BADARCH);
+ }
+ if (inbuf[0] == '\n' || inbuf[0] == 'y' || inbuf[0] == 'Y')
+ break;
+
+ switch (inbuf[0])
+ {
+ case '?':
+ {
+ fprintf (msg_file, "\
+ n [name] Give a new filename for the next (and subsequent) volume(s)\n\
+ q Abort tar\n\
+ ! Spawn a subshell\n\
+ ? Print this list\n");
+ }
+ break;
+
+ case 'q': /* Quit */
+ fprintf (msg_file, "No new volume; exiting.\n");
+ if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
+ msg ("Warning: Archive is INCOMPLETE!");
+ exit (EX_BADARCH);
+
+ case 'n': /* Get new file name */
+ {
+ char *q, *r;
+ static char *old_name;
+
+ for (q = &inbuf[1]; *q == ' ' || *q == '\t'; q++)
+ ;
+ for (r = q; *r; r++)
+ if (*r == '\n')
+ *r = '\0';
+ old_name = p = (char *) malloc ((unsigned) (strlen (q) + 2));
+ if (p == 0)
+ {
+ msg ("Can't allocate memory for name");
+ exit (EX_SYSTEM);
+ }
+ (void) strcpy (p, q);
+ ar_files[cur_ar_file] = p;
+ }
+ break;
+
+ case '!':
+#ifdef __MSDOS__
+ spawnl (P_WAIT, getenv ("COMSPEC"), "-", 0);
+#else
+ /* JF this needs work! */
+ switch (fork ())
+ {
+ case -1:
+ msg_perror ("can't fork!");
+ break;
+ case 0:
+ p = getenv ("SHELL");
+ if (p == 0)
+ p = "/bin/sh";
+ execlp (p, "-sh", "-i", 0);
+ msg_perror ("can't exec a shell %s", p);
+ _exit (55);
+ default:
+ wait (0);
+ break;
+ }
+#endif
+ break;
+ }
+ }
+ }
+
+
+ if (type == 2 || f_verify)
+ archive = rmtopen (ar_files[cur_ar_file], O_RDWR | O_CREAT, 0666);
+ else if (type == 1)
+ archive = rmtopen (ar_files[cur_ar_file], O_RDONLY, 0666);
+ else if (type == 0)
+ archive = rmtcreat (ar_files[cur_ar_file], 0666);
+ else
+ archive = -1;
+
+ if (archive < 0)
+ {
+ msg_perror ("can't open %s", ar_files[cur_ar_file]);
+ goto tryagain;
+ }
+#ifdef __MSDOS__
+ setmode (archive, O_BINARY);
+#endif
+ return 0;
+}
+
+/* this is a useless function that takes a buffer returned by wantbytes
+ and does nothing with it. If the function called by wantbytes returns
+ an error indicator (non-zero), this function is called for the rest of
+ the file.
+ */
+int
+no_op (size, data)
+ int size;
+ char *data;
+{
+ return 0;
+}
+
+/* Some other routine wants SIZE bytes in the archive. For each chunk of
+ the archive, call FUNC with the size of the chunk, and the address of
+ the chunk it can work with.
+ */
+int
+wantbytes (size, func)
+ long size;
+ int (*func) ();
+{
+ char *data;
+ long data_size;
+
+ while (size)
+ {
+ data = findrec ()->charptr;
+ if (data == NULL)
+ { /* Check it... */
+ msg ("Unexpected EOF on archive file");
+ return -1;
+ }
+ data_size = endofrecs ()->charptr - data;
+ if (data_size > size)
+ data_size = size;
+ if ((*func) (data_size, data))
+ func = no_op;
+ userec ((union record *) (data + data_size - 1));
+ size -= data_size;
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/tar/create.c b/gnu/usr.bin/tar/create.c
new file mode 100644
index 000000000000..62b9c51178f4
--- /dev/null
+++ b/gnu/usr.bin/tar/create.c
@@ -0,0 +1,1454 @@
+/* Create a tar archive.
+ Copyright (C) 1985, 1992, 1993 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 2, 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.
+ */
+
+#ifdef _AIX
+ #pragma alloca
+#endif
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#include "tar.h"
+#include "port.h"
+
+#ifndef __MSDOS__
+#include <pwd.h>
+#include <grp.h>
+#endif
+
+#if defined (_POSIX_VERSION)
+#include <utime.h>
+#else
+struct utimbuf
+{
+ long actime;
+ long modtime;
+};
+
+#endif
+
+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_ISLNK
+#define lstat stat
+#endif
+
+extern void print_header ();
+
+union record *start_header ();
+void blank_name_list ();
+int check_exclude ();
+PTR ck_malloc ();
+PTR ck_realloc ();
+void clear_buffer ();
+void close_archive ();
+void collect_and_sort_names ();
+int confirm ();
+int deal_with_sparse ();
+void find_new_file_size ();
+void finish_header ();
+int finish_sparse_file ();
+void finduname ();
+void findgname ();
+int is_dot_or_dotdot ();
+void open_archive ();
+char *name_next ();
+void name_close ();
+void to_oct ();
+void dump_file ();
+void write_dir_file ();
+void write_eot ();
+void write_long ();
+int zero_record ();
+
+/* 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 = ck_malloc (PATH_MAX);
+ char *q, *bufp;
+
+ collect_and_sort_names ();
+
+ while (p = name_from_list ())
+ dump_file (p, -1, 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, 1);
+ }
+ }
+ }
+ /* } */
+ free (buf);
+ }
+ else
+ {
+ while (p = name_next (1))
+ dump_file (p, -1, 1);
+ }
+
+ 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, toplevel)
+ char *p; /* File name to dump */
+ int curdev; /* Device our parent dir was on */
+ int toplevel; /* Whether we are a toplevel call */
+{
+ 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 critical_error = 0;
+ struct utimbuf restore_times;
+ /* 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 STX_HIDDEN /* 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
+ {
+ badperror:
+ msg_perror ("can't add file %s", p);
+ badfile:
+ if (!f_ignore_failed_read || critical_error)
+ errors++;
+ return;
+ }
+
+ restore_times.actime = hstat.st_atime;
+ restore_times.modtime = hstat.st_mtime;
+
+#ifdef S_ISHIDDEN
+ if (S_ISHIDDEN (hstat.st_mode))
+ {
+ char *new = (char *) alloca (strlen (p) + 2);
+ if (new)
+ {
+ strcpy (new, p);
+ strcat (new, "@");
+ p = new;
+ }
+ }
+#endif
+
+ /* 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
+ && !S_ISDIR (hstat.st_mode)
+ && (f_new_files > 1 || new_time > hstat.st_ctime))
+ {
+ if (curdev == -1)
+ {
+ 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
+ && (S_ISREG (hstat.st_mode)
+#ifdef S_ISCTG
+ || S_ISCTG (hstat.st_mode)
+#endif
+#ifdef S_ISCHR
+ || S_ISCHR (hstat.st_mode)
+#endif
+#ifdef S_ISBLK
+ || S_ISBLK (hstat.st_mode)
+#endif
+#ifdef S_ISFIFO
+ || S_ISFIFO (hstat.st_mode)
+#endif
+ ))
+ {
+ register struct link *lp;
+
+ /* 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. */
+ while (!f_absolute_paths && *link_name == '/')
+ {
+ static int link_warn = 0;
+
+ if (!link_warn)
+ {
+ msg ("Removing leading / from absolute links");
+ link_warn++;
+ }
+ link_name++;
+ }
+ if (link_name - lp->name >= NAMSIZ)
+ write_long (link_name, LF_LONGLINK);
+ current_link_name = link_name;
+
+ hstat.st_size = 0;
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile;
+ }
+ strncpy (header->header.arch_linkname,
+ link_name, NAMSIZ);
+
+ /* Force null truncated */
+ header->header.arch_linkname[NAMSIZ - 1] = 0;
+
+ header->header.linkflag = LF_LINK;
+ finish_header (header);
+ /* FIXME: Maybe remove from list after all links found? */
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ return; /* We dumped it */
+ }
+ }
+
+ /* Not found. Add it to the list of possible links. */
+ lp = (struct link *) ck_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.
+ */
+ if (S_ISREG (hstat.st_mode)
+#ifdef S_ISCTG
+ || S_ISCTG (hstat.st_mode)
+#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)
+ {
+ critical_error = 1;
+ 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 <file>.
+ * 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);
+ critical_error = 1;
+ goto badfile;
+ }
+ }
+#ifdef S_ISCTG
+ /* Mark contiguous files, if we support them */
+ if (f_standard && S_ISCTG (hstat.st_mode))
+ {
+ 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)
+ {
+ critical_error = 1;
+ 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);
+
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+
+ /*
+ * 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);
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+ }
+
+#ifdef S_ISLNK
+ else if (S_ISLNK (hstat.st_mode))
+ {
+ int size;
+ char *buf = alloca (PATH_MAX + 1);
+
+ size = readlink (p, buf, PATH_MAX + 1);
+ if (size < 0)
+ goto badperror;
+ buf[size] = '\0';
+ if (size >= NAMSIZ)
+ write_long (buf, LF_LONGLINK);
+ current_link_name = buf;
+
+ hstat.st_size = 0; /* Force 0 size on symlink */
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile;
+ }
+ strncpy (header->header.arch_linkname, buf, NAMSIZ);
+ header->header.arch_linkname[NAMSIZ - 1] = '\0';
+ header->header.linkflag = LF_SYMLINK;
+ finish_header (header); /* Nothing more to do to it */
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ return;
+ }
+#endif
+
+ else if (S_ISDIR (hstat.st_mode))
+ {
+ register DIR *dirp;
+ register struct dirent *d;
+ char *namebuf;
+ int buflen;
+ register int len;
+ int our_device = hstat.st_dev;
+
+ /* 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)
+ {
+ critical_error = 1;
+ 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 (sizeleft < bufsize)
+ {
+ bufsize = sizeleft;
+ count = bufsize % RECORDSIZE;
+ if (count)
+ bzero (start->charptr + 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;
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+ }
+
+ /* Now output all the files in the directory */
+#if 0
+ if (f_dironly)
+ return; /* Unless the cmdline said not to */
+#endif
+ /*
+ * 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 && !toplevel && curdev != hstat.st_dev)
+ {
+ if (f_verbose)
+ msg ("%s: is on a different filesystem; not dumped", p);
+ return;
+ }
+
+
+ errno = 0;
+ dirp = opendir (p);
+ if (!dirp)
+ {
+ if (errno)
+ {
+ msg_perror ("can't open directory %s", p);
+ }
+ else
+ {
+ msg ("error opening directory %s",
+ p);
+ }
+ return;
+ }
+
+ /* 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 (NLENGTH (d) + len >= buflen)
+ {
+ buflen = len + NLENGTH (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, 0);
+ }
+
+ closedir (dirp);
+ free (namebuf);
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+ }
+
+#ifdef S_ISCHR
+ else if (S_ISCHR (hstat.st_mode))
+ {
+ type = LF_CHR;
+ }
+#endif
+
+#ifdef S_ISBLK
+ else if (S_ISBLK (hstat.st_mode))
+ {
+ type = LF_BLK;
+ }
+#endif
+
+ /* Avoid screwy apollo lossage where S_IFIFO == S_IFSOCK */
+#if (_ISP__M68K == 0) && (_ISP__A88K == 0) && defined(S_ISFIFO)
+ else if (S_ISFIFO (hstat.st_mode))
+ {
+ type = LF_FIFO;
+ }
+#endif
+
+#ifdef S_ISSOCK
+ else if (S_ISSOCK (hstat.st_mode))
+ {
+ type = LF_FIFO;
+ }
+#endif
+ else
+ goto unknown;
+
+ if (!f_standard)
+ goto unknown;
+
+ hstat.st_size = 0; /* Force 0 size */
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile; /* eg name too long */
+ }
+
+ header->header.linkflag = type;
+#if defined(S_IFBLK) || defined(S_IFCHR)
+ 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);
+ }
+#endif
+
+ finish_header (header);
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ return;
+
+unknown:
+ msg ("%s: Unknown file type; file ignored.", p);
+}
+
+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;
+
+}
+
+void
+init_sparsearray ()
+{
+ register int i;
+
+ sp_array_size = 10;
+ /*
+ * Make room for our scratch space -- initially is 10 elts long
+ */
+ sparsearray = (struct sp_array *) ck_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;
+ int nulls_at_end;
+{
+ 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];
+#if 0
+ int read_last_data = 0; /* did we just read the last record? */
+#endif
+ 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 *)
+ ck_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;
+ else
+ {
+ sparsearray[sparse_ind].offset = offset-1;
+ sparsearray[sparse_ind++].numbytes = 1;
+ }
+ close (fd);
+
+ return sparse_ind - 1;
+}
+
+/*
+ * Just zeroes out the buffer so we don't confuse ourselves with leftover
+ * data.
+ */
+void
+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 -mib */
+/*
+ * 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.
+ */
+int
+zero_record (buffer)
+ char *buffer;
+{
+ register int i;
+
+ for (i = 0; i < RECORDSIZE; i++)
+ if (buffer[i] != '\000')
+ return 0;
+ return 1;
+}
+
+void
+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;
+
+ if (strlen (name) >= NAMSIZ)
+ write_long (name, LF_LONGNAME);
+
+ 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.");
+ }
+ }
+ current_file_name = name;
+ strncpy (header->header.arch_name, name, NAMSIZ);
+ header->header.arch_name[NAMSIZ - 1] = '\0';
+
+ to_oct ((long) (f_oldarch ? (st->st_mode & 07777) : st->st_mode),
+ 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;
+
+ 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.
+ */
+void
+write_eot ()
+{
+ union record *p;
+ int bufsize;
+
+ p = findrec ();
+ if (p)
+ {
+ bufsize = endofrecs ()->charptr - p->charptr;
+ bzero (p->charptr, bufsize);
+ userec (p);
+ }
+}
+
+/* Write a LF_LONGLINK or LF_LONGNAME record. */
+void
+write_long (p, type)
+ char *p;
+ char type;
+{
+ int size = strlen (p) + 1;
+ int bufsize;
+ union record *header;
+ struct stat foo;
+
+
+ bzero (&foo, sizeof foo);
+ foo.st_size = size;
+
+ header = start_header ("././@LongLink", &foo);
+ header->header.linkflag = type;
+ finish_header (header);
+
+ header = findrec ();
+
+ bufsize = endofrecs ()->charptr - header->charptr;
+
+ while (bufsize < size)
+ {
+ bcopy (p, header->charptr, bufsize);
+ p += bufsize;
+ size -= bufsize;
+ userec (header + (bufsize - 1) / RECORDSIZE);
+ header = findrec ();
+ bufsize = endofrecs ()->charptr - header->charptr;
+ }
+ bcopy (p, header->charptr, size);
+ bzero (header->charptr + size, bufsize - size);
+ userec (header + (size - 1) / RECORDSIZE);
+}
diff --git a/gnu/usr.bin/tar/diffarch.c b/gnu/usr.bin/tar/diffarch.c
new file mode 100644
index 000000000000..ce47d9d6cab5
--- /dev/null
+++ b/gnu/usr.bin/tar/diffarch.c
@@ -0,0 +1,759 @@
+/* Diff files from a tar archive.
+ Copyright (C) 1988, 1992, 1993 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 2, 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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <sys/types.h>
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#include "tar.h"
+#include "port.h"
+#include "rmt.h"
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+extern void *valloc ();
+
+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 */
+
+void decode_header ();
+void diff_sparse_files ();
+void fill_in_sparse_array ();
+void fl_read ();
+long from_oct ();
+int do_stat ();
+extern void print_header ();
+int read_header ();
+void saverec ();
+void sigh ();
+extern void skip_file ();
+extern void skip_extended_headers ();
+int wantbytes ();
+
+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
+ */
+void
+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 ();
+ int no_op ();
+#ifndef __MSDOS__
+ dev_t dev;
+ ino_t ino;
+#endif
+ char *get_dir_contents ();
+ long from_oct ();
+
+ 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, current_file_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 (current_file_name) - 1;
+ if (current_file_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 (!S_ISREG (filestat.st_mode))
+ {
+ fprintf (msg_file, "%s: not a regular file\n",
+ current_file_name);
+ skip_file ((long) hstat.st_size);
+ different++;
+ goto quit;
+ }
+
+ filestat.st_mode &= 07777;
+ 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 (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
+
+ if (diff_fd < 0 && !f_absolute_paths)
+ {
+ char tmpbuf[NAMSIZ + 2];
+
+ tmpbuf[0] = '/';
+ strcpy (&tmpbuf[1], current_file_name);
+ diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
+ }
+ if (diff_fd < 0)
+ {
+ msg_perror ("cannot open %s", current_file_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", current_file_name);
+
+ quit:
+ break;
+
+#ifndef __MSDOS__
+ case LF_LINK:
+ if (do_stat (&filestat))
+ break;
+ dev = filestat.st_dev;
+ ino = filestat.st_ino;
+ err = stat (current_link_name, &filestat);
+ if (err < 0)
+ {
+ if (errno == ENOENT)
+ {
+ fprintf (msg_file, "%s: does not exist\n", current_file_name);
+ }
+ else
+ {
+ msg_perror ("cannot stat file %s", current_file_name);
+ }
+ different++;
+ break;
+ }
+ if (filestat.st_dev != dev || filestat.st_ino != ino)
+ {
+ fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
+ break;
+ }
+ break;
+#endif
+
+#ifdef S_ISLNK
+ case LF_SYMLINK:
+ {
+ char linkbuf[NAMSIZ + 3];
+ check = readlink (current_file_name, linkbuf,
+ (sizeof linkbuf) - 1);
+
+ if (check < 0)
+ {
+ if (errno == ENOENT)
+ {
+ fprintf (msg_file,
+ "%s: no such file or directory\n",
+ current_file_name);
+ }
+ else
+ {
+ msg_perror ("cannot read link %s", current_file_name);
+ }
+ different++;
+ break;
+ }
+
+ linkbuf[check] = '\0'; /* Null-terminate it */
+ if (strncmp (current_link_name, linkbuf, check) != 0)
+ {
+ fprintf (msg_file, "%s: symlink differs\n",
+ current_link_name);
+ different++;
+ }
+ }
+ break;
+#endif
+
+#ifdef S_IFCHR
+ case LF_CHR:
+ hstat.st_mode |= S_IFCHR;
+ goto check_node;
+#endif
+
+#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_ISFIFO
+ /* If local system doesn't support FIFOs, use default case */
+ case LF_FIFO:
+#ifdef S_IFIFO
+ hstat.st_mode |= S_IFIFO;
+#endif
+ 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", current_file_name);
+ different++;
+ break;
+ }
+#ifdef S_IFMT
+ if (hstat.st_mode != filestat.st_mode)
+#else /* POSIX lossage */
+ if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
+#endif
+ {
+ fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
+ different++;
+ break;
+ }
+ break;
+
+ case LF_DUMPDIR:
+ data = diff_dir = get_dir_contents (current_file_name, 0);
+ if (data)
+ {
+ wantbytes ((long) (hstat.st_size), compare_dir);
+ free (data);
+ }
+ else
+ wantbytes ((long) (hstat.st_size), no_op);
+ /* FALL THROUGH */
+
+ case LF_DIR:
+ /* Check for trailing / */
+ namelen = strlen (current_file_name) - 1;
+ really_dir:
+ while (namelen && current_file_name[namelen] == '/')
+ current_file_name[namelen--] = '\0'; /* Zap / */
+
+ if (do_stat (&filestat))
+ break;
+ if (!S_ISDIR (filestat.st_mode))
+ {
+ fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
+ different++;
+ break;
+ }
+ if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
+ sigh ("mode");
+ break;
+
+ case LF_VOLHDR:
+ break;
+
+ case LF_MULTIVOL:
+ namelen = strlen (current_file_name) - 1;
+ if (current_file_name[namelen] == '/')
+ goto really_dir;
+
+ if (do_stat (&filestat))
+ break;
+
+ if (!S_ISREG (filestat.st_mode))
+ {
+ fprintf (msg_file, "%s: not a regular file\n",
+ current_file_name);
+ skip_file ((long) hstat.st_size);
+ different++;
+ break;
+ }
+
+ filestat.st_mode &= 07777;
+ 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 (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
+
+ if (diff_fd < 0)
+ {
+ msg_perror ("cannot open file %s", current_file_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, current_file_name);
+ different++;
+ break;
+ }
+
+ wantbytes ((long) (hstat.st_size), compare_chunk);
+
+ check = close (diff_fd);
+ if (check < 0)
+ {
+ msg_perror ("Error while closing %s", current_file_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", current_file_name);
+ }
+ else
+ {
+ fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
+ }
+ different++;
+ return -1;
+ }
+ if (bcmp (buffer, diff_buf, bytes))
+ {
+ fprintf (msg_file, "%s: data differs\n", current_file_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", current_file_name);
+ different++;
+ return -1;
+ }
+ diff_dir += bytes;
+ return 0;
+}
+
+/*
+ * Sigh about something that differs.
+ */
+void
+sigh (what)
+ char *what;
+{
+
+ fprintf (msg_file, "%s: %s differs\n",
+ current_file_name, what);
+}
+
+void
+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 (current_file_name, statp) : lstat (current_file_name, statp);
+ if (err < 0)
+ {
+ if (errno == ENOENT)
+ {
+ fprintf (msg_file, "%s: does not exist\n", current_file_name);
+ }
+ else
+ msg_perror ("can't stat file %s", current_file_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.
+ */
+
+void
+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 *) ck_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 *) ck_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", current_file_name);
+ else
+ fprintf (msg_file, "%s: could only read %d of %d bytes\n",
+ current_file_name, 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", current_file_name);
+ else
+ fprintf (msg_file, "%s: could only read %d of %d bytes\n",
+ current_file_name, 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", current_file_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.
+ */
+void
+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 *) ck_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 *)
+ ck_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/gnu/usr.bin/tar/extract.c b/gnu/usr.bin/tar/extract.c
new file mode 100644
index 000000000000..d162cab04ec6
--- /dev/null
+++ b/gnu/usr.bin/tar/extract.c
@@ -0,0 +1,907 @@
+/* Extract files from a tar archive.
+ Copyright (C) 1988, 1992, 1993 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 2, 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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <sys/types.h>
+#include <time.h>
+time_t time ();
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#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
+
+#include "tar.h"
+#include "port.h"
+
+#if defined(_POSIX_VERSION)
+#include <utime.h>
+#else
+struct utimbuf
+{
+ long actime;
+ long modtime;
+};
+
+#endif
+
+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;
+
+int confirm ();
+void decode_header ();
+void extract_mangle ();
+void extract_sparse_file ();
+long from_oct ();
+void gnu_restore ();
+extern void print_header ();
+extern void skip_file ();
+extern void skip_extended_headers ();
+extern void pr_mkdir ();
+void saverec ();
+
+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;*/
+
+struct saved_dir_info
+{
+ char *path;
+ int mode;
+ int atime;
+ int mtime;
+ struct saved_dir_info *next;
+};
+
+struct saved_dir_info *saved_dir_info_head;
+
+/*
+ * Set up to extract files.
+ */
+void
+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;
+ struct utimbuf acc_upd_times;
+ register int skipcrud;
+ register int i;
+ /* int sparse_ind = 0;*/
+ union record *exhdr;
+ struct saved_dir_info *tmp;
+ /* 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", current_file_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
+ && '/' == current_file_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 + current_file_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 *) ck_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 *)
+ ck_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 + current_file_name) - 1;
+ if (current_file_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 ((longname ? longname : head->header.name)
+ + skipcrud,
+ 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 ((longname
+ ? longname
+ : head->header.name) + skipcrud,
+ hstat.st_mode);
+#else
+ /*
+ * With 3-arg open(), we can do this up right.
+ */
+ fd = open (skipcrud + current_file_name,
+ openflag, hstat.st_mode);
+#endif
+ }
+
+ if (fd < 0)
+ {
+ if (make_dirs (skipcrud + current_file_name))
+ goto again_file;
+ msg_perror ("Could not create file %s",
+ skipcrud + current_file_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 + current_file_name) + 1;
+ name = (char *) ck_malloc ((sizeof (char)) * namelen);
+ bcopy (skipcrud + current_file_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 = current_file_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 + current_file_name);
+ else
+ msg ("could only write %d of %d bytes to file %s",
+ check, written, skipcrud + current_file_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 + current_file_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 + current_file_name,
+ hstat.st_uid, hstat.st_gid) < 0)
+ {
+ msg_perror ("cannot chown file %s to uid %d gid %d",
+ skipcrud + current_file_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.actime = hstat.st_atime;
+ else
+ acc_upd_times.actime = now; /* Accessed now */
+ acc_upd_times.modtime = hstat.st_mtime; /* Mod'd */
+ if (utime (skipcrud + current_file_name,
+ &acc_upd_times) < 0)
+ {
+ msg_perror ("couldn't change access and modification times of %s", skipcrud + current_file_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 + current_file_name,
+ notumask & (int) hstat.st_mode) < 0)
+ {
+ msg_perror ("cannot change mode of file %s to %ld",
+ skipcrud + current_file_name,
+ notumask & (int) hstat.st_mode);
+ }
+ }
+
+ quit:
+ break;
+
+ case LF_LINK:
+ again_link:
+ {
+ struct stat st1, st2;
+
+ check = link (current_link_name, skipcrud + current_file_name);
+
+ if (check == 0)
+ break;
+ if (make_dirs (skipcrud + current_file_name))
+ goto again_link;
+ if (f_gnudump && errno == EEXIST)
+ break;
+ if (stat (current_link_name, &st1) == 0
+ && stat (current_file_name + skipcrud, &st2) == 0
+ && st1.st_dev == st2.st_dev
+ && st1.st_ino == st2.st_ino)
+ break;
+ msg_perror ("Could not link %s to %s",
+ skipcrud + current_file_name,
+ current_link_name);
+ }
+ break;
+
+#ifdef S_ISLNK
+ case LF_SYMLINK:
+ again_symlink:
+ check = symlink (current_link_name,
+ skipcrud + current_file_name);
+ /* FIXME, don't worry uid, gid, etc... */
+ if (check == 0)
+ break;
+ if (make_dirs (current_file_name + skipcrud))
+ goto again_symlink;
+ msg_perror ("Could not create symlink to %s",
+ current_link_name);
+ 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;
+#endif
+#if defined(S_IFCHR) || defined(S_IFBLK)
+ make_node:
+ check = mknod (current_file_name + skipcrud,
+ (int) hstat.st_mode, (int) hstat.st_rdev);
+ if (check != 0)
+ {
+ if (make_dirs (skipcrud + current_file_name))
+ goto make_node;
+ msg_perror ("Could not make %s",
+ current_file_name + skipcrud);
+ break;
+ };
+ goto set_filestat;
+#endif
+
+#ifdef S_ISFIFO
+ /* If local system doesn't support FIFOs, use default case */
+ case LF_FIFO:
+ make_fifo:
+ check = mkfifo (current_file_name + skipcrud,
+ (int) hstat.st_mode);
+ if (check != 0)
+ {
+ if (make_dirs (current_file_name + skipcrud))
+ goto make_fifo;
+ msg_perror ("Could not make %s",
+ skipcrud + current_file_name);
+ break;
+ };
+ goto set_filestat;
+#endif
+
+ case LF_DIR:
+ case LF_DUMPDIR:
+ namelen = strlen (current_file_name + skipcrud) - 1;
+ really_dir:
+ /* Check for trailing /, and zap as many as we find. */
+ while (namelen
+ && current_file_name[skipcrud + namelen] == '/')
+ current_file_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 + current_file_name,
+ (we_are_root ? 0 : 0300) | (int) hstat.st_mode);
+ if (check != 0)
+ {
+ struct stat st1;
+
+ if (make_dirs (skipcrud + current_file_name))
+ goto again_dir;
+ /* If we're trying to create '.', let it be. */
+ if (current_file_name[skipcrud + namelen] == '.' &&
+ (namelen == 0 ||
+ current_file_name[skipcrud + namelen - 1] == '/'))
+ goto check_perms;
+ if (errno == EEXIST
+ && stat (skipcrud + current_file_name, &st1) == 0
+ && (S_ISDIR (st1.st_mode)))
+ break;
+ msg_perror ("Could not create directory %s", skipcrud + current_file_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 + current_file_name);
+ }
+
+ /*
+ * 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 + current_file_name,
+ hstat.st_uid, hstat.st_gid) < 0)
+ {
+ msg_perror ("cannot chown file %s to uid %d gid %d",
+ skipcrud + current_file_name,
+ hstat.st_uid, hstat.st_gid);
+ }
+ }
+
+ if (!f_modified)
+ {
+ tmp = ((struct saved_dir_info *)
+ ck_malloc (sizeof (struct saved_dir_info)));
+ tmp->path = (char *) ck_malloc (strlen (skipcrud
+ + current_file_name) + 1);
+ strcpy (tmp->path, skipcrud + current_file_name);
+ tmp->mode = hstat.st_mode;
+ tmp->atime = hstat.st_atime;
+ tmp->mtime = hstat.st_mtime;
+ tmp->next = saved_dir_info_head;
+ saved_dir_info_head = tmp;
+ }
+ else
+ {
+ /* This functions exactly as the code for set_filestat above. */
+ if ((!f_keep)
+ || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ if (chmod (skipcrud + current_file_name,
+ notumask & (int) hstat.st_mode) < 0)
+ {
+ msg_perror ("cannot change mode of file %s to %ld",
+ skipcrud + current_file_name,
+ notumask & (int) hstat.st_mode);
+ }
+ }
+ }
+ break;
+
+ case LF_VOLHDR:
+ if (f_verbose)
+ {
+ printf ("Reading %s\n", current_file_name);
+ }
+ break;
+
+ case LF_NAMES:
+ extract_mangle (head);
+ break;
+
+ case LF_MULTIVOL:
+ msg ("Can't extract '%s'--file is continued from another volume\n", current_file_name);
+ skip_file ((long) hstat.st_size);
+ break;
+
+ case LF_LONGNAME:
+ case LF_LONGLINK:
+ msg ("Visible long name error\n");
+ 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 */
+}
+
+void
+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", count,
+ 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);
+}
+
+/* Set back the utime and mode for all the extracted directories. */
+void
+restore_saved_dir_info ()
+{
+ struct utimbuf acc_upd_times;
+
+ while (saved_dir_info_head != NULL)
+ {
+ /* fixme if f_gnudump should set ctime too, but how? */
+ if (f_gnudump)
+ acc_upd_times.actime = saved_dir_info_head->atime;
+ else
+ acc_upd_times.actime = now; /* Accessed now */
+ acc_upd_times.modtime = saved_dir_info_head->mtime; /* Mod'd */
+ if (utime (saved_dir_info_head->path, &acc_upd_times) < 0)
+ {
+ msg_perror ("couldn't change access and modification times of %s",
+ saved_dir_info_head->path);
+ }
+ if ((!f_keep) || (saved_dir_info_head->mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ if (chmod (saved_dir_info_head->path,
+ notumask & saved_dir_info_head->mode) < 0)
+ {
+ msg_perror ("cannot change mode of file %s to %ld",
+ saved_dir_info_head->path,
+ notumask & saved_dir_info_head->mode);
+ }
+ }
+ saved_dir_info_head = saved_dir_info_head->next;
+ }
+}
diff --git a/gnu/usr.bin/tar/fnmatch.c b/gnu/usr.bin/tar/fnmatch.c
new file mode 100644
index 000000000000..ed8c9eea7e35
--- /dev/null
+++ b/gnu/usr.bin/tar/fnmatch.c
@@ -0,0 +1,173 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <fnmatch.h>
+
+#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
+extern int errno;
+#endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ it matches, nonzero if not. */
+int
+fnmatch (pattern, string, flags)
+ const char *pattern;
+ const char *string;
+ int flags;
+{
+ register const char *p = pattern, *n = string;
+ register char c;
+
+ if ((flags & ~__FNM_FLAGS) != 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while ((c = *p++) != '\0')
+ {
+ switch (c)
+ {
+ case '?':
+ if (*n == '\0')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PATHNAME) && *n == '/')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+ break;
+
+ case '\\':
+ if (!(flags & FNM_NOESCAPE))
+ c = *p++;
+ if (*n != c)
+ return FNM_NOMATCH;
+ break;
+
+ case '*':
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+ if (((flags & FNM_PATHNAME) && *n == '/') ||
+ (c == '?' && *n == '\0'))
+ return FNM_NOMATCH;
+
+ if (c == '\0')
+ return 0;
+
+ {
+ char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+ for (--p; *n != '\0'; ++n)
+ if ((c == '[' || *n == c1) &&
+ fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+ return 0;
+ return FNM_NOMATCH;
+ }
+
+ case '[':
+ {
+ /* Nonzero if the sense of the character class is inverted. */
+ register int not;
+
+ if (*n == '\0')
+ return FNM_NOMATCH;
+
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ not = (*p == '!' || *p == '^');
+ if (not)
+ ++p;
+
+ c = *p++;
+ for (;;)
+ {
+ register char cstart = c, cend = c;
+
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ cstart = cend = *p++;
+
+ if (c == '\0')
+ /* [ (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+
+ if ((flags & FNM_PATHNAME) && c == '/')
+ /* [/] can never match. */
+ return FNM_NOMATCH;
+
+ if (c == '-' && *p != ']')
+ {
+ cend = *p++;
+ if (!(flags & FNM_NOESCAPE) && cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return FNM_NOMATCH;
+ c = *p++;
+ }
+
+ if (*n >= cstart && *n <= cend)
+ goto matched;
+
+ if (c == ']')
+ break;
+ }
+ if (!not)
+ return FNM_NOMATCH;
+ break;
+
+ matched:;
+ /* Skip the rest of the [...] that already matched. */
+ while (c != ']')
+ {
+ if (c == '\0')
+ /* [... (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ /* 1003.2d11 is unclear if this is right. %%% */
+ ++p;
+ }
+ if (not)
+ return FNM_NOMATCH;
+ }
+ break;
+
+ default:
+ if (c != *n)
+ return FNM_NOMATCH;
+ }
+
+ ++n;
+ }
+
+ if (*n == '\0')
+ return 0;
+
+ if ((flags & FNM_LEADING_DIR) && *n == '/')
+ /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
+ return 0;
+
+ return FNM_NOMATCH;
+}
diff --git a/gnu/usr.bin/tar/fnmatch.h b/gnu/usr.bin/tar/fnmatch.h
new file mode 100644
index 000000000000..d4150a9a9969
--- /dev/null
+++ b/gnu/usr.bin/tar/fnmatch.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#ifndef _FNMATCH_H
+
+#define _FNMATCH_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef __P
+#define __P(args) args
+#else /* Not C++ or ANSI C. */
+#undef __P
+#define __P(args) ()
+#undef const
+#define const
+#endif /* C++ or ANSI C. */
+
+/* Bits set in the FLAGS argument to `fnmatch'. */
+#ifdef FNM_PATHNAME /* Because it is already defined in <unistd.h> */
+#undef FNM_PATHNAME
+#endif
+#define FNM_PATHNAME (1 << 0)/* No wildcard can ever match `/'. */
+#define FNM_NOESCAPE (1 << 1)/* Backslashes don't quote special chars. */
+#define FNM_PERIOD (1 << 2)/* Leading `.' is matched only explicitly. */
+#define __FNM_FLAGS (FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD|FNM_LEADING_DIR)
+
+#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_BSD_SOURCE)
+#define FNM_LEADING_DIR (1 << 3)/* Ignore `/...' after a match. */
+#define FNM_FILE_NAME FNM_PATHNAME
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN. */
+#define FNM_NOMATCH 1
+
+/* Match STRING against the filename pattern PATTERN,
+ returning zero if it matches, FNM_NOMATCH if not. */
+extern int fnmatch __P ((const char *__pattern, const char *__string,
+ int __flags));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/gnu/usr.bin/tar/getdate.y b/gnu/usr.bin/tar/getdate.y
new file mode 100644
index 000000000000..7b0ac79924f4
--- /dev/null
+++ b/gnu/usr.bin/tar/getdate.y
@@ -0,0 +1,969 @@
+%{
+/* $Revision: 2.1 $
+**
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> 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 HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX /* for Bison */
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* The code at the top of get_date which figures out the offset of the
+ current time zone checks various CPP symbols to see if special
+ tricks are need, but defaults to using the gettimeofday system call.
+ Include <sys/time.h> if that will be used. */
+
+#if !defined (USG) && !defined (sgi) && !defined (__386BSD__)
+#include <sys/time.h>
+#endif
+
+#if defined(vms)
+
+#include <types.h>
+#include <time.h>
+
+#else
+
+#include <sys/types.h>
+
+#if defined(USG) || !defined(HAVE_FTIME)
+/*
+** If you need to do a tzset() call to set the
+** timezone, and don't have ftime().
+*/
+struct timeb {
+ time_t time; /* Seconds since the epoch */
+ unsigned short millitm; /* Field not used */
+ short timezone;
+ short dstflag; /* Field not used */
+};
+
+#else
+
+#include <sys/timeb.h>
+
+#endif /* defined(USG) && !defined(HAVE_FTIME) */
+
+#if defined(BSD4_2) || defined(BSD4_1C) || (defined (hp9000) && !defined (hpux))
+#include <sys/time.h>
+#else
+#if defined(_AIX)
+#include <sys/time.h>
+#endif
+#include <time.h>
+#endif /* defined(BSD4_2) */
+
+#endif /* defined(vms) */
+
+#if defined (STDC_HEADERS) || defined (USG)
+#include <string.h>
+#endif
+
+#if sgi
+#undef timezone
+#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) ((time_t)(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 tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> 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;
+ }
+ |
+ tZONE tDST {
+ 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;
+ }
+ | tUNUMBER tSNUMBER tSNUMBER {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = $1;
+ yyMonth = -$2;
+ yyDay = -$3;
+ }
+ | 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 const 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 const 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 const 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. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE const 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
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "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 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and 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 */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "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 const 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 */
+static 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 const 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;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ 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;
+}
+
+
+static 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(HAVE_FTIME)
+ (void)time(&ftz.time);
+ /* Set the timezone global. */
+ tzset();
+ {
+#if sgi
+ ftz.timezone = (int) _timezone / 60;
+#else /* not sgi */
+#ifdef __386BSD__
+ ftz.timezone = 0;
+#else /* neither sgi nor 386BSD */
+#if defined (USG)
+ extern time_t timezone;
+
+ ftz.timezone = (int) timezone / 60;
+#else /* neither sgi nor 386BSD nor USG */
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday (&tv, &tz);
+ ftz.timezone = (int) tz.tz_minuteswest;
+#endif /* neither sgi nor 386BSD nor USG */
+#endif /* neither sgi nor 386BSD */
+#endif /* not sgi */
+ }
+#else /* HAVE_FTIME */
+ (void)ftime(&ftz);
+#endif /* HAVE_FTIME */
+ }
+
+ 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/gnu/usr.bin/tar/getoldopt.c b/gnu/usr.bin/tar/getoldopt.c
new file mode 100644
index 000000000000..27511b94b342
--- /dev/null
+++ b/gnu/usr.bin/tar/getoldopt.c
@@ -0,0 +1,96 @@
+/* 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 2, 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)
+ */
+
+#include <stdio.h>
+#include "getopt.h"
+#include "tar.h" /* For msg() declaration if STDC_MSG. */
+#include <sys/types.h>
+#include "port.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 '-' */
+ 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/gnu/usr.bin/tar/getopt.c b/gnu/usr.bin/tar/getopt.c
new file mode 100644
index 000000000000..3db9abf121b9
--- /dev/null
+++ b/gnu/usr.bin/tar/getopt.c
@@ -0,0 +1,712 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* NOTE!!! AIX requires this to be the first thing in the file.
+ Do not put ANYTHING before it! */
+#if !defined (__GNUC__) && defined (_AIX)
+ #pragma alloca
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
+#include <alloca.h>
+#else
+#ifndef _AIX
+char *alloca ();
+#endif
+#endif /* alloca.h */
+#endif /* not __GNUC__ */
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#undef alloca
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#else /* Not GNU C library. */
+#define __alloca alloca
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 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. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (string, chr)
+ char *string;
+ int chr;
+{
+ while (*string)
+ {
+ if (*string == chr)
+ return string;
+ string++;
+ }
+ return 0;
+}
+
+static void
+my_bcopy (from, to, size)
+ char *from, *to;
+ int size;
+{
+ int i;
+ for (i = 0; i < size; i++)
+ to[i] = from[i];
+}
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
+ char **temp = (char **) __alloca (nonopts_size);
+
+ /* Interchange the two blocks of data in ARGV. */
+
+ my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
+ my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
+ (optind - last_nonopt) * sizeof (char *));
+ my_bcopy ((char *) temp,
+ (char *) &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
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ 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 = NULL;
+
+ /* 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 ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *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 ((char **) 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')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ 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 ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ 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
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, 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 == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 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);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#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/gnu/usr.bin/tar/getopt.h b/gnu/usr.bin/tar/getopt.h
new file mode 100644
index 000000000000..93a5cf77816e
--- /dev/null
+++ b/gnu/usr.bin/tar/getopt.h
@@ -0,0 +1,125 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/tar/getopt1.c b/gnu/usr.bin/tar/getopt1.c
new file mode 100644
index 000000000000..c3582cfa5caf
--- /dev/null
+++ b/gnu/usr.bin/tar/getopt1.c
@@ -0,0 +1,161 @@
+/* Getopt for GNU.
+ Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/tar/getpagesize.h b/gnu/usr.bin/tar/getpagesize.h
new file mode 100644
index 000000000000..2d43f262c79d
--- /dev/null
+++ b/gnu/usr.bin/tar/getpagesize.h
@@ -0,0 +1,38 @@
+#ifdef BSD
+#ifndef BSD4_1
+#define HAVE_GETPAGESIZE
+#endif
+#endif
+
+#ifndef HAVE_GETPAGESIZE
+
+#ifdef VMS
+#define getpagesize() 512
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _SC_PAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#else
+
+#include <sys/param.h>
+
+#ifdef EXEC_PAGESIZE
+#define getpagesize() EXEC_PAGESIZE
+#else
+#ifdef NBPG
+#define getpagesize() NBPG * CLSIZE
+#ifndef CLSIZE
+#define CLSIZE 1
+#endif /* no CLSIZE */
+#else /* no NBPG */
+#define getpagesize() NBPC
+#endif /* no NBPG */
+#endif /* no EXEC_PAGESIZE */
+#endif /* no _SC_PAGESIZE */
+
+#endif /* not HAVE_GETPAGESIZE */
+
diff --git a/gnu/usr.bin/tar/gnu.c b/gnu/usr.bin/tar/gnu.c
new file mode 100644
index 000000000000..ef51f2b5fedd
--- /dev/null
+++ b/gnu/usr.bin/tar/gnu.c
@@ -0,0 +1,677 @@
+/* GNU dump extensions to tar.
+ Copyright (C) 1988, 1992, 1993 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 2, 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. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <time.h>
+time_t time ();
+
+#include "tar.h"
+#include "port.h"
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+extern time_t new_time;
+extern FILE *msg_file;
+
+void addname ();
+int check_exclude ();
+extern PTR ck_malloc ();
+extern PTR ck_realloc ();
+int confirm ();
+extern PTR init_buffer ();
+extern char *get_buffer ();
+int is_dot_or_dotdot ();
+extern void add_buffer ();
+extern void flush_buffer ();
+void name_gather ();
+int recursively_delete ();
+void skip_file ();
+char *un_quote_string ();
+
+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;
+ dev_t dev;
+ ino_t ino;
+{
+ struct dirname *dp;
+
+ dp = (struct dirname *) ck_malloc (sizeof (struct dirname));
+ if (!dp)
+ abort ();
+ dp->next = dir_list;
+ dir_list = dp;
+ dp->dev = dev;
+ dp->ino = ino;
+ dp->name = ck_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];
+ static char *path = 0;
+
+ if (path == 0)
+ path = ck_malloc (PATH_MAX);
+ time (&this_time);
+ if (gnu_dumpfile[0] != '/')
+ {
+#if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION)
+ if (!getcwd (path, PATH_MAX))
+ {
+ 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. */
+void
+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 (S_ISDIR (statbuf.st_mode))
+ {
+ 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 ((PTR) 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 ();
+}
+
+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)
+ const PTR p1;
+ const PTR p2;
+{
+ char *frst, *scnd;
+
+ frst = (*(char **) p1) + 1;
+ scnd = (*(char **) p2) + 1;
+
+ return strcmp (frst, scnd);
+}
+
+char *
+get_dir_contents (p, device)
+ char *p;
+ int device;
+{
+ DIR *dirp;
+ register struct dirent *d;
+ char *new_buf;
+ char *namebuf;
+ int bufsiz;
+ int len;
+ PTR the_buffer;
+ char *buf;
+ size_t 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 = NULL;
+ }
+ 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 (NLENGTH (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 (S_ISDIR (hs.st_mode))
+ {
+ 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) (NLENGTH (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 = NULL;
+ }
+ else
+ {
+ n_strs = 0;
+ for (p_buf = buf; *p_buf;)
+ {
+ int tmp;
+
+ tmp = strlen (p_buf) + 1;
+ n_strs++;
+ p_buf += tmp;
+ }
+ vec = (char **) ck_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 ((PTR) vec, n_strs, sizeof (char *), dirent_cmp);
+ new_buf = (char *) ck_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;
+
+ /* PTR 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 ? new_buf : "\0\0\0\0";
+ break;
+ }
+ }
+
+ if (new_buf)
+ {
+ 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. */
+int
+is_dot_or_dotdot (p)
+ char *p;
+{
+ return (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')));
+}
+
+
+
+
+
+
+void
+gnu_restore (skipcrud)
+ int skipcrud;
+{
+ char *current_dir;
+ /* int current_dir_length; */
+
+ char *archive_dir;
+ /* int archive_dir_length; */
+ PTR the_buffer;
+ char *p;
+ DIR *dirp;
+ struct dirent *d;
+ char *cur, *arc;
+ extern struct stat hstat; /* Stat struct corresponding */
+ long size, copied;
+ char *from, *to;
+ extern union record *head;
+
+ dirp = opendir (skipcrud + current_file_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) (NLENGTH (d) + 1));
+ }
+ closedir (dirp);
+ add_buffer (the_buffer, "", 1);
+
+ current_dir = get_buffer (the_buffer);
+ archive_dir = (char *) ck_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 ((PTR) from, (PTR) 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 + current_file_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);
+}
+
+int
+recursively_delete (path)
+ char *path;
+{
+ struct stat sbuf;
+ DIR *dirp;
+ struct dirent *dp;
+ char *path_buf;
+ /* int path_len; */
+
+
+ if (lstat (path, &sbuf) < 0)
+ return 1;
+ if (S_ISDIR (sbuf.st_mode))
+ {
+
+ /* 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/gnu/usr.bin/tar/list.c b/gnu/usr.bin/tar/list.c
new file mode 100644
index 000000000000..a0c65a334bf2
--- /dev/null
+++ b/gnu/usr.bin/tar/list.c
@@ -0,0 +1,881 @@
+/* List a tar archive.
+ Copyright (C) 1988, 1992, 1993 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 2, 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).
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <time.h>
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#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 */
+void restore_saved_dir_info ();
+PTR ck_malloc ();
+
+union record *head; /* Points to current archive header */
+struct stat hstat; /* Stat struct corresponding */
+int head_standard; /* Tape header is in ANSI format */
+
+int check_exclude ();
+void close_archive ();
+void decode_header ();
+int findgid ();
+int finduid ();
+void name_gather ();
+int name_match ();
+void names_notfound ();
+void open_archive ();
+void print_header ();
+int read_header ();
+void saverec ();
+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. */
+
+ if (!name_match (current_file_name)
+ || (f_new_files && hstat.st_mtime < new_time)
+ || (f_exclude && check_exclude (current_file_name)))
+ {
+
+ int isextended = 0;
+
+ if (head->header.linkflag == LF_VOLHDR
+ || head->header.linkflag == LF_MULTIVOL
+ || head->header.linkflag == LF_NAMES)
+ {
+ (*do_something) ();
+ continue;
+ }
+ if (f_show_omitted_dirs
+ && head->header.linkflag == LF_DIR)
+ msg ("Omitting %s\n", current_file_name);
+ /* 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;
+ };
+
+ restore_saved_dir_info ();
+ 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 long save_totsize;
+ extern long save_sizeleft;
+
+ userec (head);
+ if (f_multivol)
+ {
+ save_name = current_file_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, current_file_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 = current_file_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, signed_sum, recsum;
+ register char *p;
+ register union record *header;
+ long from_oct ();
+ char **longp;
+ char *bp, *data;
+ int size, written;
+ static char *next_long_name, *next_long_link;
+ char *name;
+
+recurse:
+
+ 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.
+ */
+ signed_sum += *p;
+ 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];
+ signed_sum -= (char) header->header.chksum[i];
+ }
+ sum += ' ' * sizeof header->header.chksum;
+ signed_sum += ' ' * sizeof header->header.chksum;
+
+ 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;
+ }
+
+ if (sum != recsum && signed_sum != recsum)
+ return 0;
+
+ /*
+ * 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);
+
+ header->header.arch_name[NAMSIZ - 1] = '\0';
+ if (header->header.linkflag == LF_LONGNAME
+ || header->header.linkflag == LF_LONGLINK)
+ {
+ longp = ((header->header.linkflag == LF_LONGNAME)
+ ? &next_long_name
+ : &next_long_link);
+
+ userec (header);
+ if (*longp)
+ free (*longp);
+ bp = *longp = (char *) ck_malloc (hstat.st_size);
+
+ for (size = hstat.st_size;
+ size > 0;
+ size -= written)
+ {
+ data = findrec ()->charptr;
+ if (data == NULL)
+ {
+ msg ("Unexpected EOF on archive file");
+ break;
+ }
+ written = endofrecs ()->charptr - data;
+ if (written > size)
+ written = size;
+
+ bcopy (data, bp, written);
+ bp += written;
+ userec ((union record *) (data + written - 1));
+ }
+ goto recurse;
+ }
+ else
+ {
+ name = (next_long_name
+ ? next_long_name
+ : head->header.arch_name);
+ if (current_file_name)
+ free (current_file_name);
+ current_file_name = ck_malloc (strlen (name) + 1);
+ strcpy (current_file_name, name);
+
+ name = (next_long_link
+ ? next_long_link
+ : head->header.arch_linkname);
+ if (current_link_name)
+ free (current_link_name);
+ current_link_name = ck_malloc (strlen (name) + 1);
+ strcpy (current_link_name, name);
+
+ next_long_link = next_long_name = 0;
+ return 1;
+ }
+}
+
+
+/*
+ * 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.
+ */
+void
+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_mode &= 07777;
+ 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 =
+ (*header->header.uname
+ ? finduid (header->header.uname)
+ : from_oct (8, header->header.uid));
+ st->st_gid =
+ (*header->header.gname
+ ? findgid (header->header.gname)
+ : from_oct (8, header->header.gid));
+#endif
+ }
+#if defined(S_IFBLK) || defined(S_IFCHR)
+ 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));
+ }
+#endif
+ }
+ 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 <head>, <hstat>, and
+ * <head_standard>, which must be set up in advance. This is not very clean
+ * and should be cleaned up. FIXME.
+ */
+#define UGSWIDTH 18 /* min width of User, group, size */
+/* UGSWIDTH of 18 means that with user and group names <= 8 chars the columns
+ never shift during the listing. */
+#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 */
+ time_t 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 (current_file_name);
+ if (name == 0)
+ name = current_file_name;
+ fprintf (msg_file, "%s\n", name);
+ if (name != current_file_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_LONGNAME:
+ case LF_LONGLINK:
+ msg ("Visible longname error\n");
+ break;
+
+ case LF_SPARSE:
+ case LF_NORMAL:
+ case LF_OLDNORMAL:
+ case LF_LINK:
+ modes[0] = '-';
+ if ('/' == current_file_name[strlen (current_file_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",
+ from_oct (8, head->header.uid));
+ }
+ if (*head->header.gname && head_standard)
+ {
+ group = head->header.gname;
+ }
+ else
+ {
+ group = gform;
+ (void) sprintf (gform, "%d",
+ from_oct (8, head->header.gid));
+ }
+
+ /* Format the file size or major/minor device numbers */
+ switch (head->header.linkflag)
+ {
+#if defined(S_IFBLK) || defined(S_IFCHR)
+ case LF_CHR:
+ case LF_BLK:
+ (void) sprintf (size, "%d,%d",
+ major (hstat.st_rdev),
+ minor (hstat.st_rdev));
+ break;
+#endif
+ 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 (current_file_name);
+ if (!name)
+ name = current_file_name;
+ fprintf (msg_file, "%s %s/%s %*s%s %s %s %s",
+ modes,
+ user,
+ group,
+ ugswidth - pad,
+ "",
+ size,
+ timestamp + 4, timestamp + 20,
+ name);
+
+ if (name != current_file_name)
+ free (name);
+ switch (head->header.linkflag)
+ {
+ case LF_SYMLINK:
+ name = quote_copy_string (current_link_name);
+ if (!name)
+ name = current_link_name;
+ fprintf (msg_file, " -> %s\n", name);
+ if (name != current_link_name)
+ free (name);
+ break;
+
+ case LF_LINK:
+ name = quote_copy_string (current_link_name);
+ if (!name)
+ name = current_link_name;
+ fprintf (msg_file, " link to %s\n", current_link_name);
+ if (name != current_link_name)
+ 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 <size> 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/gnu/usr.bin/tar/mangle.c b/gnu/usr.bin/tar/mangle.c
new file mode 100644
index 000000000000..628168473a1b
--- /dev/null
+++ b/gnu/usr.bin/tar/mangle.c
@@ -0,0 +1,270 @@
+/* mangle.c -- encode long filenames
+ Copyright (C) 1988, 1992 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 2, 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. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+time_t time ();
+
+#include "tar.h"
+#include "port.h"
+
+void add_buffer ();
+extern PTR ck_malloc ();
+void finish_header ();
+extern PTR init_buffer ();
+extern char *quote_copy_string ();
+extern char *get_buffer ();
+char *un_quote_string ();
+
+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;
+
+#if 0 /* Deleted because there is now a better way to do all this */
+
+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_ISLNK
+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;
+ PTR 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 (bufsize < size)
+ {
+ bcopy (ptr1, header->charptr, 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);
+}
+
+#endif
+
+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_ISLNK
+ else if (!strncmp (ptr, "Symlink ", 8))
+ {
+ nam1 = ptr + 8;
+ nam1end = index (nam1, ' ');
+ while (strncmp (nam1end, " to ", 4))
+ {
+ nam1end++;
+ nam1end = index (nam1end, ' ');
+ }
+ *nam1end = '\0';
+ 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/gnu/usr.bin/tar/msd_dir.h b/gnu/usr.bin/tar/msd_dir.h
new file mode 100644
index 000000000000..06c7a644b42d
--- /dev/null
+++ b/gnu/usr.bin/tar/msd_dir.h
@@ -0,0 +1,44 @@
+/*
+ * @(#)msd_dir.h 1.4 87/11/06 Public Domain.
+ *
+ * A public domain implementation of BSD directory routines for
+ * MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield),
+ * August 1897
+ */
+
+#define rewinddir(dirp) seekdir(dirp, 0L)
+
+#define MAXNAMLEN 12
+
+#ifdef __TURBOC__
+typedef int ino_t;
+typedef int dev_t;
+#endif
+
+struct dirent
+ {
+ ino_t d_ino; /* a bit of a farce */
+ int d_reclen; /* more farce */
+ int d_namlen; /* length of d_name */
+ char d_name[MAXNAMLEN + 1]; /* garentee null termination */
+ };
+
+struct _dircontents
+ {
+ char *_d_entry;
+ struct _dircontents *_d_next;
+ };
+
+typedef struct _dirdesc
+ {
+ int dd_id; /* uniquely identify each open directory */
+ long dd_loc; /* where we are in directory entry is this */
+ struct _dircontents *dd_contents; /* pointer to contents of dir */
+ struct _dircontents *dd_cp; /* pointer to current position */
+ } DIR;
+
+extern DIR *opendir ();
+extern struct dirent *readdir ();
+extern void seekdir ();
+extern long telldir ();
+extern void closedir ();
diff --git a/gnu/usr.bin/tar/names.c b/gnu/usr.bin/tar/names.c
new file mode 100644
index 000000000000..0de6a8898a30
--- /dev/null
+++ b/gnu/usr.bin/tar/names.c
@@ -0,0 +1,149 @@
+/* Look up user and/or group names.
+ Copyright (C) 1988, 1992 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 2, 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.
+ */
+
+#include <sys/types.h>
+#include "tar.h"
+#include "port.h"
+
+#ifndef NONAMES
+/* Whole module goes away if NONAMES defined. Otherwise... */
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+
+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;
+#ifndef HAVE_GETPWUID
+ extern struct passwd *getpwuid ();
+#endif
+
+ 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;
+#ifndef HAVE_GETGRGID
+ extern struct group *getgrgid ();
+#endif
+
+ 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/gnu/usr.bin/tar/open3.h b/gnu/usr.bin/tar/open3.h
new file mode 100644
index 000000000000..c1c0e59b6761
--- /dev/null
+++ b/gnu/usr.bin/tar/open3.h
@@ -0,0 +1,67 @@
+/* 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 2, 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 -- #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/gnu/usr.bin/tar/pathmax.h b/gnu/usr.bin/tar/pathmax.h
new file mode 100644
index 000000000000..aeba9f7d2186
--- /dev/null
+++ b/gnu/usr.bin/tar/pathmax.h
@@ -0,0 +1,53 @@
+/* Define PATH_MAX somehow. Requires sys/types.h.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _PATHMAX_H
+#define _PATHMAX_H
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+ PATH_MAX but might cause redefinition warnings when sys/param.h is
+ later included (as on MORE/BSD 4.3). */
+#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && defined(USG))
+#include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+
+#if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
+#define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
+#endif
+
+/* Don't include sys/param.h if it already has been. */
+#if !defined(PATH_MAX) && !defined(MAXPATHLEN) && !defined(__MSDOS__)
+#include <sys/param.h>
+#endif
+
+#if !defined(PATH_MAX) && defined(MAXPATHLEN)
+#define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+#endif /* _PATHMAX_H */
diff --git a/gnu/usr.bin/tar/port.c b/gnu/usr.bin/tar/port.c
new file mode 100644
index 000000000000..10ec32ed7288
--- /dev/null
+++ b/gnu/usr.bin/tar/port.c
@@ -0,0 +1,1256 @@
+/* Supporting routines which may sometimes be missing.
+ Copyright (C) 1988, 1992 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 2, 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. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#include "tar.h"
+#include "port.h"
+
+extern long baserec;
+
+/* All machine-dependent #ifdefs should appear here, instead of
+ being scattered through the file. For UN*X systems, it is better to
+ figure out what is needed in the configure script, for most of the
+ features. */
+
+#ifdef __MSDOS__
+char TTY_NAME[] = "con";
+#define HAVE_STRSTR
+#define HAVE_RENAME
+#define HAVE_MKDIR
+#else
+char TTY_NAME[] = "/dev/tty";
+#endif
+
+/* End of system-dependent #ifdefs */
+
+
+#ifndef HAVE_VALLOC
+/*
+ * valloc() does a malloc() on a page boundary. On some systems,
+ * this can make large block I/O more efficient.
+ */
+char *
+valloc (size)
+ unsigned size;
+{
+ return (malloc (size));
+}
+
+#endif /* !HAVE_VALLOC */
+
+#ifndef HAVE_MKDIR
+/*
+ * Written by Robert Rother, Mariah Corporation, August 1985.
+ *
+ * If you want it, it's yours. All I ask in return is that if you
+ * figure out how to do this in a Bourne Shell script you send me
+ * a copy.
+ * sdcsvax!rmr or rmr@uscd
+ *
+ * Severely hacked over by John Gilmore to make a 4.2BSD compatible
+ * subroutine. 11Mar86; hoptoad!gnu
+ *
+ * Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
+ * subroutine didn't return EEXIST. It does now.
+ */
+
+/*
+ * Make a directory.
+ */
+int
+mkdir (dpath, dmode)
+ char *dpath;
+ int dmode;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) == 0)
+ {
+ errno = EEXIST; /* Stat worked, so it already exists */
+ return -1;
+ }
+
+ /* If stat fails for a reason other than non-existence, return error */
+ if (errno != ENOENT)
+ return -1;
+
+ switch (cpid = fork ())
+ {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+ /*
+ * Cheap hack to set mode of new directory. Since this
+ * child process is going away anyway, we zap its umask.
+ * FIXME, this won't suffice to set SUID, SGID, etc. on this
+ * directory. Does anybody care?
+ */
+ status = umask (0); /* Get current umask */
+ status = umask (status | (0777 & ~dmode)); /* Set for mkdir */
+ execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
+ _exit (-1); /* Can't exec /bin/mkdir */
+
+ default: /* Parent process */
+ while (cpid != wait (&status)); /* Wait for kid to finish */
+ }
+
+ if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
+ {
+ errno = EIO; /* We don't know why, but */
+ return -1; /* /bin/mkdir failed */
+ }
+
+ return 0;
+}
+
+int
+rmdir (dpath)
+ char *dpath;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) != 0)
+ {
+ /* Stat just set errno. We don't have to */
+ return -1;
+ }
+
+ switch (cpid = fork ())
+ {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+ execl ("/bin/rmdir", "rmdir", dpath, (char *) 0);
+ _exit (-1); /* Can't exec /bin/mkdir */
+
+ default: /* Parent process */
+ while (cpid != wait (&status)); /* Wait for kid to finish */
+ }
+
+ if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
+ {
+ errno = EIO; /* We don't know why, but */
+ return -1; /* /bin/mkdir failed */
+ }
+
+ return 0;
+}
+
+#endif /* !HAVE_MKDIR */
+
+#ifndef HAVE_RENAME
+/* Rename file FROM to file TO.
+ Return 0 if successful, -1 if not. */
+
+int
+rename (from, to)
+ char *from;
+ char *to;
+{
+ struct stat from_stats;
+
+ if (stat (from, &from_stats))
+ return -1;
+
+ if (unlink (to) && errno != ENOENT)
+ return -1;
+
+ if (link (from, to))
+ return -1;
+
+ if (unlink (from) && errno != ENOENT)
+ {
+ unlink (to);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* !HAVE_RENAME */
+
+#ifdef minix
+/* 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;
+
+ 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 (!S_ISREG (statbuf.st_mode))
+ 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 /* minix */
+
+
+#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;
+{
+ 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 /* EMUL_OPEN3 */
+
+#ifndef HAVE_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;
+{
+ 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 /* !HAVE_MKNOD */
+
+#ifdef __TURBOC__
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+struct utimbuf
+{
+ time_t actime; /* Access time. */
+ time_t modtime; /* Modification time. */
+};
+
+int
+utime (char *filename, struct utimbuf *utb)
+{
+ struct tm *tm;
+ struct ftime filetime;
+ time_t when;
+ int fd;
+ int status;
+
+ if (utb == 0)
+ when = time (0);
+ else
+ when = utb->modtime;
+
+ fd = _open (filename, O_RDWR);
+ if (fd == -1)
+ return -1;
+
+ tm = localtime (&when);
+ if (tm->tm_year < 80)
+ filetime.ft_year = 0;
+ else
+ filetime.ft_year = tm->tm_year - 80;
+ filetime.ft_month = tm->tm_mon + 1;
+ filetime.ft_day = tm->tm_mday;
+ if (tm->tm_hour < 0)
+ filetime.ft_hour = 0;
+ else
+ filetime.ft_hour = tm->tm_hour;
+ filetime.ft_min = tm->tm_min;
+ filetime.ft_tsec = tm->tm_sec / 2;
+
+ status = setftime (fd, &filetime);
+ _close (fd);
+ return status;
+}
+
+#endif /* __TURBOC__ */
+
+/* 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);
+}
+
+
+PTR
+ck_malloc (size)
+ size_t size;
+{
+ PTR ret;
+
+ if (!size)
+ size++;
+ ret = malloc (size);
+ if (ret == 0)
+ panic ("Couldn't allocate memory");
+ return ret;
+}
+
+/* Used by alloca.c and bison.simple. */
+char *
+xmalloc (size)
+ size_t size;
+{
+ return (char *) ck_malloc (size);
+}
+
+PTR
+ck_realloc (ptr, size)
+ PTR ptr;
+ size_t size;
+{
+ PTR ret;
+
+ 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 <ctype.h>
+
+/* 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;
+}
+
+#ifndef __MSDOS__
+void
+ck_pipe (pipes)
+ int *pipes;
+{
+ if (pipe (pipes) < 0)
+ {
+ msg_perror ("can't open a pipe");
+ exit (EX_SYSTEM);
+ }
+}
+#endif /* !__MSDOS__ */
+
+#ifndef HAVE_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;
+
+ 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 /* !HAVE_STRSTR */
+
+#ifndef HAVE_FTRUNCATE
+
+#ifdef F_CHSIZE
+int
+ftruncate (fd, length)
+ int fd;
+ off_t length;
+{
+ return fcntl (fd, F_CHSIZE, length);
+}
+
+#else /* !F_CHSIZE */
+#ifdef F_FREESP
+/* code courtesy of William Kucharski, kucharsk@Solbourne.com */
+
+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 /* !F_FREESP */
+
+int
+ftruncate (fd, length)
+ int fd;
+ off_t length;
+{
+ errno = EIO;
+ return -1;
+}
+
+#endif /* !F_FREESP */
+#endif /* !F_CHSIZE */
+#endif /* !HAVE_FTRUNCATE */
+
+
+extern FILE *msg_file;
+
+#if defined (HAVE_VPRINTF) && __STDC__
+#include <stdarg.h>
+
+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;
+
+ 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 /* HAVE_VPRINTF and __STDC__ */
+
+#if defined(HAVE_VPRINTF) && !__STDC__
+#include <varargs.h>
+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;
+
+ 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 /* HAVE_VPRINTF and not __STDC__ */
+
+#if !defined(HAVE_VPRINTF) && defined(HAVE_DOPRNT)
+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 args;
+{
+ int save_e;
+
+ 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 /* !HAVE_VPRINTF and HAVE_DOPRNT */
+
+#if !defined(HAVE_VPRINTF) && !defined(HAVE_DOPRNT)
+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;
+
+ 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 /* !HAVE_VPRINTF and !HAVE_DOPRNT */
diff --git a/gnu/usr.bin/tar/port.h b/gnu/usr.bin/tar/port.h
new file mode 100644
index 000000000000..4e65a9ace886
--- /dev/null
+++ b/gnu/usr.bin/tar/port.h
@@ -0,0 +1,215 @@
+/* Portability declarations. Requires sys/types.h.
+ Copyright (C) 1988, 1992 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 2, 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. */
+
+/* AIX requires this to be the first thing in the file. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not HAVE_ALLOCA_H */
+#ifdef _AIX
+ #pragma alloca
+#else /* not _AIX */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */
+#endif /* not __GNUC__ */
+
+#include "pathmax.h"
+
+#ifdef _POSIX_VERSION
+#include <sys/wait.h>
+#else /* !_POSIX_VERSION */
+#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
+#define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0)
+#define WIFEXITED(w) (((w) & 0xff) == 0)
+
+#define WSTOPSIG(w) (((w) >> 8) & 0xff)
+#define WTERMSIG(w) ((w) & 0x7f)
+#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
+#endif /* _POSIX_VERSION */
+
+/* nonstandard */
+#ifndef WIFCOREDUMPED
+#define WIFCOREDUMPED(w) (((w) & 0x80) != 0)
+#endif
+
+#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)
+typedef long off_t;
+#endif /* __MSDOS__ */
+
+#if defined(__STDC__) || defined(__TURBOC__)
+#define PTR void *
+#else
+#define PTR char *
+#define const
+#endif
+
+/* Since major is a function on SVR4, we can't just use `ifndef major'. */
+#ifdef major /* Might be defined in sys/types.h. */
+#define HAVE_MAJOR
+#endif
+
+#if !defined(HAVE_MAJOR) && defined(MAJOR_IN_MKDEV)
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#endif
+
+#if !defined(HAVE_MAJOR) && defined(MAJOR_IN_SYSMACROS)
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+
+#ifndef HAVE_MAJOR
+#define major(dev) (((dev) >> 8) & 0xff)
+#define minor(dev) ((dev) & 0xff)
+#define makedev(maj, min) (((maj) << 8) | (min))
+#endif
+#undef HAVE_MAJOR
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#if !defined(__MSDOS__) && !defined(STDC_HEADERS)
+#include <memory.h>
+#endif
+#ifdef index
+#undef index
+#endif
+#ifdef rindex
+#undef rindex
+#endif
+#define index strchr
+#define rindex strrchr
+#define bcopy(s, d, n) memcpy(d, s, n)
+#define bzero(s, n) memset(s, 0, n)
+#define bcmp memcmp
+#else
+#include <strings.h>
+#endif
+
+#if defined(STDC_HEADERS)
+#include <stdlib.h>
+#else
+char *malloc (), *realloc ();
+char *getenv ();
+#endif
+
+#ifndef _POSIX_VERSION
+#ifdef __MSDOS__
+#include <io.h>
+#else /* !__MSDOS__ */
+off_t lseek ();
+#endif /* !__MSDOS__ */
+char *getcwd ();
+#endif /* !_POSIX_VERSION */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CREAT
+#define O_CREAT 0
+#endif
+#ifndef O_NDELAY
+#define O_NDELAY 0
+#endif
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+#ifndef O_RDWR
+#define O_RDWR 2
+#endif
+
+#include <sys/stat.h>
+#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */
+#define mode_t unsigned short
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
+#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
+#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#endif
+#if !defined(S_ISCTG) && defined(S_IFCTG) /* contiguous file */
+#define S_ISCTG(m) (((m) & S_IFMT) == S_IFCTG)
+#endif
+#if !defined(S_ISVTX)
+#define S_ISVTX 0001000
+#endif
+
+#ifdef __MSDOS__
+#include "msd_dir.h"
+#define NLENGTH(direct) ((direct)->d_namlen)
+
+#else /* not __MSDOS__ */
+
+#if defined(DIRENT) || defined(_POSIX_VERSION)
+#include <dirent.h>
+#define NLENGTH(direct) (strlen((direct)->d_name))
+#else /* not (DIRENT or _POSIX_VERSION) */
+#define dirent direct
+#define NLENGTH(direct) ((direct)->d_namlen)
+#ifdef SYSNDIR
+#include <sys/ndir.h>
+#endif /* SYSNDIR */
+#ifdef SYSDIR
+#include <sys/dir.h>
+#endif /* SYSDIR */
+#ifdef NDIR
+#include <ndir.h>
+#endif /* NDIR */
+#endif /* DIRENT or _POSIX_VERSION */
+
+#endif /* not __MSDOS__ */
diff --git a/gnu/usr.bin/tar/regex.c b/gnu/usr.bin/tar/regex.c
new file mode 100644
index 000000000000..cb94d597c6f7
--- /dev/null
+++ b/gnu/usr.bin/tar/regex.c
@@ -0,0 +1,4932 @@
+/* Extended regular expression matching and search library,
+ version 0.11.
+ (Implements POSIX draft P10003.2/D11.2, except for
+ internationalization features.)
+
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+ #pragma alloca
+#endif
+
+#define _GNU_SOURCE
+
+/* We need this for `regex.h', and perhaps for the Emacs include files. */
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* The `emacs' switch turns on certain matching commands
+ that make sense only in Emacs. */
+#ifdef emacs
+
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+/* Emacs uses `NULL' as a predicate. */
+#undef NULL
+
+#else /* not emacs */
+
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+ `BSTRING', as far as I know, and neither of them use this code. */
+#if HAVE_STRING_H || STDC_HEADERS
+#include <string.h>
+#ifndef bcmp
+#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n) memset ((s), 0, (n))
+#endif
+#else
+#include <strings.h>
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+#endif
+
+
+/* Define the syntax stuff for \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match_2. */
+#ifndef Sword
+#define Sword 1
+#endif
+
+#ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set. */
+#define CHAR_SET_SIZE 256
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ bzero (re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ re_syntax_table['_'] = Sword;
+
+ done = 1;
+}
+
+#endif /* not SYNTAX_TABLE */
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+
+/* Get the interface, including the syntax bits. */
+#include "regex.h"
+
+/* isalpha etc. are used for the character classes. */
+#include <ctype.h>
+
+#ifndef isascii
+#define isascii(c) 1
+#endif
+
+#ifdef isblank
+#define ISBLANK(c) (isascii (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (isascii (c) && isgraph (c))
+#else
+#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (isascii (c) && isprint (c))
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#define ISALNUM(c) (isascii (c) && isalnum (c))
+#define ISALPHA(c) (isascii (c) && isalpha (c))
+#define ISCNTRL(c) (isascii (c) && iscntrl (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
+#define ISPUNCT(c) (isascii (c) && ispunct (c))
+#define ISSPACE(c) (isascii (c) && isspace (c))
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+ since ours (we hope) works properly with all combinations of
+ machines, compilers, `char' and `unsigned char' argument types.
+ (Per Bothner suggested the basic approach.) */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+
+/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
+ use `alloca' instead of `malloc'. This is because using malloc in
+ re_search* or re_match* could cause memory leaks when C-g is used in
+ Emacs; also, malloc is slower and causes storage fragmentation. On
+ the other hand, malloc is more portable, and easier to debug.
+
+ Because we sometimes use alloca, some routines have to be macros,
+ not functions -- `alloca'-allocated space disappears at the end of the
+ function it is called in. */
+
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+
+#else /* not REGEX_MALLOC */
+
+/* Emacs already defines alloca, sometimes. */
+#ifndef alloca
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top. */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+#define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable. */
+#define REGEX_REALLOCATE(source, osize, nsize) \
+ (destination = (char *) alloca (nsize), \
+ bcopy (source, destination, osize), \
+ destination)
+
+#endif /* not REGEX_MALLOC */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+ `string1' or just past its end. This works if PTR is NULL, which is
+ a good thing. */
+#define FIRST_STRING_P(ptr) \
+ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail. */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits. */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+/* These are the command codes that appear in compiled regular
+ expressions. Some opcodes are followed by argument bytes. A
+ command code can specify any interpretation whatsoever for its
+ arguments. Zero bytes may appear in the compiled regular expression.
+
+ 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. */
+
+typedef enum
+{
+ no_op = 0,
+
+ /* Followed by one byte giving n, then by n literal bytes. */
+ exactn = 1,
+
+ /* Matches any (more or less) character. */
+ anychar,
+
+ /* Matches any one char belonging to specified set. First
+ following byte is number of bitmap bytes. Then come bytes
+ for a bitmap saying which chars are in. Bits in each byte
+ are ordered low-bit-first. A character is in the set if its
+ bit is 1. A character too large to have a bit in the map is
+ automatically not in the set. */
+ charset,
+
+ /* Same parameters as charset, but match any character that is
+ not one of those specified. */
+ charset_not,
+
+ /* Start remembering the text that is matched, for storing in a
+ register. Followed by one byte with the register number, in
+ the range 0 to one less than the pattern buffer's re_nsub
+ field. Then followed by one byte with the number of groups
+ inner to this one. (This last has to be part of the
+ start_memory only because we need it in the on_failure_jump
+ of re_match_2.) */
+ start_memory,
+
+ /* Stop remembering the text that is matched and store it in a
+ memory register. Followed by one byte with the register
+ number, in the range 0 to one less than `re_nsub' in the
+ pattern buffer, and one byte with the number of inner groups,
+ just like `start_memory'. (We need the number of inner
+ groups here because we don't have any easy way of finding the
+ corresponding start_memory when we're at a stop_memory.) */
+ stop_memory,
+
+ /* Match a duplicate of something remembered. Followed by one
+ byte containing the register number. */
+ duplicate,
+
+ /* Fail unless at beginning of line. */
+ begline,
+
+ /* Fail unless at end of line. */
+ endline,
+
+ /* Succeeds if at beginning of buffer (if emacs) or at beginning
+ of string to be matched (if not). */
+ begbuf,
+
+ /* Analogously, for end of buffer/string. */
+ endbuf,
+
+ /* Followed by two byte relative address to which to jump. */
+ jump,
+
+ /* Same as jump, but marks the end of an alternative. */
+ jump_past_alt,
+
+ /* Followed by two-byte relative address of place to resume at
+ in case of failure. */
+ on_failure_jump,
+
+ /* Like on_failure_jump, but pushes a placeholder instead of the
+ current string position when executed. */
+ on_failure_keep_string_jump,
+
+ /* Throw away latest failure point and then jump to following
+ two-byte relative address. */
+ pop_failure_jump,
+
+ /* Change to pop_failure_jump if know won't have to backtrack to
+ match; otherwise change to jump. This is used to jump
+ back to the beginning of a repeat. If what follows this jump
+ clearly won't match what the repeat does, such that we can be
+ sure that there is no use backtracking out of repetitions
+ already matched, then we change it to a pop_failure_jump.
+ Followed by two-byte address. */
+ maybe_pop_jump,
+
+ /* Jump to following two-byte address, and push a dummy failure
+ point. This failure point will be thrown away if an attempt
+ is made to use it for a failure. A `+' construct makes this
+ before the first repeat. Also used as an intermediary kind
+ of jump when compiling an alternative. */
+ dummy_failure_jump,
+
+ /* Push a dummy failure point and continue. Used at the end of
+ alternatives. */
+ push_dummy_failure,
+
+ /* Followed by two-byte relative address and two-byte number n.
+ After matching N times, jump to the address upon failure. */
+ succeed_n,
+
+ /* Followed by two-byte relative address, and two-byte number n.
+ Jump to the address N times, then fail. */
+ jump_n,
+
+ /* Set the following two-byte relative address to the
+ subsequent two-byte number. The address *includes* the two
+ bytes of number. */
+ set_number_at,
+
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound /* Succeeds if not at a word boundary. */
+
+#ifdef emacs
+ ,before_dot, /* Succeeds if before point. */
+ at_dot, /* Succeeds if at point. */
+ after_dot, /* Succeeds if after point. */
+
+ /* Matches any character whose syntax is specified. Followed by
+ a byte which contains a syntax code, e.g., Sword. */
+ syntaxspec,
+
+ /* Matches any character whose syntax is not that specified. */
+ notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+
+/* Common operations on the compiled pattern. */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
+
+#define STORE_NUMBER(destination, number) \
+ do { \
+ (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; \
+ } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+ the byte after where the number is stored. Therefore, DESTINATION
+ must be an lvalue. */
+
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ do { \
+ STORE_NUMBER (destination, number); \
+ (destination) += 2; \
+ } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+ at SOURCE. */
+
+#define EXTRACT_NUMBER(destination, source) \
+ do { \
+ (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
+ } while (0)
+
+#ifdef DEBUG
+static void
+extract_number (dest, source)
+ int *dest;
+ unsigned char *source;
+{
+ int temp = SIGN_EXTEND_CHAR (*(source + 1));
+ *dest = *source & 0377;
+ *dest += temp << 8;
+}
+
+#ifndef EXTRACT_MACROS /* To debug the macros. */
+#undef EXTRACT_NUMBER
+#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+ SOURCE must be an lvalue. */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ do { \
+ EXTRACT_NUMBER (destination, source); \
+ (source) += 2; \
+ } while (0)
+
+#ifdef DEBUG
+static void
+extract_number_and_incr (destination, source)
+ int *destination;
+ unsigned char **source;
+{
+ extract_number (destination, *source);
+ *source += 2;
+}
+
+#ifndef EXTRACT_MACROS
+#undef EXTRACT_NUMBER_AND_INCR
+#define EXTRACT_NUMBER_AND_INCR(dest, src) \
+ extract_number_and_incr (&dest, &src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+ it is doing (if the variable `debug' is nonzero). If linked with the
+ main program in `iregex.c', you can enter patterns and strings
+ interactively. And if linked with the main program in `main.c' and
+ the other test files, you can run the already-written tests. */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging. */
+#include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging. */
+#include <assert.h>
+
+static int debug = 0;
+
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \
+ if (debug) print_partial_compiled_pattern (s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \
+ if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+extern void printchar ();
+
+/* Print the fastmap in human-readable form. */
+
+void
+print_fastmap (fastmap)
+ char *fastmap;
+{
+ unsigned was_a_range = 0;
+ unsigned i = 0;
+
+ while (i < (1 << BYTEWIDTH))
+ {
+ if (fastmap[i++])
+ {
+ was_a_range = 0;
+ printchar (i - 1);
+ while (i < (1 << BYTEWIDTH) && fastmap[i])
+ {
+ was_a_range = 1;
+ i++;
+ }
+ if (was_a_range)
+ {
+ printf ("-");
+ printchar (i - 1);
+ }
+ }
+ }
+ putchar ('\n');
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+ the START pointer into it and ending just before the pointer END. */
+
+void
+print_partial_compiled_pattern (start, end)
+ unsigned char *start;
+ unsigned char *end;
+{
+ int mcnt, mcnt2;
+ unsigned char *p = start;
+ unsigned char *pend = end;
+
+ if (start == NULL)
+ {
+ printf ("(null)\n");
+ return;
+ }
+
+ /* Loop over pattern commands. */
+ while (p < pend)
+ {
+ switch ((re_opcode_t) *p++)
+ {
+ case no_op:
+ printf ("/no_op");
+ break;
+
+ case exactn:
+ mcnt = *p++;
+ printf ("/exactn/%d", mcnt);
+ do
+ {
+ putchar ('/');
+ printchar (*p++);
+ }
+ while (--mcnt);
+ break;
+
+ case start_memory:
+ mcnt = *p++;
+ printf ("/start_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case stop_memory:
+ mcnt = *p++;
+ printf ("/stop_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case duplicate:
+ printf ("/duplicate/%d", *p++);
+ break;
+
+ case anychar:
+ printf ("/anychar");
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ register int c;
+
+ printf ("/charset%s",
+ (re_opcode_t) *(p - 1) == charset_not ? "_not" : "");
+
+ assert (p + *p < pend);
+
+ for (c = 0; c < *p; c++)
+ {
+ unsigned bit;
+ unsigned char map_byte = p[1 + c];
+
+ putchar ('/');
+
+ for (bit = 0; bit < BYTEWIDTH; bit++)
+ if (map_byte & (1 << bit))
+ printchar (c * BYTEWIDTH + bit);
+ }
+ p += 1 + *p;
+ break;
+ }
+
+ case begline:
+ printf ("/begline");
+ break;
+
+ case endline:
+ printf ("/endline");
+ break;
+
+ case on_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_jump/0/%d", mcnt);
+ break;
+
+ case on_failure_keep_string_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_keep_string_jump/0/%d", mcnt);
+ break;
+
+ case dummy_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/dummy_failure_jump/0/%d", mcnt);
+ break;
+
+ case push_dummy_failure:
+ printf ("/push_dummy_failure");
+ break;
+
+ case maybe_pop_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/maybe_pop_jump/0/%d", mcnt);
+ break;
+
+ case pop_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/pop_failure_jump/0/%d", mcnt);
+ break;
+
+ case jump_past_alt:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump_past_alt/0/%d", mcnt);
+ break;
+
+ case jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump/0/%d", mcnt);
+ break;
+
+ case succeed_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case jump_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case set_number_at:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case wordbound:
+ printf ("/wordbound");
+ break;
+
+ case notwordbound:
+ printf ("/notwordbound");
+ break;
+
+ case wordbeg:
+ printf ("/wordbeg");
+ break;
+
+ case wordend:
+ printf ("/wordend");
+
+#ifdef emacs
+ case before_dot:
+ printf ("/before_dot");
+ break;
+
+ case at_dot:
+ printf ("/at_dot");
+ break;
+
+ case after_dot:
+ printf ("/after_dot");
+ break;
+
+ case syntaxspec:
+ printf ("/syntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+
+ case notsyntaxspec:
+ printf ("/notsyntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+#endif /* emacs */
+
+ case wordchar:
+ printf ("/wordchar");
+ break;
+
+ case notwordchar:
+ printf ("/notwordchar");
+ break;
+
+ case begbuf:
+ printf ("/begbuf");
+ break;
+
+ case endbuf:
+ printf ("/endbuf");
+ break;
+
+ default:
+ printf ("?%d", *(p-1));
+ }
+ }
+ printf ("/\n");
+}
+
+
+void
+print_compiled_pattern (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *buffer = bufp->buffer;
+
+ print_partial_compiled_pattern (buffer, buffer + bufp->used);
+ printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
+
+ if (bufp->fastmap_accurate && bufp->fastmap)
+ {
+ printf ("fastmap: ");
+ print_fastmap (bufp->fastmap);
+ }
+
+ printf ("re_nsub: %d\t", bufp->re_nsub);
+ printf ("regs_alloc: %d\t", bufp->regs_allocated);
+ printf ("can_be_null: %d\t", bufp->can_be_null);
+ printf ("newline_anchor: %d\n", bufp->newline_anchor);
+ printf ("no_sub: %d\t", bufp->no_sub);
+ printf ("not_bol: %d\t", bufp->not_bol);
+ printf ("not_eol: %d\t", bufp->not_eol);
+ printf ("syntax: %d\n", bufp->syntax);
+ /* Perhaps we should print the translate table? */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+ const char *where;
+ const char *string1;
+ const char *string2;
+ int size1;
+ int size2;
+{
+ unsigned this_char;
+
+ if (where == NULL)
+ printf ("(null)");
+ else
+ {
+ if (FIRST_STRING_P (where))
+ {
+ for (this_char = where - string1; this_char < size1; this_char++)
+ printchar (string1[this_char]);
+
+ where = string2;
+ }
+
+ for (this_char = where - string2; this_char < size2; this_char++)
+ printchar (string2[this_char]);
+ }
+}
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there. */
+
+static const char *re_error_msg[] =
+ { NULL, /* REG_NOERROR */
+ "No match", /* REG_NOMATCH */
+ "Invalid regular expression", /* REG_BADPAT */
+ "Invalid collation character", /* REG_ECOLLATE */
+ "Invalid character class name", /* REG_ECTYPE */
+ "Trailing backslash", /* REG_EESCAPE */
+ "Invalid back reference", /* REG_ESUBREG */
+ "Unmatched [ or [^", /* REG_EBRACK */
+ "Unmatched ( or \\(", /* REG_EPAREN */
+ "Unmatched \\{", /* REG_EBRACE */
+ "Invalid content of \\{\\}", /* REG_BADBR */
+ "Invalid range end", /* REG_ERANGE */
+ "Memory exhausted", /* REG_ESPACE */
+ "Invalid preceding regular expression", /* REG_BADRPT */
+ "Premature end of regular expression", /* REG_EEND */
+ "Regular expression too big", /* REG_ESIZE */
+ "Unmatched ) or \\)", /* REG_ERPAREN */
+ };
+
+/* Subroutine declarations and macros for regex_compile. */
+
+static void store_op1 (), store_op2 ();
+static void insert_op1 (), insert_op2 ();
+static boolean at_begline_loc_p (), at_endline_loc_p ();
+static boolean group_in_compile_stack ();
+static reg_errcode_t compile_range ();
+
+/* Fetch the next character in the uncompiled pattern---translating it
+ if necessary. Also cast from a signed character in the constant
+ string passed to us by the user to an unsigned char that we can use
+ as an array index (in, e.g., `translate'). */
+#define PATFETCH(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ if (translate) c = translate[c]; \
+ } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D. We
+ cast the subscript to translate because some data is declared as
+ `char *', to avoid warnings when a string constant is passed. But
+ when we use a character as a subscript we must make it unsigned. */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'. */
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 32
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ while (b - bufp->buffer + (n) > bufp->allocated) \
+ EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it. */
+#define BUF_PUSH(c) \
+ do { \
+ GET_BUFFER_SPACE (1); \
+ *b++ = (unsigned char) (c); \
+ } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
+#define BUF_PUSH_2(c1, c2) \
+ do { \
+ GET_BUFFER_SPACE (2); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes. */
+#define BUF_PUSH_3(c1, c2, c3) \
+ do { \
+ GET_BUFFER_SPACE (3); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ *b++ = (unsigned char) (c3); \
+ } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO. We store a
+ relative address offset by the three bytes the jump itself occupies. */
+#define STORE_JUMP(op, loc, to) \
+ store_op1 (op, loc, (to) - (loc) - 3)
+
+/* Likewise, for a two-argument jump. */
+#define STORE_JUMP2(op, loc, to, arg) \
+ store_op2 (op, loc, (to) - (loc) - 3, arg)
+
+/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP(op, loc, to) \
+ insert_op1 (op, loc, (to) - (loc) - 3, b)
+
+/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP2(op, loc, to, arg) \
+ insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+ into the pattern are two bytes long. So if 2^16 bytes turns out to
+ be too small, many things would have to change. */
+#define MAX_BUF_SIZE (1L << 16)
+
+
+/* Extend the buffer by twice its current size via realloc and
+ reset the pointers that pointed into the old block to point to the
+ correct places in the new one. If extending the buffer results in it
+ being larger than MAX_BUF_SIZE, then flag memory exhausted. */
+#define EXTEND_BUFFER() \
+ do { \
+ unsigned char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == MAX_BUF_SIZE) \
+ return REG_ESIZE; \
+ bufp->allocated <<= 1; \
+ if (bufp->allocated > MAX_BUF_SIZE) \
+ bufp->allocated = MAX_BUF_SIZE; \
+ bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
+ if (bufp->buffer == NULL) \
+ return REG_ESPACE; \
+ /* If the buffer moved, move all the pointers into it. */ \
+ if (old_buffer != bufp->buffer) \
+ { \
+ b = (b - old_buffer) + bufp->buffer; \
+ begalt = (begalt - old_buffer) + bufp->buffer; \
+ if (fixup_alt_jump) \
+ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+ if (laststart) \
+ laststart = (laststart - old_buffer) + bufp->buffer; \
+ if (pending_exact) \
+ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+ } \
+ } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+ {start,stop}_memory, the maximum number of groups we can report
+ things about is what fits in that byte. */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers. We just
+ ignore the excess. */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack. */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+ be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+ pattern_offset_t begalt_offset;
+ pattern_offset_t fixup_alt_jump;
+ pattern_offset_t inner_group_offset;
+ pattern_offset_t laststart_offset;
+ regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+ compile_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
+
+/* The next available element. */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list. */
+#define SET_LIST_BIT(c) \
+ (b[((unsigned char) (c)) / BYTEWIDTH] \
+ |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while (ISDIGIT (c)) \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
+
+#define IS_CHAR_CLASS(string) \
+ (STREQ (string, "alpha") || STREQ (string, "upper") \
+ || STREQ (string, "lower") || STREQ (string, "digit") \
+ || STREQ (string, "alnum") || STREQ (string, "xdigit") \
+ || STREQ (string, "space") || STREQ (string, "print") \
+ || STREQ (string, "punct") || STREQ (string, "graph") \
+ || STREQ (string, "cntrl") || STREQ (string, "blank"))
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+ Returns one of error codes defined in `regex.h', or zero for success.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate'
+ fields are set in BUFP on entry.
+
+ If it succeeds, results are put in BUFP (if it returns an error, the
+ contents of BUFP are undefined):
+ `buffer' is the compiled pattern;
+ `syntax' is set to SYNTAX;
+ `used' is set to the length of the compiled pattern;
+ `fastmap_accurate' is zero;
+ `re_nsub' is the number of subexpressions in PATTERN;
+ `not_bol' and `not_eol' are zero;
+
+ The `fastmap' and `newline_anchor' fields are neither
+ examined nor set. */
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+ const char *pattern;
+ int size;
+ reg_syntax_t syntax;
+ struct re_pattern_buffer *bufp;
+{
+ /* We fetch characters from PATTERN here. Even though PATTERN is
+ `char *' (i.e., signed), we declare these variables as unsigned, so
+ they can be reliably used as array indices. */
+ register unsigned char c, c1;
+
+ /* A random tempory spot in PATTERN. */
+ const char *p1;
+
+ /* Points to the end of the buffer, where we should append. */
+ register unsigned char *b;
+
+ /* Keeps track of unclosed groups. */
+ compile_stack_type compile_stack;
+
+ /* Points to the current (ending) position in the pattern. */
+ const char *p = pattern;
+ const char *pend = pattern + size;
+
+ /* How to translate the characters in the pattern. */
+ char *translate = bufp->translate;
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell if a new exact-match
+ character can be added to that command or if the character requires
+ a new `exactn' command. */
+ unsigned char *pending_exact = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells, e.g., postfix * where to find the start of its
+ operand. Reset at the beginning of groups and alternatives. */
+ unsigned char *laststart = 0;
+
+ /* Address of beginning of regexp, or inside of last group. */
+ unsigned char *begalt;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ which to go back if the interval is invalid. */
+ const char *beg_interval;
+
+ /* Address of the place where a forward jump should go to the end of
+ the containing expression. Each alternative of an `or' -- except the
+ last -- ends with a forward jump of this sort. */
+ unsigned char *fixup_alt_jump = 0;
+
+ /* Counts open-groups as they are encountered. Remembered for the
+ matching close-group on the compile stack, so the same register
+ number is put in the stop_memory as the start_memory. */
+ regnum_t regnum = 0;
+
+#ifdef DEBUG
+ DEBUG_PRINT1 ("\nCompiling pattern: ");
+ if (debug)
+ {
+ unsigned debug_count;
+
+ for (debug_count = 0; debug_count < size; debug_count++)
+ printchar (pattern[debug_count]);
+ putchar ('\n');
+ }
+#endif /* DEBUG */
+
+ /* Initialize the compile stack. */
+ compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+ if (compile_stack.stack == NULL)
+ return REG_ESPACE;
+
+ compile_stack.size = INIT_COMPILE_STACK_SIZE;
+ compile_stack.avail = 0;
+
+ /* Initialize the pattern buffer. */
+ bufp->syntax = syntax;
+ bufp->fastmap_accurate = 0;
+ bufp->not_bol = bufp->not_eol = 0;
+
+ /* Set `used' to zero, so that if we return an error, the pattern
+ printer (for debugging) will think there's no pattern. We reset it
+ at the end. */
+ bufp->used = 0;
+
+ /* Always count groups, whether or not bufp->no_sub is set. */
+ bufp->re_nsub = 0;
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+ /* Initialize the syntax table. */
+ init_syntax_once ();
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ if (bufp->buffer)
+ { /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. */
+ RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+ }
+ else
+ { /* Caller did not allocate a buffer. Do it for them. */
+ bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+ }
+ if (!bufp->buffer) return REG_ESPACE;
+
+ bufp->allocated = INIT_BUF_SIZE;
+ }
+
+ begalt = b = bufp->buffer;
+
+ /* Loop through the uncompiled pattern until we're at the end. */
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '^':
+ {
+ if ( /* If at start of pattern, it's an operator. */
+ p == pattern + 1
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's come before. */
+ || at_begline_loc_p (pattern, p, syntax))
+ BUF_PUSH (begline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '$':
+ {
+ if ( /* If at end of pattern, it's an operator. */
+ p == pend
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's next. */
+ || at_endline_loc_p (p, pend, syntax))
+ BUF_PUSH (endline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '+':
+ case '?':
+ if ((syntax & RE_BK_PLUS_QM)
+ || (syntax & RE_LIMITED_OPS))
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern... */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+
+ {
+ /* Are we optimizing this jump? */
+ boolean keep_string_p = false;
+
+ /* 1 means zero (many) matches is allowed. */
+ char zero_times_ok = 0, many_times_ok = 0;
+
+ /* If there is a sequence of repetition chars, collapse it
+ down to just one (the right one). We can't combine
+ interval operators with these because of, e.g., `a{2}*',
+ which should only match an even number of `a's. */
+
+ for (;;)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+
+ if (p == pend)
+ break;
+
+ PATFETCH (c);
+
+ if (c == '*'
+ || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+ ;
+
+ else if (syntax & RE_BK_PLUS_QM && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+
+ /* If we get here, we found another repeat character. */
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok)
+ { /* More than one repetition is allowed, so put in at the
+ end a backward relative jump from `b' to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump).
+
+ But if we are at the `*' in the exact sequence `.*\n',
+ insert an unconditional jump backwards to the .,
+ instead of the beginning of the loop. This way we only
+ push a failure point once, instead of every time
+ through the loop. */
+ assert (p - 1 > pattern);
+
+ /* Allocate the space for the jump. */
+ GET_BUFFER_SPACE (3);
+
+ /* We know we are not at the first character of the pattern,
+ because laststart was nonzero. And we've already
+ incremented `p', by the way, to be the character after
+ the `*'. Do we have to do something analogous here
+ for null bytes, because of RE_DOT_NOT_NULL? */
+ if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+ && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+ && !(syntax & RE_DOT_NEWLINE))
+ { /* We have .*\n. */
+ STORE_JUMP (jump, b, laststart);
+ keep_string_p = true;
+ }
+ else
+ /* Anything else. */
+ STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+ /* We've added more stuff to the buffer. */
+ b += 3;
+ }
+
+ /* On failure, jump from laststart to b + 3, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+ : on_failure_jump,
+ laststart, b + 3);
+ pending_exact = 0;
+ b += 3;
+
+ if (!zero_times_ok)
+ {
+ /* At least one repetition is required, so insert a
+ `dummy_failure_jump' before the initial
+ `on_failure_jump' instruction of the loop. This
+ effects a skip over that instruction the first time
+ we hit that loop. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+ b += 3;
+ }
+ }
+ break;
+
+
+ case '.':
+ laststart = b;
+ BUF_PUSH (anychar);
+ break;
+
+
+ case '[':
+ {
+ boolean had_char_class = false;
+
+ if (p == pend) return REG_EBRACK;
+
+ /* Ensure that we have enough space to push a charset: the
+ opcode, the length count, and the bitset; 34 bytes in all. */
+ GET_BUFFER_SPACE (34);
+
+ laststart = b;
+
+ /* We test `*p == '^' twice, instead of using an if
+ statement, so we only need one BUF_PUSH. */
+ BUF_PUSH (*p == '^' ? charset_not : charset);
+ if (*p == '^')
+ p++;
+
+ /* Remember the first position in the bracket expression. */
+ p1 = p;
+
+ /* Push the number of bytes in the bitmap. */
+ BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* Clear the whole map. */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* charset_not matches newline according to a syntax bit. */
+ if ((re_opcode_t) b[-2] == charset_not
+ && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+ SET_LIST_BIT ('\n');
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;)
+ {
+ if (p == pend) return REG_EBRACK;
+
+ PATFETCH (c);
+
+ /* \ might escape characters inside [...] and [^...]. */
+ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ SET_LIST_BIT (c1);
+ continue;
+ }
+
+ /* Could be the end of the bracket expression. If it's
+ not (i.e., when the bracket expression is `[]' so
+ far), the ']' character bit gets set way below. */
+ if (c == ']' && p != p1 + 1)
+ break;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (had_char_class && c == '-' && *p != ']')
+ return REG_ERANGE;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character: if this is a hyphen not at the
+ beginning or the end of a list, then it's the range
+ operator. */
+ if (c == '-'
+ && !(p - 2 >= pattern && p[-2] == '[')
+ && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+ && *p != ']')
+ {
+ reg_errcode_t ret
+ = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ else if (p[0] == '-' && p[1] != ']')
+ { /* This handles ranges made up of characters only. */
+ reg_errcode_t ret;
+
+ /* Move past the `-'. */
+ PATFETCH (c1);
+
+ ret = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ /* See if we're at the beginning of a possible character
+ class. */
+
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+ { /* Leave room for the null. */
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend) return REG_EBRACK;
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and:`]':
+ undo the ending character, the letters, and leave
+ the leading `:' and `[' (but set bits for them). */
+ if (c == ':' && *p == ']')
+ {
+ int ch;
+ boolean is_alnum = STREQ (str, "alnum");
+ boolean is_alpha = STREQ (str, "alpha");
+ boolean is_blank = STREQ (str, "blank");
+ boolean is_cntrl = STREQ (str, "cntrl");
+ boolean is_digit = STREQ (str, "digit");
+ boolean is_graph = STREQ (str, "graph");
+ boolean is_lower = STREQ (str, "lower");
+ boolean is_print = STREQ (str, "print");
+ boolean is_punct = STREQ (str, "punct");
+ boolean is_space = STREQ (str, "space");
+ boolean is_upper = STREQ (str, "upper");
+ boolean is_xdigit = STREQ (str, "xdigit");
+
+ if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) return REG_EBRACK;
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+ {
+ if ( (is_alnum && ISALNUM (ch))
+ || (is_alpha && ISALPHA (ch))
+ || (is_blank && ISBLANK (ch))
+ || (is_cntrl && ISCNTRL (ch))
+ || (is_digit && ISDIGIT (ch))
+ || (is_graph && ISGRAPH (ch))
+ || (is_lower && ISLOWER (ch))
+ || (is_print && ISPRINT (ch))
+ || (is_punct && ISPUNCT (ch))
+ || (is_space && ISSPACE (ch))
+ || (is_upper && ISUPPER (ch))
+ || (is_xdigit && ISXDIGIT (ch)))
+ SET_LIST_BIT (ch);
+ }
+ had_char_class = true;
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+ had_char_class = false;
+ }
+ }
+ else
+ {
+ had_char_class = false;
+ SET_LIST_BIT (c);
+ }
+ }
+
+ /* Discard any (non)matching list bytes that are all 0 at the
+ end of the map. Decrease the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ }
+ break;
+
+
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_open;
+ else
+ goto normal_char;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_close;
+ else
+ goto normal_char;
+
+
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '|':
+ if (syntax & RE_NO_BK_VBAR)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '{':
+ if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+ goto handle_interval;
+ else
+ goto normal_char;
+
+
+ case '\\':
+ if (p == pend) return REG_EESCAPE;
+
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW (c);
+
+ switch (c)
+ {
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto normal_backslash;
+
+ handle_open:
+ bufp->re_nsub++;
+ regnum++;
+
+ if (COMPILE_STACK_FULL)
+ {
+ RETALLOC (compile_stack.stack, compile_stack.size << 1,
+ compile_stack_elt_t);
+ if (compile_stack.stack == NULL) return REG_ESPACE;
+
+ compile_stack.size <<= 1;
+ }
+
+ /* These are the values to restore when we hit end of this
+ group. They are all relative offsets, so that if the
+ whole pattern moves because of realloc, they will still
+ be valid. */
+ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+ COMPILE_STACK_TOP.fixup_alt_jump
+ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+ COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+ COMPILE_STACK_TOP.regnum = regnum;
+
+ /* We will eventually replace the 0 with the number of
+ groups inner to this one. But do not push a
+ start_memory for groups beyond the last one we can
+ represent in the compiled pattern. */
+ if (regnum <= MAX_REGNUM)
+ {
+ COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+ BUF_PUSH_3 (start_memory, regnum, 0);
+ }
+
+ compile_stack.avail++;
+
+ fixup_alt_jump = 0;
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_backslash;
+ else
+ return REG_ERPAREN;
+
+ handle_close:
+ if (fixup_alt_jump)
+ { /* Push a dummy failure point at the end of the
+ alternative for a possible future
+ `pop_failure_jump' to pop. See comments at
+ `push_dummy_failure' in `re_match_2'. */
+ BUF_PUSH (push_dummy_failure);
+
+ /* We allocated space for this jump when we assigned
+ to `fixup_alt_jump', in the `handle_alt' case below. */
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+ }
+
+ /* See similar code for backslashed left paren above. */
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ else
+ return REG_ERPAREN;
+
+ /* Since we just checked for an empty stack above, this
+ ``can't happen''. */
+ assert (compile_stack.avail != 0);
+ {
+ /* We don't just want to restore into `regnum', because
+ later groups should continue to be numbered higher,
+ as in `(ab)c(de)' -- the second group is #2. */
+ regnum_t this_group_regnum;
+
+ compile_stack.avail--;
+ begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+ fixup_alt_jump
+ = COMPILE_STACK_TOP.fixup_alt_jump
+ ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
+ : 0;
+ laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+ this_group_regnum = COMPILE_STACK_TOP.regnum;
+
+ /* We're at the end of the group, so now we know how many
+ groups were inside this one. */
+ if (this_group_regnum <= MAX_REGNUM)
+ {
+ unsigned char *inner_group_loc
+ = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+
+ *inner_group_loc = regnum - this_group_regnum;
+ BUF_PUSH_3 (stop_memory, this_group_regnum,
+ regnum - this_group_regnum);
+ }
+ }
+ break;
+
+
+ case '|': /* `\|'. */
+ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+ goto normal_backslash;
+ handle_alt:
+ if (syntax & RE_LIMITED_OPS)
+ goto normal_char;
+
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (on_failure_jump, begalt, b + 6);
+ pending_exact = 0;
+ b += 3;
+
+ /* The alternative before this one has a jump after it
+ which gets executed if it gets matched. Adjust that
+ jump so it will jump to this alternative's analogous
+ jump (put in below, which in turn will jump to the next
+ (if any) alternative's such jump, etc.). The last such
+ jump jumps to the correct final destination. A picture:
+ _____ _____
+ | | | |
+ | v | v
+ a | b | c
+
+ If we are at `b', then fixup_alt_jump right now points to a
+ three-byte space after `a'. We'll put in the jump, set
+ fixup_alt_jump to right after `b', and leave behind three
+ bytes which we'll fill in when we get to after `c'. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ /* Mark and leave space for a jump after this alternative,
+ to be filled in later either by next alternative or
+ when know we're at the end of a series of alternatives. */
+ fixup_alt_jump = b;
+ GET_BUFFER_SPACE (3);
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case '{':
+ /* If \{ is a literal. */
+ if (!(syntax & RE_INTERVALS)
+ /* If we're at `\{' and it's not the open-interval
+ operator. */
+ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ || (p - 2 == pattern && p == pend))
+ goto normal_backslash;
+
+ handle_interval:
+ {
+ /* If got here, then the syntax allows intervals. */
+
+ /* At least (most) this many matches must be made. */
+ int lower_bound = -1, upper_bound = -1;
+
+ beg_interval = p - 1;
+
+ if (p == pend)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_EBRACE;
+ }
+
+ GET_UNSIGNED_NUMBER (lower_bound);
+
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+ }
+ else
+ /* Interval such as `{1}' => match exactly once. */
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (c != '\\') return REG_EBRACE;
+
+ PATFETCH (c);
+ }
+
+ if (c != '}')
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ /* We just parsed a valid interval. */
+
+ /* If it's invalid to have no preceding re. */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ laststart = b;
+ else
+ goto unfetch_interval;
+ }
+
+ /* If the upper bound is zero, don't want to succeed at
+ all; jump from `laststart' to `b + 3', which will be
+ the end of the buffer after we insert the jump. */
+ if (upper_bound == 0)
+ {
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (jump, laststart, b + 3);
+ b += 3;
+ }
+
+ /* Otherwise, we have a nontrivial interval. When
+ we're all done, the pattern will look like:
+ set_number_at <jump count> <upper bound>
+ set_number_at <succeed_n count> <lower bound>
+ succeed_n <after jump addr> <succed_n count>
+ <body of loop>
+ jump_n <succeed_n addr> <jump count>
+ (The upper bound and `jump_n' are omitted if
+ `upper_bound' is 1, though.) */
+ else
+ { /* If the upper bound is > 1, we need to insert
+ more at the end of the loop. */
+ unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+ GET_BUFFER_SPACE (nbytes);
+
+ /* Initialize lower bound of the `succeed_n', even
+ though it will be set during matching by its
+ attendant `set_number_at' (inserted next),
+ because `re_compile_fastmap' needs to know.
+ Jump to the `jump_n' we might insert below. */
+ INSERT_JUMP2 (succeed_n, laststart,
+ b + 5 + (upper_bound > 1) * 5,
+ lower_bound);
+ b += 5;
+
+ /* Code to initialize the lower bound. Insert
+ before the `succeed_n'. The `5' is the last two
+ bytes of this `set_number_at', plus 3 bytes of
+ the following `succeed_n'. */
+ insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+ b += 5;
+
+ if (upper_bound > 1)
+ { /* More than one repetition is allowed, so
+ append a backward jump to the `succeed_n'
+ that starts this interval.
+
+ When we've reached this during matching,
+ we'll have matched the interval once, so
+ jump back only `upper_bound - 1' times. */
+ STORE_JUMP2 (jump_n, b, laststart + 5,
+ upper_bound - 1);
+ b += 5;
+
+ /* The location we want to set is the second
+ parameter of the `jump_n'; that is `b-2' as
+ an absolute address. `laststart' will be
+ the `set_number_at' we're about to insert;
+ `laststart+3' the number to set, the source
+ for the relative address. But we are
+ inserting into the middle of the pattern --
+ so everything is getting moved up by 5.
+ Conclusion: (b - 2) - (laststart + 3) + 5,
+ i.e., b - laststart.
+
+ We insert this at the beginning of the loop
+ so that if we fail during matching, we'll
+ reinitialize the bounds. */
+ insert_op2 (set_number_at, laststart, b - laststart,
+ upper_bound - 1, b);
+ b += 5;
+ }
+ }
+ pending_exact = 0;
+ beg_interval = NULL;
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ assert (beg_interval);
+ p = beg_interval;
+ beg_interval = NULL;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH (c);
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (p > pattern && p[-1] == '\\')
+ goto normal_backslash;
+ }
+ goto normal_char;
+
+#ifdef emacs
+ /* There is no way to specify the before_dot and after_dot
+ operators. rms says this is ok. --karl */
+ case '=':
+ BUF_PUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+
+ case 'w':
+ laststart = b;
+ BUF_PUSH (wordchar);
+ break;
+
+
+ case 'W':
+ laststart = b;
+ BUF_PUSH (notwordchar);
+ break;
+
+
+ case '<':
+ BUF_PUSH (wordbeg);
+ break;
+
+ case '>':
+ BUF_PUSH (wordend);
+ break;
+
+ case 'b':
+ BUF_PUSH (wordbound);
+ break;
+
+ case 'B':
+ BUF_PUSH (notwordbound);
+ break;
+
+ case '`':
+ BUF_PUSH (begbuf);
+ break;
+
+ case '\'':
+ BUF_PUSH (endbuf);
+ break;
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (syntax & RE_NO_BK_REFS)
+ goto normal_char;
+
+ c1 = c - '0';
+
+ if (c1 > regnum)
+ return REG_ESUBREG;
+
+ /* Can't back reference to a subexpression if inside of it. */
+ if (group_in_compile_stack (compile_stack, c1))
+ goto normal_char;
+
+ laststart = b;
+ BUF_PUSH_2 (duplicate, c1);
+ break;
+
+
+ case '+':
+ case '?':
+ if (syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backslash;
+
+ default:
+ normal_backslash:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ c = TRANSLATE (c);
+ goto normal_char;
+ }
+ break;
+
+
+ default:
+ /* Expects the character in `c'. */
+ normal_char:
+ /* If no exactn currently being built. */
+ if (!pending_exact
+
+ /* If last exactn not at current position. */
+ || pending_exact + *pending_exact + 1 != b
+
+ /* We have only one byte following the exactn for the count. */
+ || *pending_exact == (1 << BYTEWIDTH) - 1
+
+ /* If followed by a repetition operator. */
+ || *p == '*' || *p == '^'
+ || ((syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?'))
+ || ((syntax & RE_INTERVALS)
+ && ((syntax & RE_NO_BK_BRACES)
+ ? *p == '{'
+ : (p[0] == '\\' && p[1] == '{'))))
+ {
+ /* Start building a new exactn. */
+
+ laststart = b;
+
+ BUF_PUSH_2 (exactn, 0);
+ pending_exact = b - 1;
+ }
+
+ BUF_PUSH (c);
+ (*pending_exact)++;
+ break;
+ } /* switch (c) */
+ } /* while p != pend */
+
+
+ /* Through the pattern now. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ if (!COMPILE_STACK_EMPTY)
+ return REG_EPAREN;
+
+ free (compile_stack.stack);
+
+ /* We have succeeded; set the length of the buffer. */
+ bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+ if (debug)
+ {
+ DEBUG_PRINT1 ("\nCompiled pattern: ");
+ print_compiled_pattern (bufp);
+ }
+#endif /* DEBUG */
+
+ return REG_NOERROR;
+} /* regex_compile */
+
+/* Subroutines for `regex_compile'. */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG. */
+
+static void
+store_op1 (op, loc, arg)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg1);
+ STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+ for OP followed by two-byte integer parameter ARG. */
+
+static void
+insert_op1 (op, loc, arg, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 3;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 5;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN. Return true if that ^ comes
+ after an alternative or a begin-subexpression. We assume there is at
+ least one character before the ^. */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+ const char *pattern, *p;
+ reg_syntax_t syntax;
+{
+ const char *prev = p - 2;
+ boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+
+ return
+ /* After a subexpression? */
+ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+ /* After an alternative? */
+ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p. This one is for $. We assume there is
+ at least one character after the $, i.e., `P < PEND'. */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+ const char *p, *pend;
+ int syntax;
+{
+ const char *next = p;
+ boolean next_backslash = *next == '\\';
+ const char *next_next = p + 1 < pend ? p + 1 : NULL;
+
+ return
+ /* Before a subexpression? */
+ (syntax & RE_NO_BK_PARENS ? *next == ')'
+ : next_backslash && next_next && *next_next == ')')
+ /* Before an alternative? */
+ || (syntax & RE_NO_BK_VBAR ? *next == '|'
+ : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+ false if it's not. */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+ compile_stack_type compile_stack;
+ regnum_t regnum;
+{
+ int this_element;
+
+ for (this_element = compile_stack.avail - 1;
+ this_element >= 0;
+ this_element--)
+ if (compile_stack.stack[this_element].regnum == regnum)
+ return true;
+
+ return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+ uncompiled pattern *P_PTR (which ends at PEND). We assume the
+ starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
+ Then we set the translation of all bits between the starting and
+ ending characters (inclusive) in the compiled pattern B.
+
+ Return an error code.
+
+ We use these short variable names so we can use the same macros as
+ `regex_compile' itself. */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+ const char **p_ptr, *pend;
+ char *translate;
+ reg_syntax_t syntax;
+ unsigned char *b;
+{
+ unsigned this_char;
+
+ const char *p = *p_ptr;
+ int range_start, range_end;
+
+ if (p == pend)
+ return REG_ERANGE;
+
+ /* Even though the pattern is a signed `char *', we need to fetch
+ with unsigned char *'s; if the high bit of the pattern character
+ is set, the range endpoints will be negative if we fetch using a
+ signed char *.
+
+ We also want to fetch the endpoints without translating them; the
+ appropriate translation is done in the bit-setting loop below. */
+ range_start = ((unsigned char *) p)[-2];
+ range_end = ((unsigned char *) p)[0];
+
+ /* Have to increment the pointer into the pattern string, so the
+ caller isn't still at the ending character. */
+ (*p_ptr)++;
+
+ /* If the start is after the end, the range is empty. */
+ if (range_start > range_end)
+ return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+ /* Here we see why `this_char' has to be larger than an `unsigned
+ char' -- the range is inclusive, so if `range_end' == 0xff
+ (assuming 8-bit characters), we would otherwise go into an infinite
+ loop, since all characters <= 0xff. */
+ for (this_char = range_start; this_char <= range_end; this_char++)
+ {
+ SET_LIST_BIT (TRANSLATE (this_char));
+ }
+
+ return REG_NOERROR;
+}
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+ re_match_2 use a failure stack. These have to be macros because of
+ REGEX_ALLOCATE. */
+
+
+/* Number of failure points for which to initially allocate space
+ when matching. If this number is exceeded, we allocate more
+ space, so it is not a hard limit. */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always used MAX_FAILURE_SPACE each time we failed.
+ This is a variable only so users of regex can assign to it; we never
+ change it ourselves. */
+int re_max_failures = 2000;
+
+typedef const unsigned char *fail_stack_elt_t;
+
+typedef struct
+{
+ fail_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
+#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
+
+
+/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
+
+#define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.stack = (fail_stack_elt_t *) \
+ REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+ \
+ if (fail_stack.stack == NULL) \
+ return -2; \
+ \
+ fail_stack.size = INIT_FAILURE_ALLOC; \
+ fail_stack.avail = 0; \
+ } while (0)
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+ Return 1 if succeeds, and 0 if either ran out of memory
+ allocating space for it or it was already too large.
+
+ REGEX_REALLOCATE requires `destination' be declared. */
+
+#define DOUBLE_FAIL_STACK(fail_stack) \
+ ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
+ ? 0 \
+ : ((fail_stack).stack = (fail_stack_elt_t *) \
+ REGEX_REALLOCATE ((fail_stack).stack, \
+ (fail_stack).size * sizeof (fail_stack_elt_t), \
+ ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
+ \
+ (fail_stack).stack == NULL \
+ ? 0 \
+ : ((fail_stack).size <<= 1, \
+ 1)))
+
+
+/* Push PATTERN_OP on FAIL_STACK.
+
+ Return 1 if was able to do so and 0 if ran out of memory allocating
+ space to do so. */
+#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
+ ((FAIL_STACK_FULL () \
+ && !DOUBLE_FAIL_STACK (fail_stack)) \
+ ? 0 \
+ : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
+ 1))
+
+/* This pushes an item onto the failure stack. Must be a four-byte
+ value. Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_ITEM(item) \
+ fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
+
+/* The complement operation. Assumes `fail_stack' is nonempty. */
+#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging. */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+ if we ever fail back to it.
+
+ Requires variables fail_stack, regstart, regend, reg_info, and
+ num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
+ declared.
+
+ Does `return FAILURE_CODE' if runs out of memory. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
+ do { \
+ char *destination; \
+ /* Must be int, so when we don't save any registers, the arithmetic \
+ of 0 + -1 isn't done as unsigned. */ \
+ int this_reg; \
+ \
+ DEBUG_STATEMENT (failure_id++); \
+ DEBUG_STATEMENT (nfailure_points_pushed++); \
+ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
+ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
+ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
+ \
+ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
+ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
+ \
+ /* Ensure we have enough space allocated for what we will push. */ \
+ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
+ { \
+ if (!DOUBLE_FAIL_STACK (fail_stack)) \
+ return failure_code; \
+ \
+ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
+ (fail_stack).size); \
+ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+ } \
+ \
+ /* Push the info, starting with the registers. */ \
+ DEBUG_PRINT1 ("\n"); \
+ \
+ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+ this_reg++) \
+ { \
+ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
+ DEBUG_STATEMENT (num_regs_pushed++); \
+ \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ PUSH_FAILURE_ITEM (regstart[this_reg]); \
+ \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ PUSH_FAILURE_ITEM (regend[this_reg]); \
+ \
+ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
+ DEBUG_PRINT2 (" match_null=%d", \
+ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" matched_something=%d", \
+ MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" ever_matched=%d", \
+ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT1 ("\n"); \
+ PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
+ } \
+ \
+ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
+ PUSH_FAILURE_ITEM (lowest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
+ PUSH_FAILURE_ITEM (highest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
+ PUSH_FAILURE_ITEM (pattern_place); \
+ \
+ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
+ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
+ size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ PUSH_FAILURE_ITEM (string_place); \
+ \
+ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
+ DEBUG_PUSH (failure_id); \
+ } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+ for each register. */
+#define NUM_REG_ITEMS 3
+
+/* Individual items aside from the registers. */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack. */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items. */
+#define NUM_FAILURE_ITEMS \
+ ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
+ + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it. */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+ We restore into the parameters, all of which should be lvalues:
+ STR -- the saved data position.
+ PAT -- the saved pattern position.
+ LOW_REG, HIGH_REG -- the highest and lowest active registers.
+ REGSTART, REGEND -- arrays of string positions.
+ REG_INFO -- array of information about each subexpression.
+
+ Also assumes the variables `fail_stack' and (if debugging), `bufp',
+ `pend', `string1', `size1', `string2', and `size2'. */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{ \
+ DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
+ int this_reg; \
+ const unsigned char *string_temp; \
+ \
+ assert (!FAIL_STACK_EMPTY ()); \
+ \
+ /* Remove failure points and point to how many regs pushed. */ \
+ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
+ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
+ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
+ \
+ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
+ \
+ DEBUG_POP (&failure_id); \
+ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
+ \
+ /* If the saved string location is NULL, it came from an \
+ on_failure_keep_string_jump opcode, and we want to throw away the \
+ saved NULL, thus retaining our current position in the string. */ \
+ string_temp = POP_FAILURE_ITEM (); \
+ if (string_temp != NULL) \
+ str = (const char *) string_temp; \
+ \
+ DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
+ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ \
+ pat = (unsigned char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
+ \
+ /* Restore register info. */ \
+ high_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
+ \
+ low_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
+ \
+ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
+ { \
+ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
+ \
+ reg_info[this_reg].word = POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
+ \
+ regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ \
+ regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ } \
+ \
+ DEBUG_STATEMENT (nfailure_points_popped++); \
+} /* POP_FAILURE_POINT */
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+ BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
+ characters can start a string that matches the pattern. This fastmap
+ is used by re_search to skip quickly over impossible starting points.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as BUFP->fastmap.
+
+ We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+ the pattern buffer.
+
+ Returns 0 if we succeed, -2 if an internal error. */
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int j, k;
+ fail_stack_type fail_stack;
+#ifndef REGEX_MALLOC
+ char *destination;
+#endif
+ /* We don't push any register information onto the failure stack. */
+ unsigned num_regs = 0;
+
+ register char *fastmap = bufp->fastmap;
+ unsigned char *pattern = bufp->buffer;
+ unsigned long size = bufp->used;
+ const unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+
+ /* Assume that each path through the pattern can be null until
+ proven otherwise. We set this false at the bottom of switch
+ statement, to which we get only if a particular path doesn't
+ match the empty string. */
+ boolean path_can_be_null = true;
+
+ /* We aren't doing a `succeed_n' to begin with. */
+ boolean succeed_n_p = false;
+
+ assert (fastmap != NULL && p != NULL);
+
+ INIT_FAIL_STACK ();
+ bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
+ bufp->fastmap_accurate = 1; /* It will be when we're done. */
+ bufp->can_be_null = 0;
+
+ while (p != pend || !FAIL_STACK_EMPTY ())
+ {
+ if (p == pend)
+ {
+ bufp->can_be_null |= path_can_be_null;
+
+ /* Reset for next path. */
+ path_can_be_null = true;
+
+ p = fail_stack.stack[--fail_stack.avail];
+ }
+
+ /* We should never be about to go beyond the end of the pattern. */
+ assert (p < pend);
+
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+
+ /* I guess the idea here is to simply not bother with a fastmap
+ if a backreference is used, since it's too hard to figure out
+ the fastmap for the corresponding group. Setting
+ `can_be_null' stops `re_search_2' from using the fastmap, so
+ that is all we do. */
+ case duplicate:
+ bufp->can_be_null = 1;
+ return 0;
+
+
+ /* Following are the cases which match a character. These end
+ with `break'. */
+
+ case exactn:
+ fastmap[p[1]] = 1;
+ break;
+
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ fastmap[j] = 1;
+ break;
+
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed. */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ fastmap[j] = 1;
+ break;
+
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case anychar:
+ /* `.' matches anything ... */
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ /* ... except perhaps newline. */
+ if (!(bufp->syntax & RE_DOT_NEWLINE))
+ fastmap['\n'] = 0;
+
+ /* Return if we have already set `can_be_null'; if we have,
+ then the fastmap is irrelevant. Something's wrong here. */
+ else if (bufp->can_be_null)
+ return 0;
+
+ /* Otherwise, have to check alternative paths. */
+ break;
+
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ /* All cases after this match the empty string. These end with
+ `continue'. */
+
+
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ continue;
+#endif /* not emacs */
+
+
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ case push_dummy_failure:
+ continue;
+
+
+ case jump_n:
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case jump_past_alt:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (j > 0)
+ continue;
+
+ /* Jump backward implies we just went through the body of a
+ loop and matched nothing. Opcode jumped to should be
+ `on_failure_jump' or `succeed_n'. Just treat it like an
+ ordinary jump. For a * loop, it has pushed its failure
+ point already; if so, discard that as redundant. */
+ if ((re_opcode_t) *p != on_failure_jump
+ && (re_opcode_t) *p != succeed_n)
+ continue;
+
+ p++;
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+
+ /* If what's on the stack is where we are now, pop it. */
+ if (!FAIL_STACK_EMPTY ()
+ && fail_stack.stack[fail_stack.avail - 1] == p)
+ fail_stack.avail--;
+
+ continue;
+
+
+ case on_failure_jump:
+ case on_failure_keep_string_jump:
+ handle_on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+
+ /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+ end of the pattern. We don't want to push such a point,
+ since when we restore it above, entering the switch will
+ increment `p' past the end of the pattern. We don't need
+ to push such a point since we obviously won't find any more
+ fastmap entries beyond `pend'. Such a pattern can match
+ the null string, though. */
+ if (p + j < pend)
+ {
+ if (!PUSH_PATTERN_OP (p + j, fail_stack))
+ return -2;
+ }
+ else
+ bufp->can_be_null = 1;
+
+ if (succeed_n_p)
+ {
+ EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
+ succeed_n_p = false;
+ }
+
+ continue;
+
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p += 2;
+
+ /* Increment p past the n for when k != 0. */
+ EXTRACT_NUMBER_AND_INCR (k, p);
+ if (k == 0)
+ {
+ p -= 4;
+ succeed_n_p = true; /* Spaghetti code alert. */
+ goto handle_on_failure_jump;
+ }
+ continue;
+
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ continue;
+
+
+ default:
+ abort (); /* We have listed all the cases. */
+ } /* switch *p++ */
+
+ /* Getting here means we have found the possible starting
+ characters for one path of the pattern -- and that the empty
+ string does not match. We need not follow this path further.
+ Instead, look at the next alternative (remembered on the
+ stack), or quit if no more. The test at the top of the loop
+ does these things. */
+ path_can_be_null = false;
+ p = pend;
+ } /* while p */
+
+ /* Set `can_be_null' for the last path (also the first path, if the
+ pattern is empty). */
+ bufp->can_be_null |= path_can_be_null;
+ return 0;
+} /* re_compile_fastmap */
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t) 0;
+ }
+}
+
+/* Searching routines. */
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
+ regs, size);
+}
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+ virtual concatenation of STRING1 and STRING2, starting first at index
+ STARTPOS, then at STARTPOS + 1, and so on.
+
+ STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+ RANGE is how far to scan while trying to match. RANGE = 0 means try
+ only at STARTPOS; in general, the last start tried is STARTPOS +
+ RANGE.
+
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire BUFP->buffer and its contained
+ subexpressions.
+
+ Do not consider matching one past the index STOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ We return either the position in the strings at which the match was
+ found, -1 if no match, or -2 if error (such as failure
+ stack overflow). */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ int range;
+ struct re_registers *regs;
+ int stop;
+{
+ int val;
+ register char *fastmap = bufp->fastmap;
+ register char *translate = bufp->translate;
+ int total_size = size1 + size2;
+ int endpos = startpos + range;
+
+ /* Check for out-of-range STARTPOS. */
+ if (startpos < 0 || startpos > total_size)
+ return -1;
+
+ /* Fix up RANGE if it might eventually take us outside
+ the virtual concatenation of STRING1 and STRING2. */
+ if (endpos < -1)
+ range = -1 - startpos;
+ else if (endpos > total_size)
+ range = total_size - startpos;
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ search for a pattern that must be anchored. */
+ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !bufp->fastmap_accurate)
+ if (re_compile_fastmap (bufp) == -2)
+ return -2;
+
+ /* Loop through the string, looking for a place to start matching. */
+ for (;;)
+ {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot be the start of a match. If the pattern can match the
+ null string, however, we don't need to skip characters; we want
+ the first null string. */
+ if (fastmap && startpos < total_size && !bufp->can_be_null)
+ {
+ if (range > 0) /* Searching forwards. */
+ {
+ register const char *d;
+ register int lim = 0;
+ int irange = range;
+
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+
+ /* Written out as an if-else to avoid testing `translate'
+ inside the loop. */
+ if (translate)
+ while (range > lim
+ && !fastmap[(unsigned char)
+ translate[(unsigned char) *d++]])
+ range--;
+ else
+ while (range > lim && !fastmap[(unsigned char) *d++])
+ range--;
+
+ startpos += irange - range;
+ }
+ else /* Searching backwards. */
+ {
+ register char c = (size1 == 0 || startpos >= size1
+ ? string2[startpos - size1]
+ : string1[startpos]);
+
+ if (!fastmap[(unsigned char) TRANSLATE (c)])
+ goto advance;
+ }
+ }
+
+ /* If can't match the null string, and that's all we have left, fail. */
+ if (range >= 0 && startpos == total_size && fastmap
+ && !bufp->can_be_null)
+ return -1;
+
+ val = re_match_2 (bufp, string1, size1, string2, size2,
+ startpos, regs, stop);
+ if (val >= 0)
+ return startpos;
+
+ if (val == -2)
+ return -2;
+
+ advance:
+ if (!range)
+ break;
+ else if (range > 0)
+ {
+ range--;
+ startpos++;
+ }
+ else
+ {
+ range++;
+ startpos--;
+ }
+ }
+ return -1;
+} /* re_search_2 */
+
+/* Declarations and macros for re_match_2. */
+
+static int bcmp_translate ();
+static boolean alt_match_null_string_p (),
+ common_op_match_null_string_p (),
+ group_match_null_string_p ();
+
+/* Structure for per-register (a.k.a. per-group) information.
+ This must not be longer than one word, because we push this value
+ onto the failure stack. Other register information, such as the
+ starting and ending positions (which are addresses), and the list of
+ inner groups (which is a bits list) are maintained in separate
+ variables.
+
+ We are making a (strictly speaking) nonportable assumption here: that
+ the compiler will pack our bit fields into something that fits into
+ the type of `word', i.e., is something that fits into one item on the
+ failure stack. */
+typedef union
+{
+ fail_stack_elt_t word;
+ struct
+ {
+ /* This field is one if this group can match the empty string,
+ zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
+#define MATCH_NULL_UNSET_VALUE 3
+ unsigned match_null_string_p : 2;
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ unsigned ever_matched_something : 1;
+ } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+ for the subexpressions which we are currently inside. Also records
+ that those subexprs have matched. */
+#define SET_REGS_MATCHED() \
+ do \
+ { \
+ unsigned r; \
+ for (r = lowest_active_reg; r <= highest_active_reg; r++) \
+ { \
+ MATCHED_SOMETHING (reg_info[r]) \
+ = EVER_MATCHED_SOMETHING (reg_info[r]) \
+ = 1; \
+ } \
+ } \
+ while (0)
+
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+ and `string2' into an offset from the beginning of that string. */
+#define POINTER_TO_OFFSET(ptr) \
+ (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
+
+/* Registers are set to a sentinel when they haven't yet matched. */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+/* Macros for dealing with the split strings in re_match_2. */
+
+#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
+
+/* Call before fetching a character with *d. This switches over to
+ string2 if necessary. */
+#define PREFETCH() \
+ while (d == dend) \
+ { \
+ /* End of string2 => fail. */ \
+ if (dend == end_match_2) \
+ goto fail; \
+ /* End of string1 => advance to string2. */ \
+ d = string2; \
+ dend = end_match_2; \
+ }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+ of `string1' and `string2'. If only one string, it's `string2'. */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)
+
+
+/* Test if D points to a character which is word-constituent. We have
+ two special cases to check for: if past the end of string1, look at
+ the first character in string2; and if before the beginning of
+ string2, look at the last character in string1. */
+#define WORDCHAR_P(d) \
+ (SYNTAX ((d) == end1 ? *string2 \
+ : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
+ == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+ to being word-constituent. */
+#define AT_WORD_BOUNDARY(d) \
+ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
+ || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+
+
+/* Free everything we malloc. */
+#ifdef REGEX_MALLOC
+#define FREE_VAR(var) if (var) free (var); var = NULL
+#define FREE_VARIABLES() \
+ do { \
+ FREE_VAR (fail_stack.stack); \
+ FREE_VAR (regstart); \
+ FREE_VAR (regend); \
+ FREE_VAR (old_regstart); \
+ FREE_VAR (old_regend); \
+ FREE_VAR (best_regstart); \
+ FREE_VAR (best_regend); \
+ FREE_VAR (reg_info); \
+ FREE_VAR (reg_dummy); \
+ FREE_VAR (reg_info_dummy); \
+ } while (0)
+#else /* not REGEX_MALLOC */
+/* Some MIPS systems (at least) want this to free alloca'd storage. */
+#define FREE_VARIABLES() alloca (0)
+#endif /* not REGEX_MALLOC */
+
+
+/* These values must meet several constraints. They must not be valid
+ register values; since we have a limit of 255 registers (because
+ we use only one byte in the pattern for the register number), we can
+ use numbers larger than 255. They must differ by 1, because of
+ NUM_FAILURE_ITEMS above. And the value for the lowest register must
+ be larger than the value for the highest register, so we do not try
+ to actually save any registers when none are active. */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+/* Matching routines. */
+
+#ifndef emacs /* Emacs never uses this. */
+/* re_match is like re_match_2 except it takes only a single string. */
+
+int
+re_match (bufp, string, size, pos, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, pos;
+ struct re_registers *regs;
+ {
+ return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
+}
+#endif /* not emacs */
+
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+ the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+ and SIZE2, respectively). We start matching at POS, and stop
+ matching at STOP.
+
+ If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+ store offsets for the substring each group matched in REGS. See the
+ documentation for exactly how many groups we fill.
+
+ We return -1 if no match, -2 if an internal error (such as the
+ failure stack overflowing). Otherwise, we return the length of the
+ matched substring. */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int stop;
+{
+ /* General temporaries. */
+ int mcnt;
+ unsigned char *p1;
+
+ /* Just past the end of the corresponding string. */
+ const char *end1, *end2;
+
+ /* Pointers into string1 and string2, just past the last characters in
+ each to consider matching. */
+ const char *end_match_1, *end_match_2;
+
+ /* Where we are in the data, and the end of the current string. */
+ const char *d, *dend;
+
+ /* Where we are in the pattern, and the end of the pattern. */
+ unsigned char *p = bufp->buffer;
+ register unsigned char *pend = p + bufp->used;
+
+ /* We use this to map every character in the string. */
+ char *translate = bufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to
+ the subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where
+ to resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is
+ a ``dummy''; if a failure happens and the failure point is a dummy,
+ it gets discarded and the next next one is tried. */
+ fail_stack_type fail_stack;
+#ifdef DEBUG
+ static unsigned failure_id = 0;
+ unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+ /* We fill all the registers internally, independent of what we
+ return, for use in backreferences. The number here includes
+ an element for register zero. */
+ unsigned num_regs = bufp->re_nsub + 1;
+
+ /* The currently active registers. */
+ unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+ const char **regstart, **regend;
+
+ /* If a group that's operated upon by a repetition operator fails to
+ match anything, then the register for its start will need to be
+ restored because it will have been set to wherever in the string we
+ are when we last see its open-group operator. Similarly for a
+ register's end. */
+ const char **old_regstart, **old_regend;
+
+ /* 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. */
+ register_info_type *reg_info;
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+ unsigned best_regs_set = false;
+ const char **best_regstart, **best_regend;
+
+ /* Logically, this is `best_regend[0]'. But we don't want to have to
+ allocate space for that if we're not allocating space for anything
+ else (see below). Also, we never need info about register 0 for
+ any of the other register vectors, and it seems rather a kludge to
+ treat `best_regend' differently than the rest. So we keep track of
+ the end of the best match so far in a separate variable. We
+ initialize this to NULL so that when we backtrack the first time
+ and need to test it, it's not garbage. */
+ const char *match_end = NULL;
+
+ /* Used when we pop values we don't care about. */
+ const char **reg_dummy;
+ register_info_type *reg_info_dummy;
+
+#ifdef DEBUG
+ /* Counts the total number of registers pushed. */
+ unsigned num_regs_pushed = 0;
+#endif
+
+ DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+
+ INIT_FAIL_STACK ();
+
+ /* Do not bother to initialize all the register variables if there are
+ no groups in the pattern, as it takes a fair amount of time. If
+ there are groups, we include space for register 0 (the whole
+ pattern), even though we never use it, since it simplifies the
+ array indexing. We should fix this. */
+ if (bufp->re_nsub)
+ {
+ regstart = REGEX_TALLOC (num_regs, const char *);
+ regend = REGEX_TALLOC (num_regs, const char *);
+ old_regstart = REGEX_TALLOC (num_regs, const char *);
+ old_regend = REGEX_TALLOC (num_regs, const char *);
+ best_regstart = REGEX_TALLOC (num_regs, const char *);
+ best_regend = REGEX_TALLOC (num_regs, const char *);
+ reg_info = REGEX_TALLOC (num_regs, register_info_type);
+ reg_dummy = REGEX_TALLOC (num_regs, const char *);
+ reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+ if (!(regstart && regend && old_regstart && old_regend && reg_info
+ && best_regstart && best_regend && reg_dummy && reg_info_dummy))
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+ }
+#ifdef REGEX_MALLOC
+ else
+ {
+ /* We must initialize all our variables to NULL, so that
+ `FREE_VARIABLES' doesn't try to free them. */
+ regstart = regend = old_regstart = old_regend = best_regstart
+ = best_regend = reg_dummy = NULL;
+ reg_info = reg_info_dummy = (register_info_type *) NULL;
+ }
+#endif /* REGEX_MALLOC */
+
+ /* The starting position is bogus. */
+ if (pos < 0 || pos > size1 + size2)
+ {
+ FREE_VARIABLES ();
+ return -1;
+ }
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ start_memory/stop_memory has been seen for. Also initialize the
+ register information struct. */
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = regend[mcnt]
+ = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+
+ REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ /* We move `string1' into `string2' if the latter's empty -- but not if
+ `string1' is null. */
+ if (size2 == 0 && string1 != NULL)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings. */
+ if (stop <= size1)
+ {
+ end_match_1 = string1 + stop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + stop - size1;
+ }
+
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within. `d'
+ is advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal `string2'. */
+ if (size1 > 0 && pos <= size1)
+ {
+ d = string1 + pos;
+ dend = end_match_1;
+ }
+ else
+ {
+ d = string2 + pos - size1;
+ dend = end_match_2;
+ }
+
+ DEBUG_PRINT1 ("The compiled pattern is: ");
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+ DEBUG_PRINT1 ("The string to match is: `");
+ DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+ DEBUG_PRINT1 ("'\n");
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if the match is complete, or it drops through if the match
+ fails at this starting point in the input data. */
+ for (;;)
+ {
+ DEBUG_PRINT2 ("\n0x%x: ", p);
+
+ if (p == pend)
+ { /* End of pattern means we might have succeeded. */
+ DEBUG_PRINT1 ("end of pattern ... ");
+
+ /* If we haven't matched the entire string, and we want the
+ longest match, try backtracking. */
+ if (d != end_match_2)
+ {
+ DEBUG_PRINT1 ("backtracking.\n");
+
+ if (!FAIL_STACK_EMPTY ())
+ { /* More failure points to try. */
+ boolean same_str_p = (FIRST_STRING_P (match_end)
+ == MATCHING_IN_FIRST_STRING);
+
+ /* If exceeds best match so far, save it. */
+ if (!best_regs_set
+ || (same_str_p && d > match_end)
+ || (!same_str_p && !MATCHING_IN_FIRST_STRING))
+ {
+ best_regs_set = true;
+ match_end = d;
+
+ DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+
+ /* If no failure points, don't restore garbage. */
+ else if (best_regs_set)
+ {
+ restore_best_regs:
+ /* Restore best match. It may happen that `dend ==
+ end_match_1' while the restored d is in string2.
+ For example, the pattern `x.*y.*z' against the
+ strings `x-' and `y-z-', if the two strings are
+ not consecutive in memory. */
+ DEBUG_PRINT1 ("Restoring best registers.\n");
+
+ d = match_end;
+ dend = ((d >= string1 && d <= end1)
+ ? end_match_1 : end_match_2);
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
+ }
+ }
+ } /* d != end_match_2 */
+
+ DEBUG_PRINT1 ("Accepting match.\n");
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !bufp->no_sub)
+ {
+ /* Have the register data arrays been allocated? */
+ if (bufp->regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. We need one
+ extra element beyond `num_regs' for the `-1' marker
+ GNU code uses. */
+ regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+ regs->start = TALLOC (regs->num_regs, regoff_t);
+ regs->end = TALLOC (regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ bufp->regs_allocated = REGS_REALLOCATE;
+ }
+ else if (bufp->regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (regs->num_regs < num_regs + 1)
+ {
+ regs->num_regs = num_regs + 1;
+ RETALLOC (regs->start, regs->num_regs, regoff_t);
+ RETALLOC (regs->end, regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ }
+ }
+ else
+ assert (bufp->regs_allocated == REGS_FIXED);
+
+ /* Convert the pointer data in `regstart' and `regend' to
+ indices. Register zero has to be set differently,
+ since we haven't kept track of any info for it. */
+ if (regs->num_regs > 0)
+ {
+ regs->start[0] = pos;
+ regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
+ : d - string2 + size1);
+ }
+
+ /* Go through the first `min (num_regs, regs->num_regs)'
+ registers, since that is all we initialized. */
+ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+ {
+ if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ else
+ {
+ regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
+ regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+ }
+ }
+
+ /* If the regs structure we return has more elements than
+ were in the pattern, set the extra elements to -1. If
+ we (re)allocated the registers, this is the case,
+ because we always allocate enough to have at least one
+ -1 at the end. */
+ for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ } /* regs && !bufp->no_sub */
+
+ FREE_VARIABLES ();
+ DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+ nfailure_points_pushed, nfailure_points_popped,
+ nfailure_points_pushed - nfailure_points_popped);
+ DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+ mcnt = d - pos - (MATCHING_IN_FIRST_STRING
+ ? string1
+ : string2 - size1);
+
+ DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+ return mcnt;
+ }
+
+ /* Otherwise match next pattern command. */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case no_op:
+ DEBUG_PRINT1 ("EXECUTING no_op.\n");
+ break;
+
+
+ /* Match the next n pattern characters exactly. The following
+ byte in the pattern defines n, and the n bytes after that
+ are the characters to match. */
+ case exactn:
+ mcnt = *p++;
+ DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (translate)
+ {
+ do
+ {
+ PREFETCH ();
+ if (translate[(unsigned char) *d++] != (char) *p++)
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH ();
+ if (*d++ != (char) *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED ();
+ break;
+
+
+ /* Match any character except possibly a newline or a null. */
+ case anychar:
+ DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+ PREFETCH ();
+
+ if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+ || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+ goto fail;
+
+ SET_REGS_MATCHED ();
+ DEBUG_PRINT2 (" Matched `%d'.\n", *d);
+ d++;
+ break;
+
+
+ case charset:
+ case charset_not:
+ {
+ register unsigned char c;
+ boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+ DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+ PREFETCH ();
+ c = TRANSLATE (*d); /* The character to match. */
+
+ /* Cast to `unsigned' instead of `unsigned char' in case the
+ bit list is a full 32 bytes long. */
+ if (c < (unsigned) (*p * BYTEWIDTH)
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+ }
+
+
+ /* The beginning of a group is represented by start_memory.
+ The arguments are the register number in the next byte, and the
+ number of groups inner to this one in the next. The text
+ matched within the group is recorded (in the internal
+ registers data structure) under the register number. */
+ case start_memory:
+ DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+ /* Find out if this group can match the empty string. */
+ p1 = p; /* To send to group_match_null_string_p. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[*p])
+ = group_match_null_string_p (&p1, pend, reg_info);
+
+ /* Save the position in the string where we were the last time
+ we were at this open-group operator in case the group is
+ operated upon by a repetition operator, e.g., with `(a*)*b'
+ against `ab'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+ : regstart[*p];
+ DEBUG_PRINT2 (" old_regstart: %d\n",
+ POINTER_TO_OFFSET (old_regstart[*p]));
+
+ regstart[*p] = d;
+ DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+ IS_ACTIVE (reg_info[*p]) = 1;
+ MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* This is the new highest active register. */
+ highest_active_reg = *p;
+
+ /* If nothing was active before, this is the new lowest active
+ register. */
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *p;
+
+ /* Move past the register number and inner group count. */
+ p += 2;
+ break;
+
+
+ /* The stop_memory opcode represents the end of a group. Its
+ arguments are the same as start_memory's: the register
+ number, and the number of inner groups. */
+ case stop_memory:
+ DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+
+ /* We need to save the string position the last time we were at
+ this close-group operator in case the group is operated
+ upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+ against `aba'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regend[*p]) ? d : regend[*p]
+ : regend[*p];
+ DEBUG_PRINT2 (" old_regend: %d\n",
+ POINTER_TO_OFFSET (old_regend[*p]));
+
+ regend[*p] = d;
+ DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+ /* This register isn't active anymore. */
+ IS_ACTIVE (reg_info[*p]) = 0;
+
+ /* If this was the only register active, nothing is active
+ anymore. */
+ if (lowest_active_reg == highest_active_reg)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ { /* We must scan for the new highest active register, since
+ it isn't necessarily one less than now: consider
+ (a(b)c(d(e)f)g). When group 3 ends, after the f), the
+ new highest active register is 1. */
+ unsigned char r = *p - 1;
+ while (r > 0 && !IS_ACTIVE (reg_info[r]))
+ r--;
+
+ /* If we end up at register zero, that means that we saved
+ the registers as the result of an `on_failure_jump', not
+ a `start_memory', and we jumped to past the innermost
+ `stop_memory'. For example, in ((.)*) we save
+ registers 1 and 2 as a result of the *, but when we pop
+ back to the second ), we are at the stop_memory 1.
+ Thus, nothing is active. */
+ if (r == 0)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ highest_active_reg = r;
+ }
+
+ /* If just failed to match something this time around with a
+ group that's operated on by a repetition operator, try to
+ force exit from the ``loop'', and restore the register
+ information for this group that we had before trying this
+ last match. */
+ if ((!MATCHED_SOMETHING (reg_info[*p])
+ || (re_opcode_t) p[-3] == start_memory)
+ && (p + 2) < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ p1 = p + 2;
+ mcnt = 0;
+ switch ((re_opcode_t) *p1++)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (is_a_jump_n)
+ p1 += 2;
+ break;
+
+ default:
+ /* do nothing */ ;
+ }
+ p1 += mcnt;
+
+ /* If the next operation is a jump backwards in the pattern
+ to an on_failure_jump right before the start_memory
+ corresponding to this stop_memory, exit from the loop
+ by forcing a failure after pushing on the stack the
+ on_failure_jump's jump in the pattern, and d. */
+ if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+ && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+ {
+ /* If this group ever matched anything, then restore
+ what its registers were before trying this last
+ failed match, e.g., with `(a*)*b' against `ab' for
+ regstart[1], and, e.g., with `((a*)*(b*)*)*'
+ against `aba' for regend[3].
+
+ Also restore the registers for inner groups for,
+ e.g., `((a*)(b*))*' against `aba' (register 3 would
+ otherwise get trashed). */
+
+ if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+ {
+ unsigned r;
+
+ EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* Restore this and inner groups' (if any) registers. */
+ for (r = *p; r < *p + *(p + 1); r++)
+ {
+ regstart[r] = old_regstart[r];
+
+ /* xx why this test? */
+ if ((int) old_regend[r] >= (int) regstart[r])
+ regend[r] = old_regend[r];
+ }
+ }
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+ goto fail;
+ }
+ }
+
+ /* Move past the register number and the inner group count. */
+ p += 2;
+ break;
+
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ register const char *d2, *dend2;
+ int regno = *p++; /* Get which register to match against. */
+ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+ /* Can't back reference a group which we've never matched. */
+ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+ goto fail;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = ((FIRST_STRING_P (regstart[regno])
+ == FIRST_STRING_P (regend[regno]))
+ ? regend[regno] : end_match_1);
+ for (;;)
+ {
+ /* If necessary, advance to next segment in register
+ contents. */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+
+ /* End of string1 => advance to string2. */
+ d2 = string2;
+ dend2 = regend[regno];
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* If necessary, advance to next segment in data. */
+ PREFETCH ();
+
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if (translate
+ ? bcmp_translate (d, d2, mcnt, translate)
+ : bcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+
+ /* begline matches the empty string at the beginning of the string
+ (unless `not_bol' is set in `bufp'), and, if
+ `newline_anchor' is set, after newlines. */
+ case begline:
+ DEBUG_PRINT1 ("EXECUTING begline.\n");
+
+ if (AT_STRINGS_BEG (d))
+ {
+ if (!bufp->not_bol) break;
+ }
+ else if (d[-1] == '\n' && bufp->newline_anchor)
+ {
+ break;
+ }
+ /* In all other cases, we fail. */
+ goto fail;
+
+
+ /* endline is the dual of begline. */
+ case endline:
+ DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+ if (AT_STRINGS_END (d))
+ {
+ if (!bufp->not_eol) break;
+ }
+
+ /* We have to ``prefetch'' the next character. */
+ else if ((d == end1 ? *string2 : *d) == '\n'
+ && bufp->newline_anchor)
+ {
+ break;
+ }
+ goto fail;
+
+
+ /* Match at the very beginning of the data. */
+ case begbuf:
+ DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+ if (AT_STRINGS_BEG (d))
+ break;
+ goto fail;
+
+
+ /* Match at the very end of the data. */
+ case endbuf:
+ DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+ if (AT_STRINGS_END (d))
+ break;
+ goto fail;
+
+
+ /* on_failure_keep_string_jump is used to optimize `.*\n'. It
+ pushes NULL as the value for the string on the stack. Then
+ `pop_failure_point' will keep the current value for the
+ string, instead of restoring it. To see why, consider
+ matching `foo\nbar' against `.*\n'. The .* matches the foo;
+ then the . fails against the \n. But the next thing we want
+ to do is match the \n against the \n; if we restored the
+ string value, we would be back at the foo.
+
+ Because this is used only in specific cases, we don't need to
+ check all the things that `on_failure_jump' does, to make
+ sure the right things get saved on the stack. Hence we don't
+ share its code. The only reason to push anything on the
+ stack at all is that otherwise we would have to change
+ `anychar's code to do something besides goto fail in this
+ case; that seems worse than this. */
+ case on_failure_keep_string_jump:
+ DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+ PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+ break;
+
+
+ /* Uses of on_failure_jump:
+
+ Each alternative starts with an on_failure_jump that points
+ to the beginning of the next alternative. Each alternative
+ except the last ends with a jump that in effect jumps past
+ the rest of the alternatives. (They really jump to the
+ ending jump of the following alternative, because tensioning
+ these jumps is a hassle.)
+
+ Repeats start with an on_failure_jump that points past both
+ the repetition text and either the following jump or
+ pop_failure_jump back to this on_failure_jump. */
+ case on_failure_jump:
+ on_failure:
+ DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+ /* If this on_failure_jump comes right before a group (i.e.,
+ the original * applied to a group), save the information
+ for that group and all inner ones, so that if we fail back
+ to this point, the group's information will be correct.
+ For example, in \(a*\)*\1, we need the preceding group,
+ and in \(\(a*\)b*\)\2, we need the inner group. */
+
+ /* We can't use `p' to check ahead because we push
+ a failure point to `p + mcnt' after we do this. */
+ p1 = p;
+
+ /* We need to skip no_op's before we look for the
+ start_memory in case this on_failure_jump is happening as
+ the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+ against aba. */
+ while (p1 < pend && (re_opcode_t) *p1 == no_op)
+ p1++;
+
+ if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+ {
+ /* We have a new highest active register now. This will
+ get reset at the start_memory we are about to get to,
+ but we will have saved all the registers relevant to
+ this repetition op, as described above. */
+ highest_active_reg = *(p1 + 1) + *(p1 + 2);
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *(p1 + 1);
+ }
+
+ DEBUG_PRINT1 (":\n");
+ PUSH_FAILURE_POINT (p + mcnt, d, -2);
+ break;
+
+
+ /* A smart repeat ends with `maybe_pop_jump'.
+ We change it to either `pop_failure_jump' or `jump'. */
+ case maybe_pop_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+ {
+ register unsigned char *p2 = p;
+
+ /* Compare the beginning of the repeat with what in the
+ pattern follows its end. If we can establish that there
+ is nothing that they would both match, i.e., that we
+ would have to backtrack because of (as in, e.g., `a*a')
+ then we can change to pop_failure_jump, because we'll
+ never have to backtrack.
+
+ This is not true in the case of alternatives: in
+ `(a|ab)*' we do need to backtrack to the `ab' alternative
+ (e.g., if the string was `ab'). But instead of trying to
+ detect that here, the alternative has put on a dummy
+ failure point which is what we will end up popping. */
+
+ /* Skip over open/close-group commands. */
+ while (p2 + 2 < pend
+ && ((re_opcode_t) *p2 == stop_memory
+ || (re_opcode_t) *p2 == start_memory))
+ p2 += 3; /* Skip over args, too. */
+
+ /* If we're at the end of the pattern, we can change. */
+ if (p2 == pend)
+ {
+ /* Consider what happens when matching ":\(.*\)"
+ against ":/". I don't really understand this code
+ yet. */
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1
+ (" End of pattern: change to `pop_failure_jump'.\n");
+ }
+
+ else if ((re_opcode_t) *p2 == exactn
+ || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+ {
+ register unsigned char c
+ = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ p1 = p + mcnt;
+
+ /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+ to the `maybe_finalize_jump' of this case. Examine what
+ follows. */
+ if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
+ c, p1[5]);
+ }
+
+ else if ((re_opcode_t) p1[3] == charset
+ || (re_opcode_t) p1[3] == charset_not)
+ {
+ int not = (re_opcode_t) p1[3] == charset_not;
+
+ if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ /* `not' is equal to 1 if c would match, which means
+ that we can't change to pop_failure_jump. */
+ if (!not)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ }
+ }
+ p -= 2; /* Point at relative address again. */
+ if ((re_opcode_t) p[-1] != pop_failure_jump)
+ {
+ p[-1] = (unsigned char) jump;
+ DEBUG_PRINT1 (" Match => jump.\n");
+ goto unconditional_jump;
+ }
+ /* Note fall through. */
+
+
+ /* The end of a simple repeat has a pop_failure_jump back to
+ its matching on_failure_jump, where the latter will push a
+ failure point. The pop_failure_jump takes off failure
+ points put on by this pop_failure_jump's matching
+ on_failure_jump; we got through the pattern to here from the
+ matching on_failure_jump, so didn't fail. */
+ case pop_failure_jump:
+ {
+ /* We need to pass separate storage for the lowest and
+ highest registers, even though we don't care about the
+ actual values. Otherwise, we will restore only one
+ register from the stack, since lowest will == highest in
+ `pop_failure_point'. */
+ unsigned dummy_low_reg, dummy_high_reg;
+ unsigned char *pdummy;
+ const char *sdummy;
+
+ DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+ POP_FAILURE_POINT (sdummy, pdummy,
+ dummy_low_reg, dummy_high_reg,
+ reg_dummy, reg_dummy, reg_info_dummy);
+ }
+ /* Note fall through. */
+
+
+ /* Unconditionally jump (without popping any failure points). */
+ case jump:
+ unconditional_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
+ DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+ p += mcnt; /* Do the jump. */
+ DEBUG_PRINT2 ("(to 0x%x).\n", p);
+ break;
+
+
+ /* We need this opcode so we can detect where alternatives end
+ in `group_match_null_string_p' et al. */
+ case jump_past_alt:
+ DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+ goto unconditional_jump;
+
+
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at pop_failure_jump. We will end up at
+ pop_failure_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for pop_failure_jump to pop. */
+ case dummy_failure_jump:
+ DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+ /* It doesn't matter what we push for the string here. What
+ the code at `fail' tests is the value for the pattern. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ goto unconditional_jump;
+
+
+ /* At the end of an alternative, we need to push a dummy failure
+ point in case we are followed by a `pop_failure_jump', because
+ we don't want the failure point for the alternative to be
+ popped. For example, matching `(a|ab)*' against `aab'
+ requires that we match the `ab' alternative. */
+ case push_dummy_failure:
+ DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+ /* See comments just above at `dummy_failure_jump' about the
+ two zeroes. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ break;
+
+ /* Have to succeed matching what follows at least n times.
+ After that, handle like `on_failure_jump'. */
+ case succeed_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+ assert (mcnt >= 0);
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt > 0)
+ {
+ mcnt--;
+ p += 2;
+ STORE_NUMBER_AND_INCR (p, mcnt);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt);
+ }
+ else if (mcnt == 0)
+ {
+ DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
+ p[2] = (unsigned char) no_op;
+ p[3] = (unsigned char) no_op;
+ goto on_failure;
+ }
+ break;
+
+ case jump_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt)
+ {
+ mcnt--;
+ STORE_NUMBER (p + 2, mcnt);
+ goto unconditional_jump;
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ break;
+
+ case set_number_at:
+ {
+ DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt);
+ STORE_NUMBER (p1, mcnt);
+ break;
+ }
+
+ case wordbound:
+ DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ break;
+ goto fail;
+
+ case notwordbound:
+ DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ goto fail;
+ break;
+
+ case wordbeg:
+ DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+ if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+ break;
+ goto fail;
+
+ case wordend:
+ DEBUG_PRINT1 ("EXECUTING wordend.\n");
+ if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+ && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+ break;
+ goto fail;
+
+#ifdef emacs
+#ifdef emacs19
+ case before_dot:
+ DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) != point)
+ goto fail;
+ break;
+
+ case after_dot:
+ DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+ goto fail;
+ break;
+#else /* not emacs19 */
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
+ goto fail;
+ break;
+#endif /* not emacs19 */
+
+ case syntaxspec:
+ DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchsyntax;
+
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+ mcnt = (int) Sword;
+ matchsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+ case notsyntaxspec:
+ DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchnotsyntax;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+ mcnt = (int) Sword;
+ matchnotsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+#else /* not emacs */
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+ PREFETCH ();
+ if (!WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+ PREFETCH ();
+ if (WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+#endif /* not emacs */
+
+ default:
+ abort ();
+ }
+ continue; /* Successfully executed one pattern command; keep going. */
+
+
+ /* We goto here if a matching operation fails. */
+ fail:
+ if (!FAIL_STACK_EMPTY ())
+ { /* A restart point is known. Restore to that state. */
+ DEBUG_PRINT1 ("\nFAIL:\n");
+ POP_FAILURE_POINT (d, p,
+ lowest_active_reg, highest_active_reg,
+ regstart, regend, reg_info);
+
+ /* If this failure point is a dummy, try the next one. */
+ if (!p)
+ goto fail;
+
+ /* If we failed to the end of the pattern, don't examine *p. */
+ assert (p <= pend);
+ if (p < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ /* If failed to a backwards jump that's part of a repetition
+ loop, need to pop this failure point and use the next one. */
+ switch ((re_opcode_t) *p)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case maybe_pop_jump:
+ case pop_failure_jump:
+ case jump:
+ p1 = p + 1;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+
+ if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+ || (!is_a_jump_n
+ && (re_opcode_t) *p1 == on_failure_jump))
+ goto fail;
+ break;
+ default:
+ /* do nothing */ ;
+ }
+ }
+
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ } /* for (;;) */
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_VARIABLES ();
+
+ return -1; /* Failure to match. */
+} /* re_match_2 */
+
+/* Subroutine definitions for re_match_2. */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+
+ Return true if the pattern up to the corresponding stop_memory can
+ match the empty string, and false otherwise.
+
+ If we find the matching stop_memory, sets P to point to one past its number.
+ Otherwise, sets P to an undefined byte less than or equal to END.
+
+ We don't handle duplicates properly (yet). */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ /* Point to after the args to the start_memory. */
+ unsigned char *p1 = *p + 2;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and return true or
+ false, as appropriate, when we get to one that can't, or to the
+ matching stop_memory. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* Could be either a loop or a series of alternatives. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ /* If the next operation is not a jump backwards in the
+ pattern. */
+
+ if (mcnt >= 0)
+ {
+ /* Go through the on_failure_jumps of the alternatives,
+ seeing if any of the alternatives cannot match nothing.
+ The last alternative starts with only a jump,
+ whereas the rest start with on_failure_jump and end
+ with a jump, e.g., here is the pattern for `a|b|c':
+
+ /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+ /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+ /exactn/1/c
+
+ So, we have to first go through the first (n-1)
+ alternatives and then deal with the last one separately. */
+
+
+ /* Deal with the first (n-1) alternatives, which start
+ with an on_failure_jump (see above) that jumps to right
+ past a jump_past_alt. */
+
+ while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+ {
+ /* `mcnt' holds how many bytes long the alternative
+ is, including the ending `jump_past_alt' and
+ its number. */
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
+ reg_info))
+ return false;
+
+ /* Move to right after this alternative, including the
+ jump_past_alt. */
+ p1 += mcnt;
+
+ /* Break if it's the beginning of an n-th alternative
+ that doesn't begin with an on_failure_jump. */
+ if ((re_opcode_t) *p1 != on_failure_jump)
+ break;
+
+ /* Still have to check that it's not an n-th
+ alternative that starts with an on_failure_jump. */
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+ {
+ /* Get to the beginning of the n-th alternative. */
+ p1 -= 3;
+ break;
+ }
+ }
+
+ /* Deal with the last alternative: go back and get number
+ of the `jump_past_alt' just before it. `mcnt' contains
+ the length of the alternative. */
+ EXTRACT_NUMBER (mcnt, p1 - 2);
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+ return false;
+
+ p1 += mcnt; /* Get past the n-th alternative. */
+ } /* if mcnt > 0 */
+ break;
+
+
+ case stop_memory:
+ assert (p1[1] == **p);
+ *p = p1 + 2;
+ return true;
+
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+ It expects P to be the first byte of a single alternative and END one
+ byte past the last. The alternative can contain groups. */
+
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+ unsigned char *p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ unsigned char *p1 = p;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and break when we get
+ to one that can't. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* It's a loop. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ break;
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+ alt_match_null_string_p.
+
+ Sets P to one after the op and its arguments, if any. */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ boolean ret;
+ int reg_no;
+ unsigned char *p1 = *p;
+
+ switch ((re_opcode_t) *p1++)
+ {
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbeg:
+ case wordend:
+ case wordbound:
+ case notwordbound:
+#ifdef emacs
+ case before_dot:
+ case at_dot:
+ case after_dot:
+#endif
+ break;
+
+ case start_memory:
+ reg_no = *p1;
+ assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+ ret = group_match_null_string_p (&p1, end, reg_info);
+
+ /* Have to set this here in case we're checking a group which
+ contains a group and a back reference to it. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+ if (!ret)
+ return false;
+ break;
+
+ /* If this is an optimized succeed_n for zero times, make the jump. */
+ case jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (mcnt >= 0)
+ p1 += mcnt;
+ else
+ return false;
+ break;
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p1 += 2;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ if (mcnt == 0)
+ {
+ p1 -= 4;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ }
+ else
+ return false;
+ break;
+
+ case duplicate:
+ if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+ return false;
+ break;
+
+ case set_number_at:
+ p1 += 4;
+
+ default:
+ /* All other opcodes mean we cannot match the empty string. */
+ return false;
+ }
+
+ *p = p1;
+ return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+ bytes; nonzero otherwise. */
+
+static int
+bcmp_translate (s1, s2, len, translate)
+ unsigned char *s1, *s2;
+ register int len;
+ char *translate;
+{
+ register unsigned char *p1 = s1, *p2 = s2;
+ while (len)
+ {
+ if (translate[*p1++] != translate[*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length SIZE) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry.
+
+ We call regex_compile to do the actual compilation. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ int length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume at least RE_NREGS registers will be set
+ (and at least one extra will be -1). */
+ bufp->regs_allocated = REGS_UNALLOCATED;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+ return re_error_msg[(int) ret];
+}
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them if this is an Emacs or POSIX compilation. */
+
+#if !defined (emacs) && !defined (_POSIX_SOURCE)
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ re_comp_buf.buffer = (unsigned char *) malloc (200);
+ if (re_comp_buf.buffer == NULL)
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+
+ re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+ if (re_comp_buf.fastmap == NULL)
+ return "Memory exhausted";
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+
+ /* Yes, we're discarding `const' here. */
+ return (char *) re_error_msg[(int) ret];
+}
+
+
+int
+re_exec (s)
+ const char *s;
+{
+ const int len = strlen (s);
+ return
+ 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+#endif /* not emacs and not _POSIX_SOURCE */
+
+/* POSIX.2 functions. Don't define these for Emacs. */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' and `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *preg;
+ const char *pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ unsigned syntax
+ = (cflags & REG_EXTENDED) ?
+ RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+ /* regex_compile will allocate the space for the compiled pattern. */
+ preg->buffer = 0;
+ preg->allocated = 0;
+
+ /* Don't bother to use a fastmap when searching. This simplifies the
+ REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+ characters after newlines into the fastmap. This way, we just try
+ every character. */
+ preg->fastmap = 0;
+
+ if (cflags & REG_ICASE)
+ {
+ unsigned i;
+
+ preg->translate = (char *) malloc (CHAR_SET_SIZE);
+ if (preg->translate == NULL)
+ return (int) REG_ESPACE;
+
+ /* Map uppercase characters to corresponding lowercase ones. */
+ for (i = 0; i < CHAR_SET_SIZE; i++)
+ preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
+ }
+ else
+ preg->translate = NULL;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+
+ preg->no_sub = !!(cflags & REG_NOSUB);
+
+ /* POSIX says a null character in the pattern terminates it, so we
+ can use strlen here in compiling the pattern. */
+ ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+ return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ int ret;
+ struct re_registers regs;
+ regex_t private_preg;
+ int len = strlen (string);
+ boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+ private_preg = *preg;
+
+ private_preg.not_bol = !!(eflags & REG_NOTBOL);
+ private_preg.not_eol = !!(eflags & REG_NOTEOL);
+
+ /* The user has told us exactly how many registers to return
+ information about, via `nmatch'. We have to pass that on to the
+ matching routines. */
+ private_preg.regs_allocated = REGS_FIXED;
+
+ if (want_reg_info)
+ {
+ regs.num_regs = nmatch;
+ regs.start = TALLOC (nmatch, regoff_t);
+ regs.end = TALLOC (nmatch, regoff_t);
+ if (regs.start == NULL || regs.end == NULL)
+ return (int) REG_NOMATCH;
+ }
+
+ /* Perform the searching operation. */
+ ret = re_search (&private_preg, string, len,
+ /* start: */ 0, /* range: */ len,
+ want_reg_info ? &regs : (struct re_registers *) 0);
+
+ /* Copy the register information to the POSIX structure. */
+ if (want_reg_info)
+ {
+ if (ret >= 0)
+ {
+ unsigned r;
+
+ for (r = 0; r < nmatch; r++)
+ {
+ pmatch[r].rm_so = regs.start[r];
+ pmatch[r].rm_eo = regs.end[r];
+ }
+ }
+
+ /* If we needed the temporary register info, free the space now. */
+ free (regs.start);
+ free (regs.end);
+ }
+
+ /* We want zero return to mean success, unlike `re_search'. */
+ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (errcode < 0
+ || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (errbuf_size != 0)
+ {
+ if (msg_size > errbuf_size)
+ {
+ strncpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+ }
+ else
+ strcpy (errbuf, msg);
+ }
+
+ return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ if (preg->buffer != NULL)
+ free (preg->buffer);
+ preg->buffer = NULL;
+
+ preg->allocated = 0;
+ preg->used = 0;
+
+ if (preg->fastmap != NULL)
+ free (preg->fastmap);
+ preg->fastmap = NULL;
+ preg->fastmap_accurate = 0;
+
+ if (preg->translate != NULL)
+ free (preg->translate);
+ preg->translate = NULL;
+}
+
+#endif /* not emacs */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/tar/regex.h b/gnu/usr.bin/tar/regex.h
new file mode 100644
index 000000000000..0840861da369
--- /dev/null
+++ b/gnu/usr.bin/tar/regex.h
@@ -0,0 +1,490 @@
+/* Definitions for data structures and routines for the regular
+ expression library, version 0.11.
+
+ Copyright (C) 1985, 89, 90, 91, 92 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+ <regex.h>. */
+
+#ifdef VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+ should be there. */
+#include <stddef.h>
+#endif
+
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+#define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+ replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Not implemented. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are
+ sometimes used as array indexes. */
+ unsigned char *buffer;
+
+ /* Number of bytes to which `buffer' points. */
+ unsigned long allocated;
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long used;
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t syntax;
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses
+ the fastmap, if there is one, to skip over impossible
+ starting points for matches. */
+ char *fastmap;
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation
+ is applied to a pattern when it is compiled and to a string
+ when it is matched. */
+ char *translate;
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see
+ whether or not we should use the fastmap, so we don't set
+ this absolutely perfectly; see `re_compile_fastmap' (the
+ `duplicate' case). */
+ unsigned can_be_null : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+ unsigned regs_allocated : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned fastmap_accurate : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned no_sub : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the
+ beginning of the string. */
+ unsigned not_bol : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned not_eol : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value. It is
+ defined both in `regex.c' and here. */
+#define RE_EXACTN_VALUE 1
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+/* To avoid duplicating every routine declaration -- once with a
+ prototype (if we are ANSI), and once without (if we aren't) -- we
+ use the following macro to declare argument types. This
+ unfortunately clutters up the declarations a bit, but I think it's
+ worth it. */
+
+#if __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern
+ _RE_ARGS ((const char *pattern, int length,
+ struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers
+ _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+ unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+/* 4.2 bsd compatibility. */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+
+/* POSIX compatibility. */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+ _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags));
+extern size_t regerror
+ _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+ size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/tar/rmt.h b/gnu/usr.bin/tar/rmt.h
new file mode 100644
index 000000000000..2155223954c3
--- /dev/null
+++ b/gnu/usr.bin/tar/rmt.h
@@ -0,0 +1,98 @@
+/* Definitions for communicating with a remote tape drive.
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if !defined(_POSIX_VERSION)
+#ifdef __MSDOS__
+#include <io.h>
+#else /* !__MSDOS__ */
+extern off_t lseek ();
+#endif /* __MSDOS__ */
+#endif /* _POSIX_VERSION */
+
+#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
+
+#else /* !NO_REMOTE */
+
+#define __REM_BIAS 128
+#define RMTIOCTL
+
+#ifndef O_CREAT
+#define O_CREAT 01000
+#endif
+
+extern char *__rmt_path;
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#ifndef index
+#define index strchr
+#endif
+#else
+extern char *index ();
+#endif
+
+#define _remdev(path) (!f_force_local && (__rmt_path=index(path, ':')))
+#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
+
+int __rmt_open ();
+int __rmt_close ();
+int __rmt_read ();
+int __rmt_write ();
+long __rmt_lseek ();
+int __rmt_ioctl ();
+#endif /* !NO_REMOTE */
diff --git a/gnu/usr.bin/tar/rtapelib.c b/gnu/usr.bin/tar/rtapelib.c
new file mode 100644
index 000000000000..eece76ffcd0f
--- /dev/null
+++ b/gnu/usr.bin/tar/rtapelib.c
@@ -0,0 +1,582 @@
+/* Functions for communicating with a remote tape drive.
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* 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
+
+ Originally written by Jeff Lee, modified some by Arnold Robbins.
+ Redone as a library that can replace open, read, write, etc., by
+ Fred Fish, with some additional work by Arnold Robbins.
+ Modified to make all rmtXXX calls into macros for speed by Jay Fenlason.
+ Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+/* Maximum size of a fully qualified host name. */
+#define MAXHOSTLEN 257
+
+/* Size of buffers for reading and writing commands to rmt.
+ (An arbitrary limit.) */
+#define CMDBUFSIZE 64
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+/* Maximum number of simultaneous remote tape connections.
+ (Another arbitrary limit.) */
+#define MAXUNIT 4
+
+/* Return the parent's read side of remote tape connection FILDES. */
+#define READ(fildes) (from_rmt[fildes][0])
+
+/* Return the parent's write side of remote tape connection FILDES. */
+#define WRITE(fildes) (to_rmt[fildes][1])
+
+/* The pipes for receiving data from remote tape drives. */
+static int from_rmt[MAXUNIT][2] =
+{-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* The pipes for sending data to remote tape drives. */
+static int to_rmt[MAXUNIT][2] =
+{-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Temporary variable used by macros in rmt.h. */
+char *__rmt_path;
+
+/* Close remote tape connection FILDES. */
+
+static void
+_rmt_shutdown (fildes)
+ int fildes;
+{
+ close (READ (fildes));
+ close (WRITE (fildes));
+ READ (fildes) = -1;
+ WRITE (fildes) = -1;
+}
+
+/* Attempt to perform the remote tape command specified in BUF
+ on remote tape connection FILDES.
+ Return 0 if successful, -1 on error. */
+
+static int
+command (fildes, buf)
+ int fildes;
+ char *buf;
+{
+ register int buflen;
+ RETSIGTYPE (*pipe_handler) ();
+
+ /* Save the current pipe handler and try to make the request. */
+
+ pipe_handler = signal (SIGPIPE, SIG_IGN);
+ buflen = strlen (buf);
+ if (write (WRITE (fildes), buf, buflen) == buflen)
+ {
+ signal (SIGPIPE, pipe_handler);
+ return 0;
+ }
+
+ /* Something went wrong. Close down and go home. */
+
+ signal (SIGPIPE, pipe_handler);
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+}
+
+/* Read and return the status from remote tape connection FILDES.
+ If an error occurred, return -1 and set errno. */
+
+static int
+status (fildes)
+ int fildes;
+{
+ int i;
+ char c, *cp;
+ char buffer[CMDBUFSIZE];
+
+ /* Read the reply command line. */
+
+ for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++)
+ {
+ if (read (READ (fildes), cp, 1) != 1)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ if (*cp == '\n')
+ {
+ *cp = '\0';
+ break;
+ }
+ }
+
+ if (i == CMDBUFSIZE)
+ {
+ _rmt_shutdown (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);
+ /* Skip the error message line. */
+ while (read (READ (fildes), &c, 1) == 1)
+ if (c == '\n')
+ break;
+
+ if (*cp == 'F')
+ _rmt_shutdown (fildes);
+
+ return -1;
+ }
+
+ /* Check for mis-synced pipes. */
+
+ if (*cp != 'A')
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+
+ /* Got an `A' (success) response. */
+ return atoi (cp + 1);
+}
+
+#ifdef HAVE_NETDB_H
+/* Execute /etc/rmt as user USER on remote system HOST using rexec.
+ Return a file descriptor of a bidirectional socket for stdin and stdout.
+ If USER is NULL, or an empty string, use the current username.
+
+ 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;
+{
+ 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 = 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 /* HAVE_NETDB_H */
+
+/* Open a magtape device on the system specified in PATH, as the given user.
+ PATH has the form `[user@]system:/dev/????'.
+ If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
+
+ OFLAG is O_RDONLY, O_WRONLY, etc.
+ MODE is ignored; 0666 is always used.
+
+ If successful, return the remote tape pipe number plus BIAS.
+ On error, return -1. */
+
+int
+__rmt_open (path, oflag, mode, bias)
+ char *path;
+ int oflag;
+ int mode;
+ int bias;
+{
+ int i, rc;
+ char buffer[CMDBUFSIZE]; /* Command buffer. */
+ char system[MAXHOSTLEN]; /* The remote host name. */
+ char device[CMDBUFSIZE]; /* The remote device name. */
+ char login[CMDBUFSIZE]; /* The remote user name. */
+ char *sys, *dev, *user; /* For copying into the above buffers. */
+
+ sys = system;
+ dev = device;
+ user = login;
+
+ /* Find an unused 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 the system and device, and optional user.
+ Don't munge the original string. */
+
+ while (*path != '@'
+#ifdef COMPAT
+ && *path != '.'
+#endif
+ && *path != ':')
+ {
+ *sys++ = *path++;
+ }
+ *sys = '\0';
+ path++;
+
+ if (*(path - 1) == '@')
+ {
+ /* Saw user part of user@host. Start over. */
+ strcpy (user, system);
+ sys = system;
+ 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 HAVE_NETDB_H
+ /* Execute the remote command using rexec. */
+ READ (i) = WRITE (i) = _rmt_rexec (system, login);
+ if (READ (i) < 0)
+ return -1;
+#else /* !HAVE_NETDB_H */
+ /* Set up the pipes for the `rsh' command, and fork. */
+
+ if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1)
+ return -1;
+
+ rc = fork ();
+ if (rc == -1)
+ return -1;
+
+ if (rc == 0)
+ {
+ /* Child. */
+ close (0);
+ dup (to_rmt[i][0]);
+ close (to_rmt[i][0]);
+ close (to_rmt[i][1]);
+
+ close (1);
+ dup (from_rmt[i][1]);
+ close (from_rmt[i][0]);
+ close (from_rmt[i][1]);
+
+ setuid (getuid ());
+ 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 ("cannot execute remote shell");
+ _exit (1);
+ }
+
+ /* Parent. */
+ close (to_rmt[i][0]);
+ close (from_rmt[i][1]);
+#endif /* !HAVE_NETDB_H */
+
+ /* 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;
+}
+
+/* Close remote tape connection FILDES and shut down.
+ Return 0 if successful, -1 on error. */
+
+int
+__rmt_close (fildes)
+ int fildes;
+{
+ int rc;
+
+ if (command (fildes, "C\n") == -1)
+ return -1;
+
+ rc = status (fildes);
+ _rmt_shutdown (fildes);
+ return rc;
+}
+
+/* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
+ Return the number of bytes read on success, -1 on error. */
+
+int
+__rmt_read (fildes, buf, nbyte)
+ int fildes;
+ char *buf;
+ unsigned int nbyte;
+{
+ int rc, i;
+ char buffer[CMDBUFSIZE];
+
+ 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 - i);
+ if (nbyte <= 0)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+/* Write NBYTE bytes from BUF to remote tape connection FILDES.
+ Return the number of bytes written on success, -1 on error. */
+
+int
+__rmt_write (fildes, buf, nbyte)
+ int fildes;
+ char *buf;
+ unsigned int nbyte;
+{
+ char buffer[CMDBUFSIZE];
+ RETSIGTYPE (*pipe_handler) ();
+
+ sprintf (buffer, "W%d\n", nbyte);
+ if (command (fildes, buffer) == -1)
+ return -1;
+
+ pipe_handler = signal (SIGPIPE, SIG_IGN);
+ if (write (WRITE (fildes), buf, nbyte) == nbyte)
+ {
+ signal (SIGPIPE, pipe_handler);
+ return status (fildes);
+ }
+
+ /* Write error. */
+ signal (SIGPIPE, pipe_handler);
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+}
+
+/* Perform an imitation lseek operation on remote tape connection FILDES.
+ Return the new file offset if successful, -1 if on error. */
+
+long
+__rmt_lseek (fildes, offset, whence)
+ int fildes;
+ long offset;
+ int whence;
+{
+ char buffer[CMDBUFSIZE];
+
+ sprintf (buffer, "L%ld\n%d\n", offset, whence);
+ if (command (fildes, buffer) == -1)
+ return -1;
+
+ return status (fildes);
+}
+
+/* Perform a raw tape operation on remote tape connection FILDES.
+ Return the results of the ioctl, or -1 on error. */
+
+#ifdef MTIOCTOP
+int
+__rmt_ioctl (fildes, op, arg)
+ int fildes, op;
+ char *arg;
+{
+ char c;
+ int rc, cnt;
+ char buffer[CMDBUFSIZE];
+
+ switch (op)
+ {
+ default:
+ errno = EINVAL;
+ return -1;
+
+ case MTIOCTOP:
+ /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
+ 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); /* Return the count. */
+
+ case MTIOCGET:
+ /* Grab the status and read it directly into the structure.
+ This assumes that the status buffer is not padded
+ and that 2 shorts fit in a long without any word
+ alignment problems; i.e., 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_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ /* 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
diff --git a/gnu/usr.bin/tar/tar.c b/gnu/usr.bin/tar/tar.c
new file mode 100644
index 000000000000..938258233980
--- /dev/null
+++ b/gnu/usr.bin/tar/tar.c
@@ -0,0 +1,1504 @@
+/* Tar -- a tape archiver.
+ Copyright (C) 1988, 1992, 1993 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 2, 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. */
+
+/*
+ * A tar (tape archiver) program.
+ *
+ * Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
+ */
+
+#include <stdio.h>
+#include <sys/types.h> /* Needed for typedefs in tar.h */
+#include "getopt.h"
+
+/*
+ * The following causes "tar.h" to produce definitions of all the
+ * global variables, rather than just "extern" declarations of them.
+ */
+#define TAR_EXTERN /**/
+#include "tar.h"
+
+#include "port.h"
+#include "regex.h"
+#include "fnmatch.h"
+
+/*
+ * We should use a conversion routine that does reasonable error
+ * checking -- atoi doesn't. For now, punt. FIXME.
+ */
+#define intconv atoi
+PTR ck_malloc ();
+PTR ck_realloc ();
+extern int getoldopt ();
+extern void read_and ();
+extern void list_archive ();
+extern void extract_archive ();
+extern void diff_archive ();
+extern void create_archive ();
+extern void update_archive ();
+extern void junk_archive ();
+extern void init_volume_number ();
+extern void closeout_volume_number ();
+
+/* JF */
+extern time_t get_date ();
+
+time_t new_time;
+
+static FILE *namef; /* File to read names from */
+static char **n_argv; /* Argv used by name routines */
+static int n_argc; /* Argc used by name routines */
+static char **n_ind; /* Store an array of names */
+static int n_indalloc; /* How big is the array? */
+static int n_indused; /* How many entries does it have? */
+static int n_indscan; /* How many of the entries have we scanned? */
+
+
+extern FILE *msg_file;
+
+int check_exclude ();
+void add_exclude ();
+void add_exclude_file ();
+void addname ();
+void describe ();
+void diff_init ();
+void extr_init ();
+int is_regex ();
+void name_add ();
+void name_init ();
+void options ();
+char *un_quote_string ();
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+#ifndef DEFBLOCKING
+#define DEFBLOCKING 20
+#endif
+
+#ifndef DEF_AR_FILE
+#define DEF_AR_FILE "tar.out"
+#endif
+
+/* For long options that unconditionally set a single flag, we have getopt
+ do it. For the others, we share the code for the equivalent short
+ named option, the name of which is stored in the otherwise-unused `val'
+ field of the `struct option'; for long options that have no equivalent
+ short option, we use nongraphic characters as pseudo short option
+ characters, starting (for no particular reason) with character 10. */
+
+struct option long_options[] =
+{
+ {"create", 0, 0, 'c'},
+ {"append", 0, 0, 'r'},
+ {"extract", 0, 0, 'x'},
+ {"get", 0, 0, 'x'},
+ {"list", 0, 0, 't'},
+ {"update", 0, 0, 'u'},
+ {"catenate", 0, 0, 'A'},
+ {"concatenate", 0, 0, 'A'},
+ {"compare", 0, 0, 'd'},
+ {"diff", 0, 0, 'd'},
+ {"delete", 0, 0, 14},
+ {"help", 0, 0, 12},
+
+ {"null", 0, 0, 16},
+ {"directory", 1, 0, 'C'},
+ {"record-number", 0, &f_sayblock, 1},
+ {"files-from", 1, 0, 'T'},
+ {"label", 1, 0, 'V'},
+ {"exclude-from", 1, 0, 'X'},
+ {"exclude", 1, 0, 15},
+ {"file", 1, 0, 'f'},
+ {"block-size", 1, 0, 'b'},
+ {"version", 0, 0, 11},
+ {"verbose", 0, 0, 'v'},
+ {"totals", 0, &f_totals, 1},
+
+ {"read-full-blocks", 0, &f_reblock, 1},
+ {"starting-file", 1, 0, 'K'},
+ {"to-stdout", 0, &f_exstdout, 1},
+ {"ignore-zeros", 0, &f_ignorez, 1},
+ {"keep-old-files", 0, 0, 'k'},
+ {"same-permissions", 0, &f_use_protection, 1},
+ {"preserve-permissions", 0, &f_use_protection, 1},
+ {"modification-time", 0, &f_modified, 1},
+ {"preserve", 0, 0, 10},
+ {"same-order", 0, &f_sorted_names, 1},
+ {"same-owner", 0, &f_do_chown, 1},
+ {"preserve-order", 0, &f_sorted_names, 1},
+
+ {"newer", 1, 0, 'N'},
+ {"after-date", 1, 0, 'N'},
+ {"newer-mtime", 1, 0, 13},
+ {"incremental", 0, 0, 'G'},
+ {"listed-incremental", 1, 0, 'g'},
+ {"multi-volume", 0, &f_multivol, 1},
+ {"info-script", 1, 0, 'F'},
+ {"new-volume-script", 1, 0, 'F'},
+ {"absolute-paths", 0, &f_absolute_paths, 1},
+ {"interactive", 0, &f_confirm, 1},
+ {"confirmation", 0, &f_confirm, 1},
+
+ {"verify", 0, &f_verify, 1},
+ {"dereference", 0, &f_follow_links, 1},
+ {"one-file-system", 0, &f_local_filesys, 1},
+ {"old-archive", 0, 0, 'o'},
+ {"portability", 0, 0, 'o'},
+ {"compress", 0, 0, 'Z'},
+ {"uncompress", 0, 0, 'Z'},
+ {"block-compress", 0, &f_compress_block, 1},
+ {"gzip", 0, 0, 'z'},
+ {"ungzip", 0, 0, 'z'},
+ {"use-compress-program", 1, 0, 18},
+
+
+ {"same-permissions", 0, &f_use_protection, 1},
+ {"sparse", 0, &f_sparse_files, 1},
+ {"tape-length", 1, 0, 'L'},
+ {"remove-files", 0, &f_remove_files, 1},
+ {"ignore-failed-read", 0, &f_ignore_failed_read, 1},
+ {"checkpoint", 0, &f_checkpoint, 1},
+ {"show-omitted-dirs", 0, &f_show_omitted_dirs, 1},
+ {"volno-file", 1, 0, 17},
+ {"force-local", 0, &f_force_local, 1},
+ {"atime-preserve", 0, &f_atime_preserve, 1},
+
+ {0, 0, 0, 0}
+};
+
+/*
+ * Main routine for tar.
+ */
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char version_string[];
+
+ tar = argv[0]; /* JF: was "tar" Set program name */
+ filename_terminator = '\n';
+ errors = 0;
+
+ options (argc, argv);
+
+ if (!n_argv)
+ name_init (argc, argv);
+
+ if (f_volno_file)
+ init_volume_number ();
+
+ switch (cmd_mode)
+ {
+ case CMD_CAT:
+ case CMD_UPDATE:
+ case CMD_APPEND:
+ update_archive ();
+ break;
+ case CMD_DELETE:
+ junk_archive ();
+ break;
+ case CMD_CREATE:
+ create_archive ();
+ if (f_totals)
+ fprintf (stderr, "Total bytes written: %d\n", tot_written);
+ break;
+ case CMD_EXTRACT:
+ if (f_volhdr)
+ {
+ const char *err;
+ label_pattern = (struct re_pattern_buffer *)
+ ck_malloc (sizeof *label_pattern);
+ err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
+ label_pattern);
+ if (err)
+ {
+ fprintf (stderr, "Bad regular expression: %s\n",
+ err);
+ errors++;
+ break;
+ }
+
+ }
+ extr_init ();
+ read_and (extract_archive);
+ break;
+ case CMD_LIST:
+ if (f_volhdr)
+ {
+ const char *err;
+ label_pattern = (struct re_pattern_buffer *)
+ ck_malloc (sizeof *label_pattern);
+ err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
+ label_pattern);
+ if (err)
+ {
+ fprintf (stderr, "Bad regular expression: %s\n",
+ err);
+ errors++;
+ break;
+ }
+ }
+ read_and (list_archive);
+#if 0
+ if (!errors)
+ errors = different;
+#endif
+ break;
+ case CMD_DIFF:
+ diff_init ();
+ read_and (diff_archive);
+ break;
+ case CMD_VERSION:
+ fprintf (stderr, "%s\n", version_string);
+ break;
+ case CMD_NONE:
+ msg ("you must specify exactly one of the r, c, t, x, or d options\n");
+ fprintf (stderr, "For more information, type ``%s --help''.\n", tar);
+ exit (EX_ARGSBAD);
+ }
+ if (f_volno_file)
+ closeout_volume_number ();
+ exit (errors);
+ /* NOTREACHED */
+}
+
+
+/*
+ * Parse the options for tar.
+ */
+void
+options (argc, argv)
+ int argc;
+ char **argv;
+{
+ register int c; /* Option letter */
+ int ind = -1;
+
+ /* Set default option values */
+ blocking = DEFBLOCKING; /* From Makefile */
+ ar_files = (char **) ck_malloc (sizeof (char *) * 10);
+ ar_files_len = 10;
+ n_ar_files = 0;
+ cur_ar_file = 0;
+
+ /* Parse options */
+ while ((c = getoldopt (argc, argv,
+ "-01234567Ab:BcC:df:F:g:GhikK:lL:mMN:oOpPrRsStT:uvV:wWxX:zZ",
+ long_options, &ind)) != EOF)
+ {
+ switch (c)
+ {
+ case 0: /* long options that set a single flag */
+ break;
+ case 1:
+ /* File name or non-parsed option */
+ name_add (optarg);
+ break;
+ case 'C':
+ name_add ("-C");
+ name_add (optarg);
+ break;
+ case 10: /* preserve */
+ f_use_protection = f_sorted_names = 1;
+ break;
+ case 11:
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_VERSION;
+ break;
+ case 12: /* help */
+ printf ("This is GNU tar, the tape archiving program.\n");
+ describe ();
+ exit (1);
+ case 13:
+ f_new_files++;
+ goto get_newer;
+
+ case 14: /* Delete in the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_DELETE;
+ break;
+
+ case 15:
+ f_exclude++;
+ add_exclude (optarg);
+ break;
+
+ case 16: /* -T reads null terminated filenames. */
+ filename_terminator = '\0';
+ break;
+
+ case 17:
+ f_volno_file = optarg;
+ break;
+
+ case 18:
+ if (f_compressprog)
+ {
+ msg ("Only one compression option permitted\n");
+ exit (EX_ARGSBAD);
+ }
+ f_compressprog = optarg;
+ break;
+
+ case 'g': /* We are making a GNU dump; save
+ directories at the beginning of
+ the archive, and include in each
+ directory its contents */
+ if (f_oldarch)
+ goto badopt;
+ f_gnudump++;
+ gnu_dumpfile = optarg;
+ break;
+
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ /* JF this'll have to be modified for other
+ systems, of course! */
+ int d, add;
+ static char buf[50];
+
+ d = getoldopt (argc, argv, "lmh");
+#ifdef MAYBEDEF
+ sprintf (buf, "/dev/rmt/%d%c", c, d);
+#else
+#ifndef LOW_NUM
+#define LOW_NUM 0
+#define MID_NUM 8
+#define HGH_NUM 16
+#endif
+ if (d == 'l')
+ add = LOW_NUM;
+ else if (d == 'm')
+ add = MID_NUM;
+ else if (d == 'h')
+ add = HGH_NUM;
+ else
+ goto badopt;
+
+ sprintf (buf, "/dev/rmt%d", add + c - '0');
+#endif
+ if (n_ar_files == ar_files_len)
+ ar_files
+ = (char **)
+ ck_malloc (sizeof (char *)
+ * (ar_files_len *= 2));
+ ar_files[n_ar_files++] = buf;
+ }
+ break;
+
+ case 'A': /* Arguments are tar files,
+ just cat them onto the end
+ of the archive. */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_CAT;
+ break;
+
+ case 'b': /* Set blocking factor */
+ blocking = intconv (optarg);
+ break;
+
+ case 'B': /* Try to reblock input */
+ f_reblock++; /* For reading 4.2BSD pipes */
+ break;
+
+ case 'c': /* Create an archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_CREATE;
+ break;
+
+#if 0
+ case 'C':
+ if (chdir (optarg) < 0)
+ msg_perror ("Can't change directory to %d", optarg);
+ break;
+#endif
+
+ case 'd': /* Find difference tape/disk */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_DIFF;
+ break;
+
+ case 'f': /* Use ar_file for the archive */
+ if (n_ar_files == ar_files_len)
+ ar_files
+ = (char **) ck_malloc (sizeof (char *)
+ * (ar_files_len *= 2));
+
+ ar_files[n_ar_files++] = optarg;
+ break;
+
+ case 'F':
+ /* Since -F is only useful with -M , make it implied */
+ f_run_script_at_end++;/* run this script at the end */
+ info_script = optarg; /* of each tape */
+ f_multivol++;
+ break;
+
+ case 'G': /* We are making a GNU dump; save
+ directories at the beginning of
+ the archive, and include in each
+ directory its contents */
+ if (f_oldarch)
+ goto badopt;
+ f_gnudump++;
+ gnu_dumpfile = 0;
+ break;
+
+ case 'h':
+ f_follow_links++; /* follow symbolic links */
+ break;
+
+ case 'i':
+ f_ignorez++; /* Ignore zero records (eofs) */
+ /*
+ * This can't be the default, because Unix tar
+ * writes two records of zeros, then pads out the
+ * block with garbage.
+ */
+ break;
+
+ case 'k': /* Don't overwrite files */
+#ifdef NO_OPEN3
+ msg ("can't keep old files on this system");
+ exit (EX_ARGSBAD);
+#else
+ f_keep++;
+#endif
+ break;
+
+ case 'K':
+ f_startfile++;
+ addname (optarg);
+ break;
+
+ case 'l': /* When dumping directories, don't
+ dump files/subdirectories that are
+ on other filesystems. */
+ f_local_filesys++;
+ break;
+
+ case 'L':
+ tape_length = intconv (optarg);
+ f_multivol++;
+ break;
+ case 'm':
+ f_modified++;
+ break;
+
+ case 'M': /* Make Multivolume archive:
+ When we can't write any more
+ into the archive, re-open it,
+ and continue writing */
+ f_multivol++;
+ break;
+
+ case 'N': /* Only write files newer than X */
+ get_newer:
+ f_new_files++;
+ new_time = get_date (optarg, (PTR) 0);
+ if (new_time == (time_t) - 1)
+ {
+ msg ("invalid date format `%s'", optarg);
+ exit (EX_ARGSBAD);
+ }
+ break;
+
+ case 'o': /* Generate old archive */
+ if (f_gnudump /* || f_dironly */ )
+ goto badopt;
+ f_oldarch++;
+ break;
+
+ case 'O':
+ f_exstdout++;
+ break;
+
+ case 'p':
+ f_use_protection++;
+ break;
+
+ case 'P':
+ f_absolute_paths++;
+ break;
+
+ case 'r': /* Append files to the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_APPEND;
+ break;
+
+ case 'R':
+ f_sayblock++; /* Print block #s for debug */
+ break; /* of bad tar archives */
+
+ case 's':
+ f_sorted_names++; /* Names to extr are sorted */
+ break;
+
+ case 'S': /* deal with sparse files */
+ f_sparse_files++;
+ break;
+ case 't':
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_LIST;
+ f_verbose++; /* "t" output == "cv" or "xv" */
+ break;
+
+ case 'T':
+ name_file = optarg;
+ f_namefile++;
+ break;
+
+ case 'u': /* Append files to the archive that
+ aren't there, or are newer than the
+ copy in the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_UPDATE;
+ break;
+
+ case 'v':
+ f_verbose++;
+ break;
+
+ case 'V':
+ f_volhdr = optarg;
+ break;
+
+ case 'w':
+ f_confirm++;
+ break;
+
+ case 'W':
+ f_verify++;
+ break;
+
+ case 'x': /* Extract files from the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_EXTRACT;
+ break;
+
+ case 'X':
+ f_exclude++;
+ add_exclude_file (optarg);
+ break;
+
+ case 'z':
+ if (f_compressprog)
+ {
+ msg ("Only one compression option permitted\n");
+ exit (EX_ARGSBAD);
+ }
+ f_compressprog = "gzip";
+ break;
+
+ case 'Z':
+ if (f_compressprog)
+ {
+ msg ("Only one compression option permitted\n");
+ exit (EX_ARGSBAD);
+ }
+ f_compressprog = "compress";
+ break;
+
+ case '?':
+ badopt:
+ msg ("Unknown option. Use '%s --help' for a complete list of options.", tar);
+ exit (EX_ARGSBAD);
+
+ }
+ }
+
+ blocksize = blocking * RECORDSIZE;
+ if (n_ar_files == 0)
+ {
+ n_ar_files = 1;
+ ar_files[0] = getenv ("TAPE"); /* From environment, or */
+ if (ar_files[0] == 0)
+ ar_files[0] = DEF_AR_FILE; /* From Makefile */
+ }
+ if (n_ar_files > 1 && !f_multivol)
+ {
+ msg ("Multiple archive files requires --multi-volume\n");
+ exit (EX_ARGSBAD);
+ }
+ if (f_compress_block && !f_compressprog)
+ {
+ msg ("You must use a compression option (--gzip, --compress\n\
+or --use-compress-program) with --block-compress.\n");
+ exit (EX_ARGSBAD);
+ }
+}
+
+
+/*
+ * Print as much help as the user's gonna get.
+ *
+ * We have to sprinkle in the KLUDGE lines because too many compilers
+ * cannot handle character strings longer than about 512 bytes. Yuk!
+ * In particular, MS-DOS and Xenix MSC and PDP-11 V7 Unix have this
+ * problem.
+ */
+void
+describe ()
+{
+ puts ("choose one of the following:");
+ fputs ("\
+-A, --catenate,\n\
+ --concatenate append tar files to an archive\n\
+-c, --create create a new archive\n\
+-d, --diff,\n\
+ --compare find differences between archive and file system\n\
+--delete delete from the archive (not for use on mag tapes!)\n\
+-r, --append append files to the end of an archive\n\
+-t, --list list the contents of an archive\n\
+-u, --update only append files that are newer than copy in archive\n\
+-x, --extract,\n\
+ --get extract files from an archive\n", stdout);
+
+ fprintf (stdout, "\
+Other options:\n\
+--atime-preserve don't change access times on dumped files\n\
+-b, --block-size N block size of Nx512 bytes (default N=%d)\n", DEFBLOCKING);
+ fputs ("\
+-B, --read-full-blocks reblock as we read (for reading 4.2BSD pipes)\n\
+-C, --directory DIR change to directory DIR\n\
+--checkpoint print directory names while reading the archive\n\
+", stdout); /* KLUDGE */
+ fprintf (stdout, "\
+-f, --file [HOSTNAME:]F use archive file or device F (default %s)\n",
+ DEF_AR_FILE);
+ fputs ("\
+--force-local archive file is local even if has a colon\n\
+-F, --info-script F\n\
+ --new-volume-script F run script at end of each tape (implies -M)\n\
+-G, --incremental create/list/extract old GNU-format incremental backup\n\
+-g, --listed-incremental F create/list/extract new GNU-format incremental backup\n\
+-h, --dereference don't dump symlinks; dump the files they point to\n\
+-i, --ignore-zeros ignore blocks of zeros in archive (normally mean EOF)\n\
+--ignore-failed-read don't exit with non-zero status on unreadable files\n\
+-k, --keep-old-files keep existing files; don't overwrite them from archive\n\
+-K, --starting-file F begin at file F in the archive\n\
+-l, --one-file-system stay in local file system when creating an archive\n\
+-L, --tape-length N change tapes after writing N*1024 bytes\n\
+", stdout); /* KLUDGE */
+ fputs ("\
+-m, --modification-time don't extract file modified time\n\
+-M, --multi-volume create/list/extract multi-volume archive\n\
+-N, --after-date DATE,\n\
+ --newer DATE only store files newer than DATE\n\
+-o, --old-archive,\n\
+ --portability write a V7 format archive, rather than ANSI format\n\
+-O, --to-stdout extract files to standard output\n\
+-p, --same-permissions,\n\
+ --preserve-permissions extract all protection information\n\
+-P, --absolute-paths don't strip leading `/'s from file names\n\
+--preserve like -p -s\n\
+", stdout); /* KLUDGE */
+ fputs ("\
+-R, --record-number show record number within archive with each message\n\
+--remove-files remove files after adding them to the archive\n\
+-s, --same-order,\n\
+ --preserve-order list of names to extract is sorted to match archive\n\
+--same-owner create extracted files with the same ownership \n\
+-S, --sparse handle sparse files efficiently\n\
+-T, --files-from F get names to extract or create from file F\n\
+--null -T reads null-terminated names, disable -C\n\
+--totals print total bytes written with --create\n\
+-v, --verbose verbosely list files processed\n\
+-V, --label NAME create archive with volume name NAME\n\
+--version print tar program version number\n\
+-w, --interactive,\n\
+ --confirmation ask for confirmation for every action\n\
+", stdout); /* KLUDGE */
+ fputs ("\
+-W, --verify attempt to verify the archive after writing it\n\
+--exclude FILE exclude file FILE\n\
+-X, --exclude-from FILE exclude files listed in FILE\n\
+-Z, --compress,\n\
+ --uncompress filter the archive through compress\n\
+-z, --gzip,\n\
+ --ungzip filter the archive through gzip\n\
+--use-compress-program PROG\n\
+ filter the archive through PROG (which must accept -d)\n\
+--block-compress block the output of compression program for tapes\n\
+-[0-7][lmh] specify drive and density\n\
+", stdout);
+}
+
+void
+name_add (name)
+ char *name;
+{
+ if (n_indalloc == n_indused)
+ {
+ n_indalloc += 10;
+ n_ind = (char **) (n_indused ? ck_realloc (n_ind, n_indalloc * sizeof (char *)): ck_malloc (n_indalloc * sizeof (char *)));
+ }
+ n_ind[n_indused++] = name;
+}
+
+/*
+ * Set up to gather file names for tar.
+ *
+ * They can either come from stdin or from argv.
+ */
+void
+name_init (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ if (f_namefile)
+ {
+ if (optind < argc)
+ {
+ msg ("too many args with -T option");
+ exit (EX_ARGSBAD);
+ }
+ if (!strcmp (name_file, "-"))
+ {
+ namef = stdin;
+ }
+ else
+ {
+ namef = fopen (name_file, "r");
+ if (namef == NULL)
+ {
+ msg_perror ("can't open file %s", name_file);
+ exit (EX_BADFILE);
+ }
+ }
+ }
+ else
+ {
+ /* Get file names from argv, after options. */
+ n_argc = argc;
+ n_argv = argv;
+ }
+}
+
+/* Read the next filename read from STREAM and null-terminate it.
+ Put it into BUFFER, reallocating and adjusting *PBUFFER_SIZE if necessary.
+ Return the new value for BUFFER, or NULL at end of file. */
+
+char *
+read_name_from_file (buffer, pbuffer_size, stream)
+ char *buffer;
+ size_t *pbuffer_size;
+ FILE *stream;
+{
+ register int c;
+ register int indx = 0;
+ register size_t buffer_size = *pbuffer_size;
+
+ while ((c = getc (stream)) != EOF && c != filename_terminator)
+ {
+ if (indx == buffer_size)
+ {
+ buffer_size += NAMSIZ;
+ buffer = ck_realloc (buffer, buffer_size + 2);
+ }
+ buffer[indx++] = c;
+ }
+ if (indx == 0 && c == EOF)
+ return NULL;
+ if (indx == buffer_size)
+ {
+ buffer_size += NAMSIZ;
+ buffer = ck_realloc (buffer, buffer_size + 2);
+ }
+ buffer[indx] = '\0';
+ *pbuffer_size = buffer_size;
+ return buffer;
+}
+
+/*
+ * Get the next name from argv or the name file.
+ *
+ * Result is in static storage and can't be relied upon across two calls.
+ *
+ * If CHANGE_DIRS is non-zero, treat a filename of the form "-C" as
+ * meaning that the next filename is the name of a directory to change to.
+ * If `filename_terminator' is '\0', CHANGE_DIRS is effectively always 0.
+ */
+
+char *
+name_next (change_dirs)
+ int change_dirs;
+{
+ static char *buffer; /* Holding pattern */
+ static int buffer_siz;
+ register char *p;
+ register char *q = 0;
+ register int next_name_is_dir = 0;
+ extern char *un_quote_string ();
+
+ if (buffer_siz == 0)
+ {
+ buffer = ck_malloc (NAMSIZ + 2);
+ buffer_siz = NAMSIZ;
+ }
+ if (filename_terminator == '\0')
+ change_dirs = 0;
+tryagain:
+ if (namef == NULL)
+ {
+ if (n_indscan < n_indused)
+ p = n_ind[n_indscan++];
+ else if (optind < n_argc)
+ /* Names come from argv, after options */
+ p = n_argv[optind++];
+ else
+ {
+ if (q)
+ msg ("Missing filename after -C");
+ return NULL;
+ }
+
+ /* JF trivial support for -C option. I don't know if
+ chdir'ing at this point is dangerous or not.
+ It seems to work, which is all I ask. */
+ if (change_dirs && !q && p[0] == '-' && p[1] == 'C' && p[2] == '\0')
+ {
+ q = p;
+ goto tryagain;
+ }
+ if (q)
+ {
+ if (chdir (p) < 0)
+ msg_perror ("Can't chdir to %s", p);
+ q = 0;
+ goto tryagain;
+ }
+ /* End of JF quick -C hack */
+
+#if 0
+ if (f_exclude && check_exclude (p))
+ goto tryagain;
+#endif
+ return un_quote_string (p);
+ }
+ while (p = read_name_from_file (buffer, &buffer_siz, namef))
+ {
+ buffer = p;
+ if (*p == '\0')
+ continue; /* Ignore empty lines. */
+ q = p + strlen (p) - 1;
+ while (q > p && *q == '/')/* Zap trailing "/"s. */
+ *q-- = '\0';
+ if (change_dirs && next_name_is_dir == 0
+ && p[0] == '-' && p[1] == 'C' && p[2] == '\0')
+ {
+ next_name_is_dir = 1;
+ goto tryagain;
+ }
+ if (next_name_is_dir)
+ {
+ if (chdir (p) < 0)
+ msg_perror ("Can't change to directory %s", p);
+ next_name_is_dir = 0;
+ goto tryagain;
+ }
+#if 0
+ if (f_exclude && check_exclude (p))
+ goto tryagain;
+#endif
+ return un_quote_string (p);
+ }
+ return NULL;
+}
+
+
+/*
+ * Close the name file, if any.
+ */
+void
+name_close ()
+{
+
+ if (namef != NULL && namef != stdin)
+ fclose (namef);
+}
+
+
+/*
+ * Gather names in a list for scanning.
+ * Could hash them later if we really care.
+ *
+ * If the names are already sorted to match the archive, we just
+ * read them one by one. name_gather reads the first one, and it
+ * is called by name_match as appropriate to read the next ones.
+ * At EOF, the last name read is just left in the buffer.
+ * This option lets users of small machines extract an arbitrary
+ * number of files by doing "tar t" and editing down the list of files.
+ */
+void
+name_gather ()
+{
+ register char *p;
+ static struct name *namebuf; /* One-name buffer */
+ static namelen;
+ static char *chdir_name;
+
+ if (f_sorted_names)
+ {
+ if (!namelen)
+ {
+ namelen = NAMSIZ;
+ namebuf = (struct name *) ck_malloc (sizeof (struct name) + NAMSIZ);
+ }
+ p = name_next (0);
+ if (p)
+ {
+ if (*p == '-' && p[1] == 'C' && p[2] == '\0')
+ {
+ chdir_name = name_next (0);
+ p = name_next (0);
+ if (!p)
+ {
+ msg ("Missing file name after -C");
+ exit (EX_ARGSBAD);
+ }
+ namebuf->change_dir = chdir_name;
+ }
+ namebuf->length = strlen (p);
+ if (namebuf->length >= namelen)
+ {
+ namebuf = (struct name *) ck_realloc (namebuf, sizeof (struct name) + namebuf->length);
+ namelen = namebuf->length;
+ }
+ strncpy (namebuf->name, p, namebuf->length);
+ namebuf->name[namebuf->length] = 0;
+ namebuf->next = (struct name *) NULL;
+ namebuf->found = 0;
+ namelist = namebuf;
+ namelast = namelist;
+ }
+ return;
+ }
+
+ /* Non sorted names -- read them all in */
+ while (p = name_next (0))
+ addname (p);
+}
+
+/*
+ * Add a name to the namelist.
+ */
+void
+addname (name)
+ char *name; /* pointer to name */
+{
+ register int i; /* Length of string */
+ register struct name *p; /* Current struct pointer */
+ static char *chdir_name;
+ char *new_name ();
+
+ if (name[0] == '-' && name[1] == 'C' && name[2] == '\0')
+ {
+ chdir_name = name_next (0);
+ name = name_next (0);
+ if (!chdir_name)
+ {
+ msg ("Missing file name after -C");
+ exit (EX_ARGSBAD);
+ }
+ if (chdir_name[0] != '/')
+ {
+ char *path = ck_malloc (PATH_MAX);
+#if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION)
+ if (!getcwd (path, PATH_MAX))
+ {
+ 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
+ chdir_name = new_name (path, chdir_name);
+ free (path);
+ }
+ }
+
+ if (name)
+ {
+ i = strlen (name);
+ /*NOSTRICT*/
+ p = (struct name *) malloc ((unsigned) (sizeof (struct name) + i));
+ }
+ else
+ p = (struct name *) malloc ((unsigned) (sizeof (struct name)));
+ if (!p)
+ {
+ if (name)
+ msg ("cannot allocate mem for name '%s'.", name);
+ else
+ msg ("cannot allocate mem for chdir record.");
+ exit (EX_SYSTEM);
+ }
+ p->next = (struct name *) NULL;
+ if (name)
+ {
+ p->fake = 0;
+ p->length = i;
+ strncpy (p->name, name, i);
+ p->name[i] = '\0'; /* Null term */
+ }
+ else
+ p->fake = 1;
+ p->found = 0;
+ p->regexp = 0; /* Assume not a regular expression */
+ p->firstch = 1; /* Assume first char is literal */
+ p->change_dir = chdir_name;
+ p->dir_contents = 0; /* JF */
+ if (name)
+ {
+ if (index (name, '*') || index (name, '[') || index (name, '?'))
+ {
+ p->regexp = 1; /* No, it's a regexp */
+ if (name[0] == '*' || name[0] == '[' || name[0] == '?')
+ p->firstch = 0; /* Not even 1st char literal */
+ }
+ }
+
+ if (namelast)
+ namelast->next = p;
+ namelast = p;
+ if (!namelist)
+ namelist = p;
+}
+
+/*
+ * Return nonzero if name P (from an archive) matches any name from
+ * the namelist, zero if not.
+ */
+int
+name_match (p)
+ register char *p;
+{
+ register struct name *nlp;
+ register int len;
+
+again:
+ if (0 == (nlp = namelist)) /* Empty namelist is easy */
+ return 1;
+ if (nlp->fake)
+ {
+ if (nlp->change_dir && chdir (nlp->change_dir))
+ msg_perror ("Can't change to directory %d", nlp->change_dir);
+ namelist = 0;
+ return 1;
+ }
+ len = strlen (p);
+ for (; nlp != 0; nlp = nlp->next)
+ {
+ /* If first chars don't match, quick skip */
+ if (nlp->firstch && nlp->name[0] != p[0])
+ continue;
+
+ /* Regular expressions (shell globbing, actually). */
+ if (nlp->regexp)
+ {
+ if (fnmatch (nlp->name, p, FNM_LEADING_DIR) == 0)
+ {
+ nlp->found = 1; /* Remember it matched */
+ if (f_startfile)
+ {
+ free ((void *) namelist);
+ namelist = 0;
+ }
+ if (nlp->change_dir && chdir (nlp->change_dir))
+ msg_perror ("Can't change to directory %s", nlp->change_dir);
+ return 1; /* We got a match */
+ }
+ continue;
+ }
+
+ /* Plain Old Strings */
+ if (nlp->length <= len /* Archive len >= specified */
+ && (p[nlp->length] == '\0' || p[nlp->length] == '/')
+ /* Full match on file/dirname */
+ && strncmp (p, nlp->name, nlp->length) == 0) /* Name compare */
+ {
+ nlp->found = 1; /* Remember it matched */
+ if (f_startfile)
+ {
+ free ((void *) namelist);
+ namelist = 0;
+ }
+ if (nlp->change_dir && chdir (nlp->change_dir))
+ msg_perror ("Can't change to directory %s", nlp->change_dir);
+ return 1; /* We got a match */
+ }
+ }
+
+ /*
+ * Filename from archive not found in namelist.
+ * If we have the whole namelist here, just return 0.
+ * Otherwise, read the next name in and compare it.
+ * If this was the last name, namelist->found will remain on.
+ * If not, we loop to compare the newly read name.
+ */
+ if (f_sorted_names && namelist->found)
+ {
+ name_gather (); /* Read one more */
+ if (!namelist->found)
+ goto again;
+ }
+ return 0;
+}
+
+
+/*
+ * Print the names of things in the namelist that were not matched.
+ */
+void
+names_notfound ()
+{
+ register struct name *nlp, *next;
+ register char *p;
+
+ for (nlp = namelist; nlp != 0; nlp = next)
+ {
+ next = nlp->next;
+ if (!nlp->found)
+ msg ("%s not found in archive", nlp->name);
+
+ /*
+ * We could free() the list, but the process is about
+ * to die anyway, so save some CPU time. Amigas and
+ * other similarly broken software will need to waste
+ * the time, though.
+ */
+#ifdef amiga
+ if (!f_sorted_names)
+ free (nlp);
+#endif
+ }
+ namelist = (struct name *) NULL;
+ namelast = (struct name *) NULL;
+
+ if (f_sorted_names)
+ {
+ while (0 != (p = name_next (1)))
+ msg ("%s not found in archive", p);
+ }
+}
+
+/* These next routines were created by JF */
+
+void
+name_expand ()
+{
+ ;
+}
+
+/* This is like name_match(), except that it returns a pointer to the name
+ it matched, and doesn't set ->found The caller will have to do that
+ if it wants to. Oh, and if the namelist is empty, it returns 0, unlike
+ name_match(), which returns TRUE */
+
+struct name *
+name_scan (p)
+ register char *p;
+{
+ register struct name *nlp;
+ register int len;
+
+again:
+ if (0 == (nlp = namelist)) /* Empty namelist is easy */
+ return 0;
+ len = strlen (p);
+ for (; nlp != 0; nlp = nlp->next)
+ {
+ /* If first chars don't match, quick skip */
+ if (nlp->firstch && nlp->name[0] != p[0])
+ continue;
+
+ /* Regular expressions */
+ if (nlp->regexp)
+ {
+ if (fnmatch (nlp->name, p, FNM_LEADING_DIR) == 0)
+ return nlp; /* We got a match */
+ continue;
+ }
+
+ /* Plain Old Strings */
+ if (nlp->length <= len /* Archive len >= specified */
+ && (p[nlp->length] == '\0' || p[nlp->length] == '/')
+ /* Full match on file/dirname */
+ && strncmp (p, nlp->name, nlp->length) == 0) /* Name compare */
+ return nlp; /* We got a match */
+ }
+
+ /*
+ * Filename from archive not found in namelist.
+ * If we have the whole namelist here, just return 0.
+ * Otherwise, read the next name in and compare it.
+ * If this was the last name, namelist->found will remain on.
+ * If not, we loop to compare the newly read name.
+ */
+ if (f_sorted_names && namelist->found)
+ {
+ name_gather (); /* Read one more */
+ if (!namelist->found)
+ goto again;
+ }
+ return (struct name *) 0;
+}
+
+/* This returns a name from the namelist which doesn't have ->found set.
+ It sets ->found before returning, so successive calls will find and return
+ all the non-found names in the namelist */
+
+struct name *gnu_list_name;
+
+char *
+name_from_list ()
+{
+ if (!gnu_list_name)
+ gnu_list_name = namelist;
+ while (gnu_list_name && gnu_list_name->found)
+ gnu_list_name = gnu_list_name->next;
+ if (gnu_list_name)
+ {
+ gnu_list_name->found++;
+ if (gnu_list_name->change_dir)
+ if (chdir (gnu_list_name->change_dir) < 0)
+ msg_perror ("can't chdir to %s", gnu_list_name->change_dir);
+ return gnu_list_name->name;
+ }
+ return (char *) 0;
+}
+
+void
+blank_name_list ()
+{
+ struct name *n;
+
+ gnu_list_name = 0;
+ for (n = namelist; n; n = n->next)
+ n->found = 0;
+}
+
+char *
+new_name (path, name)
+ char *path, *name;
+{
+ char *path_buf;
+
+ path_buf = (char *) malloc (strlen (path) + strlen (name) + 2);
+ if (path_buf == 0)
+ {
+ msg ("Can't allocate memory for name '%s/%s", path, name);
+ exit (EX_SYSTEM);
+ }
+ (void) sprintf (path_buf, "%s/%s", path, name);
+ return path_buf;
+}
+
+/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */
+
+int
+confirm (action, file)
+ char *action, *file;
+{
+ int c, nl;
+ static FILE *confirm_file = 0;
+ extern FILE *msg_file;
+ extern char TTY_NAME[];
+
+ fprintf (msg_file, "%s %s?", action, file);
+ fflush (msg_file);
+ if (!confirm_file)
+ {
+ confirm_file = (archive == 0) ? fopen (TTY_NAME, "r") : stdin;
+ if (!confirm_file)
+ {
+ msg ("Can't read confirmation from user");
+ exit (EX_SYSTEM);
+ }
+ }
+ c = getc (confirm_file);
+ for (nl = c; nl != '\n' && nl != EOF; nl = getc (confirm_file))
+ ;
+ return (c == 'y' || c == 'Y');
+}
+
+char *x_buffer = 0;
+int size_x_buffer;
+int free_x_buffer;
+
+char **exclude = 0;
+int size_exclude = 0;
+int free_exclude = 0;
+
+char **re_exclude = 0;
+int size_re_exclude = 0;
+int free_re_exclude = 0;
+
+void
+add_exclude (name)
+ char *name;
+{
+ /* char *rname;*/
+ /* char **tmp_ptr;*/
+ int size_buf;
+
+ un_quote_string (name);
+ size_buf = strlen (name);
+
+ if (x_buffer == 0)
+ {
+ x_buffer = (char *) ck_malloc (size_buf + 1024);
+ free_x_buffer = 1024;
+ }
+ else if (free_x_buffer <= size_buf)
+ {
+ char *old_x_buffer;
+ char **tmp_ptr;
+
+ old_x_buffer = x_buffer;
+ x_buffer = (char *) ck_realloc (x_buffer, size_x_buffer + 1024);
+ free_x_buffer = 1024;
+ for (tmp_ptr = exclude; tmp_ptr < exclude + size_exclude; tmp_ptr++)
+ *tmp_ptr = x_buffer + ((*tmp_ptr) - old_x_buffer);
+ for (tmp_ptr = re_exclude; tmp_ptr < re_exclude + size_re_exclude; tmp_ptr++)
+ *tmp_ptr = x_buffer + ((*tmp_ptr) - old_x_buffer);
+ }
+
+ if (is_regex (name))
+ {
+ if (free_re_exclude == 0)
+ {
+ re_exclude = (char **) (re_exclude ? ck_realloc (re_exclude, (size_re_exclude + 32) * sizeof (char *)): ck_malloc (sizeof (char *) * 32));
+ free_re_exclude += 32;
+ }
+ re_exclude[size_re_exclude] = x_buffer + size_x_buffer;
+ size_re_exclude++;
+ free_re_exclude--;
+ }
+ else
+ {
+ if (free_exclude == 0)
+ {
+ exclude = (char **) (exclude ? ck_realloc (exclude, (size_exclude + 32) * sizeof (char *)): ck_malloc (sizeof (char *) * 32));
+ free_exclude += 32;
+ }
+ exclude[size_exclude] = x_buffer + size_x_buffer;
+ size_exclude++;
+ free_exclude--;
+ }
+ strcpy (x_buffer + size_x_buffer, name);
+ size_x_buffer += size_buf + 1;
+ free_x_buffer -= size_buf + 1;
+}
+
+void
+add_exclude_file (file)
+ char *file;
+{
+ FILE *fp;
+ char buf[1024];
+
+ if (strcmp (file, "-"))
+ fp = fopen (file, "r");
+ else
+ /* Let's hope the person knows what they're doing. */
+ /* Using -X - -T - -f - will get you *REALLY* strange
+ results. . . */
+ fp = stdin;
+
+ if (!fp)
+ {
+ msg_perror ("can't open %s", file);
+ exit (2);
+ }
+ while (fgets (buf, 1024, fp))
+ {
+ /* int size_buf;*/
+ char *end_str;
+
+ end_str = rindex (buf, '\n');
+ if (end_str)
+ *end_str = '\0';
+ add_exclude (buf);
+
+ }
+ fclose (fp);
+}
+
+int
+is_regex (str)
+ char *str;
+{
+ return index (str, '*') || index (str, '[') || index (str, '?');
+}
+
+/* Returns non-zero if the file 'name' should not be added/extracted */
+int
+check_exclude (name)
+ char *name;
+{
+ int n;
+ char *str;
+ extern char *strstr ();
+
+ for (n = 0; n < size_re_exclude; n++)
+ {
+ if (fnmatch (re_exclude[n], name, FNM_LEADING_DIR) == 0)
+ return 1;
+ }
+ for (n = 0; n < size_exclude; n++)
+ {
+ /* Accept the output from strstr only if it is the last
+ part of the string. There is certainly a faster way to
+ do this. . . */
+ if ((str = strstr (name, exclude[n]))
+ && (str == name || str[-1] == '/')
+ && str[strlen (exclude[n])] == '\0')
+ return 1;
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/tar/tar.h b/gnu/usr.bin/tar/tar.h
new file mode 100644
index 000000000000..c3fec78743bb
--- /dev/null
+++ b/gnu/usr.bin/tar/tar.h
@@ -0,0 +1,291 @@
+/* Declarations for tar archives.
+ Copyright (C) 1988, 1992, 1993 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 2, 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. */
+
+/* major() and minor() macros (among other things) defined here for hpux */
+#ifdef hpux
+#include <sys/mknod.h>
+#endif
+
+/*
+ * Kludge for handling systems that can't cope with multiple
+ * external definitions of a variable. In ONE routine (tar.c),
+ * we #define TAR_EXTERN to null; here, we set it to "extern" if
+ * it is not already set.
+ */
+#ifndef TAR_EXTERN
+#define TAR_EXTERN extern
+#endif
+
+/*
+ * Header block on tape.
+ *
+ * I'm going to use traditional DP naming conventions here.
+ * A "block" is a big chunk of stuff that we do I/O on.
+ * A "record" is a piece of info that we care about.
+ * Typically many "record"s fit into a "block".
+ */
+#define RECORDSIZE 512
+#define NAMSIZ 100
+#define TUNMLEN 32
+#define TGNMLEN 32
+#define SPARSE_EXT_HDR 21
+#define SPARSE_IN_HDR 4
+
+struct sparse
+ {
+ char offset[12];
+ char numbytes[12];
+ };
+
+struct sp_array
+ {
+ int offset;
+ int numbytes;
+ };
+
+union record
+ {
+ char charptr[RECORDSIZE];
+ struct header
+ {
+ char arch_name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char arch_linkname[NAMSIZ];
+ char magic[8];
+ char uname[TUNMLEN];
+ char gname[TGNMLEN];
+ char devmajor[8];
+ char devminor[8];
+ /* these following fields were added by JF for gnu */
+ /* and are NOT standard */
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+#ifdef NEEDPAD
+ char pad;
+#endif
+ struct sparse sp[SPARSE_IN_HDR];
+ char isextended;
+ char realsize[12]; /* true size of the sparse file */
+ /* char ending_blanks[12];*//* number of nulls at the
+ end of the file, if any */
+ }
+ header;
+ struct extended_header
+ {
+ struct sparse sp[21];
+ char isextended;
+ }
+ ext_hdr;
+ };
+
+/* The checksum field is filled with this while the checksum is computed. */
+#define CHKBLANKS " " /* 8 blanks, no null */
+
+/* The magic field is filled with this if uname and gname are valid. */
+#define TMAGIC "ustar " /* 7 chars and a null */
+
+/* The linkflag defines the type of file */
+#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
+#define LF_NORMAL '0' /* Normal disk file */
+#define LF_LINK '1' /* Link to previously dumped file */
+#define LF_SYMLINK '2' /* Symbolic link */
+#define LF_CHR '3' /* Character special file */
+#define LF_BLK '4' /* Block special file */
+#define LF_DIR '5' /* Directory */
+#define LF_FIFO '6' /* FIFO special file */
+#define LF_CONTIG '7' /* Contiguous file */
+/* Further link types may be defined later. */
+
+/* Note that the standards committee allows only capital A through
+ capital Z for user-defined expansion. This means that defining something
+ as, say '8' is a *bad* idea. */
+#define LF_DUMPDIR 'D' /* This is a dir entry that contains
+ the names of files that were in
+ the dir at the time the dump
+ was made */
+#define LF_LONGLINK 'K' /* Identifies the NEXT file on the tape
+ as having a long linkname */
+#define LF_LONGNAME 'L' /* Identifies the NEXT file on the tape
+ as having a long name. */
+#define LF_MULTIVOL 'M' /* This is the continuation
+ of a file that began on another
+ volume */
+#define LF_NAMES 'N' /* For storing filenames that didn't
+ fit in 100 characters */
+#define LF_SPARSE 'S' /* This is for sparse files */
+#define LF_VOLHDR 'V' /* This file is a tape/volume header */
+/* Ignore it on extraction */
+
+/*
+ * Exit codes from the "tar" program
+ */
+#define EX_SUCCESS 0 /* success! */
+#define EX_ARGSBAD 1 /* invalid args */
+#define EX_BADFILE 2 /* invalid filename */
+#define EX_BADARCH 3 /* bad archive */
+#define EX_SYSTEM 4 /* system gave unexpected error */
+#define EX_BADVOL 5 /* Special error code means
+ Tape volume doesn't match the one
+ specified on the command line */
+
+/*
+ * Global variables
+ */
+TAR_EXTERN union record *ar_block; /* Start of block of archive */
+TAR_EXTERN union record *ar_record; /* Current record of archive */
+TAR_EXTERN union record *ar_last; /* Last+1 record of archive block */
+TAR_EXTERN char ar_reading; /* 0 writing, !0 reading archive */
+TAR_EXTERN int blocking; /* Size of each block, in records */
+TAR_EXTERN int blocksize; /* Size of each block, in bytes */
+TAR_EXTERN char *info_script; /* Script to run at end of each tape change */
+TAR_EXTERN char *name_file; /* File containing names to work on */
+TAR_EXTERN char filename_terminator; /* \n or \0. */
+TAR_EXTERN char *tar; /* Name of this program */
+TAR_EXTERN struct sp_array *sparsearray; /* Pointer to the start of the scratch space */
+TAR_EXTERN int sp_array_size; /* Initial size of the sparsearray */
+TAR_EXTERN int tot_written; /* Total written to output */
+TAR_EXTERN struct re_pattern_buffer
+ *label_pattern; /* compiled regex for extract label */
+TAR_EXTERN char **ar_files; /* list of tape drive names */
+TAR_EXTERN int n_ar_files; /* number of tape drive names */
+TAR_EXTERN int cur_ar_file; /* tape drive currently being used */
+TAR_EXTERN int ar_files_len; /* malloced size of ar_files */
+TAR_EXTERN char *current_file_name, *current_link_name;
+
+/*
+ * Flags from the command line
+ */
+TAR_EXTERN int cmd_mode;
+#define CMD_NONE 0
+#define CMD_CAT 1 /* -A */
+#define CMD_CREATE 2 /* -c */
+#define CMD_DIFF 3 /* -d */
+#define CMD_APPEND 4 /* -r */
+#define CMD_LIST 5 /* -t */
+#define CMD_UPDATE 6 /* -u */
+#define CMD_EXTRACT 7 /* -x */
+#define CMD_DELETE 8 /* -D */
+#define CMD_VERSION 9 /* --version */
+
+
+TAR_EXTERN int f_reblock; /* -B */
+#if 0
+TAR_EXTERN char f_dironly; /* -D */
+#endif
+TAR_EXTERN int f_run_script_at_end; /* -F */
+TAR_EXTERN int f_gnudump; /* -G */
+TAR_EXTERN int f_follow_links; /* -h */
+TAR_EXTERN int f_ignorez; /* -i */
+TAR_EXTERN int f_keep; /* -k */
+TAR_EXTERN int f_startfile; /* -K */
+TAR_EXTERN int f_local_filesys; /* -l */
+TAR_EXTERN int tape_length; /* -L */
+TAR_EXTERN int f_modified; /* -m */
+TAR_EXTERN int f_multivol; /* -M */
+TAR_EXTERN int f_new_files; /* -N */
+TAR_EXTERN int f_oldarch; /* -o */
+TAR_EXTERN int f_exstdout; /* -O */
+TAR_EXTERN int f_use_protection;/* -p */
+TAR_EXTERN int f_absolute_paths;/* -P */
+TAR_EXTERN int f_sayblock; /* -R */
+TAR_EXTERN int f_sorted_names; /* -s */
+TAR_EXTERN int f_sparse_files; /* -S ... JK */
+TAR_EXTERN int f_namefile; /* -T */
+TAR_EXTERN int f_verbose; /* -v */
+TAR_EXTERN char *f_volhdr; /* -V */
+TAR_EXTERN int f_confirm; /* -w */
+TAR_EXTERN int f_verify; /* -W */
+TAR_EXTERN int f_exclude; /* -X */
+TAR_EXTERN char *f_compressprog; /* -z and -Z */
+TAR_EXTERN int f_do_chown; /* --do-chown */
+TAR_EXTERN int f_totals; /* --totals */
+TAR_EXTERN int f_remove_files; /* --remove-files */
+TAR_EXTERN int f_ignore_failed_read; /* --ignore-failed-read */
+TAR_EXTERN int f_checkpoint; /* --checkpoint */
+TAR_EXTERN int f_show_omitted_dirs; /* --show-omitted-dirs */
+TAR_EXTERN char *f_volno_file; /* --volno-file */
+TAR_EXTERN int f_force_local; /* --force-local */
+TAR_EXTERN int f_atime_preserve;/* --atime-preserve */
+TAR_EXTERN int f_compress_block; /* --compress-block */
+
+/*
+ * We default to Unix Standard format rather than 4.2BSD tar format.
+ * The code can actually produce all three:
+ * f_standard ANSI standard
+ * f_oldarch V7
+ * neither 4.2BSD
+ * but we don't bother, since 4.2BSD can read ANSI standard format anyway.
+ * The only advantage to the "neither" option is that we can cmp our
+ * output to the output of 4.2BSD tar, for debugging.
+ */
+#define f_standard (!f_oldarch)
+
+/*
+ * Structure for keeping track of filenames and lists thereof.
+ */
+struct name
+ {
+ struct name *next;
+ short length; /* cached strlen(name) */
+ char found; /* A matching file has been found */
+ char firstch; /* First char is literally matched */
+ char regexp; /* This name is a regexp, not literal */
+ char *change_dir; /* JF set with the -C option */
+ char *dir_contents; /* JF for f_gnudump */
+ char fake; /* dummy entry */
+ char name[1];
+ };
+
+TAR_EXTERN struct name *namelist; /* Points to first name in list */
+TAR_EXTERN struct name *namelast; /* Points to last name in list */
+
+TAR_EXTERN int archive; /* File descriptor for archive file */
+TAR_EXTERN int errors; /* # of files in error */
+
+TAR_EXTERN char *gnu_dumpfile;
+
+/*
+ * Error recovery stuff
+ */
+TAR_EXTERN char read_error_flag;
+
+
+/*
+ * Declarations of functions available to the world.
+ */
+union record *findrec ();
+void userec ();
+union record *endofrecs ();
+void anno ();
+
+#if defined (HAVE_VPRINTF) && __STDC__
+void msg (char *,...);
+void msg_perror (char *,...);
+#else
+void msg ();
+void msg_perror ();
+#endif
diff --git a/gnu/usr.bin/tar/update.c b/gnu/usr.bin/tar/update.c
new file mode 100644
index 000000000000..a64317c666dc
--- /dev/null
+++ b/gnu/usr.bin/tar/update.c
@@ -0,0 +1,585 @@
+/* Update a tar archive.
+ Copyright (C) 1988, 1992 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 2, 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 <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifndef __MSDOS__
+#include <pwd.h>
+#include <grp.h>
+#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;
+
+void append_file ();
+void close_archive ();
+int confirm ();
+void decode_header ();
+void fl_read ();
+void fl_write ();
+void flush_archive ();
+int move_arch ();
+struct name *name_scan ();
+char *name_from_list ();
+void name_expand ();
+void name_gather ();
+void names_notfound ();
+void open_archive ();
+int read_header ();
+void reset_eof ();
+void write_block ();
+void write_eot ();
+
+/* 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 (current_file_name)))
+ {
+
+ /* struct stat hstat; */
+ struct stat nstat;
+ int head_standard;
+
+ decode_header (head, &hstat, &head_standard, 0);
+ if (stat (current_file_name, &nstat) < 0)
+ {
+ msg_perror ("can't stat %s:", current_file_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, 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 */
+
+void
+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 (current_file_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 (current_file_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 ();
+}
+
+void
+write_block (f)
+ int 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! */
+int
+move_arch (n)
+ int n;
+{
+ long cur;
+
+#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/gnu/usr.bin/tar/version.c b/gnu/usr.bin/tar/version.c
new file mode 100644
index 000000000000..4454f62c8e98
--- /dev/null
+++ b/gnu/usr.bin/tar/version.c
@@ -0,0 +1 @@
+char version_string[] = "GNU tar version 1.11.2";
diff --git a/gnu/usr.bin/tar/y.tab.h b/gnu/usr.bin/tar/y.tab.h
new file mode 100644
index 000000000000..4a541d2c97f7
--- /dev/null
+++ b/gnu/usr.bin/tar/y.tab.h
@@ -0,0 +1,18 @@
+#define tAGO 257
+#define tDAY 258
+#define tDAYZONE 259
+#define tID 260
+#define tMERIDIAN 261
+#define tMINUTE_UNIT 262
+#define tMONTH 263
+#define tMONTH_UNIT 264
+#define tSEC_UNIT 265
+#define tSNUMBER 266
+#define tUNUMBER 267
+#define tZONE 268
+#define tDST 269
+typedef union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+} YYSTYPE;
+extern YYSTYPE yylval;