From 4b6324645f7223db0870236240d6e99eb4cda757 Mon Sep 17 00:00:00 2001 From: steve Date: Sun, 29 Jun 1997 06:55:02 +0000 Subject: Import of GNU patch version 2.4. --- contrib/patch/COPYING | 340 +++++++++ contrib/patch/ChangeLog | 1627 +++++++++++++++++++++++++++++++++++++++ contrib/patch/NEWS | 158 ++++ contrib/patch/README | 52 ++ contrib/patch/acconfig.h | 14 + contrib/patch/addext.c | 106 +++ contrib/patch/argmatch.c | 92 +++ contrib/patch/argmatch.h | 12 + contrib/patch/backupfile.c | 251 ++++++ contrib/patch/backupfile.h | 50 ++ contrib/patch/basename.c | 32 + contrib/patch/common.h | 307 ++++++++ contrib/patch/getopt.c | 1053 +++++++++++++++++++++++++ contrib/patch/getopt.h | 133 ++++ contrib/patch/getopt1.c | 189 +++++ contrib/patch/inp.c | 442 +++++++++++ contrib/patch/inp.h | 10 + contrib/patch/maketime.c | 391 ++++++++++ contrib/patch/maketime.h | 39 + contrib/patch/memchr.c | 199 +++++ contrib/patch/partime.c | 742 ++++++++++++++++++ contrib/patch/partime.h | 67 ++ contrib/patch/patch.1 | 1063 ++++++++++++++++++++++++++ contrib/patch/patch.c | 1293 +++++++++++++++++++++++++++++++ contrib/patch/pch.c | 1823 ++++++++++++++++++++++++++++++++++++++++++++ contrib/patch/pch.h | 25 + contrib/patch/quotearg.c | 125 +++ contrib/patch/quotearg.h | 9 + contrib/patch/rename.c | 112 +++ contrib/patch/util.c | 1070 ++++++++++++++++++++++++++ contrib/patch/util.h | 32 + contrib/patch/version.c | 30 + contrib/patch/version.h | 5 + 33 files changed, 11893 insertions(+) create mode 100644 contrib/patch/COPYING create mode 100644 contrib/patch/ChangeLog create mode 100644 contrib/patch/NEWS create mode 100644 contrib/patch/README create mode 100644 contrib/patch/acconfig.h create mode 100644 contrib/patch/addext.c create mode 100644 contrib/patch/argmatch.c create mode 100644 contrib/patch/argmatch.h create mode 100644 contrib/patch/backupfile.c create mode 100644 contrib/patch/backupfile.h create mode 100644 contrib/patch/basename.c create mode 100644 contrib/patch/common.h create mode 100644 contrib/patch/getopt.c create mode 100644 contrib/patch/getopt.h create mode 100644 contrib/patch/getopt1.c create mode 100644 contrib/patch/inp.c create mode 100644 contrib/patch/inp.h create mode 100644 contrib/patch/maketime.c create mode 100644 contrib/patch/maketime.h create mode 100644 contrib/patch/memchr.c create mode 100644 contrib/patch/partime.c create mode 100644 contrib/patch/partime.h create mode 100644 contrib/patch/patch.1 create mode 100644 contrib/patch/patch.c create mode 100644 contrib/patch/pch.c create mode 100644 contrib/patch/pch.h create mode 100644 contrib/patch/quotearg.c create mode 100644 contrib/patch/quotearg.h create mode 100644 contrib/patch/rename.c create mode 100644 contrib/patch/util.c create mode 100644 contrib/patch/util.h create mode 100644 contrib/patch/version.c create mode 100644 contrib/patch/version.h diff --git a/contrib/patch/COPYING b/contrib/patch/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/contrib/patch/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/contrib/patch/ChangeLog b/contrib/patch/ChangeLog new file mode 100644 index 0000000..ea9ed3d --- /dev/null +++ b/contrib/patch/ChangeLog @@ -0,0 +1,1627 @@ +1997-06-19 Paul Eggert + + * configure.in (VERSION): Version 2.4 released. + * NEWS: Patch is now verbose when patches do not match exactly. + +1997-06-17 Paul Eggert + + * pc/djgpp/configure.sed (config.h): Remove redundant $(srcdir). + + * configure.in (VERSION): Bump to 2.3.9. + * patch.c (main): By default, warn about hunks that succeed + with nonzero offset. + * patch.man: Add LC_ALL=C advice for making patches. + * pc/djgpp/configure.sed (config.h): Fix paths to dependent files. + +1997-06-17 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.8. + + * pch.c (open_patch_file): Test stdin for fseekability. + (intuit_diff_type): Missing context diff headers are now warnings, + not errors; some people use patches with them (e.g. when retrying + rejects). + + * patch.c (struct outstate): + New type, collecting together some output state vars. + (apply_hunk, copy_till, spew_output, init_output): Use it. + Keep track of whether some output has been generated. + (backup_if_mismatch): New var. + (ofp): Remove, in favor of local struct outstate vars. + (main): Use struct outstate. Initialize backup_if_mismatch to + be the inverse of posixly_correct. Keep track of whether mismatches + occur, and use this to implement backup_if_mismatch. + Report files that are not empty after patching, but should be. + (longopts, option_help, get_some_switches): New options + --backup-if-mismatch, --no-backup-if-mismatch. + (get_some_switches): -B, -Y, -z no longer set backup_type. + * backupfile.c (find_backup_file_name): + Treat backup_type == none like simple. + + * Makefile.in (CONFIG_HDRS): + Remove var; no longer needed by djgpp port. + (DISTFILES_PC_DJGPP): Rename pc/djgpp/config.sed to + pc/djgpp/configure.sed; remove pc/djgpp/config.h in favor of + new file that edits it, called pc/djgpp/config.sed. + * pc/djgpp/configure.bat: Rename config.sed to configure.sed. + * pc/djgpp/configure.sed (CONFIG_HDRS): Remove. + (config.h): Add rule to build this from config.hin and + pc/djgpp/config.sed. + * pc/djgpp/config.sed: + Convert from .h file to .sed script that generates .h file. + + * NEWS: Describe --backup-if-mismatch, --no-backup-if-mismatch. + * patch.man: + Describe new options --backup-if-mismatch, --no-backup-if-mismatch + and their ramifications. Use unreadable backup to represent + nonexistent file. + +1997-06-12 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.7. + (AC_CHECK_FUNCS): Add `raise'. + + * Makefile.in (inp.o): No longer depends on quotearg.h. + + * common.h (outfile): New decl (was private var named `output'). + (invc): New decl. + (GENERIC_OBJECT): Renamed from VOID. + (NULL_DEVICE, TTY_DEVICE): New macros. + + * patch.c (output): Remove; renamed to `outfile' and moved to common.h. + (main): `failed' is count, not boolean. + Say "Skipping patch." when deciding to skip patch. + (get_some_switches): Set invc when setting inname. + + * inp.c: Do not include . + (SCCSPREFIX, GET, GET_LOCKED, SCCSDIFF1, SCCSDIFF2, SCCSDIFF3, + RCSSUFFIX, CHECKOUT, CHECKOUT_LOCKED, RCSDIFF1, RCSDIFF2): + Move to util.c. + (get_input_file): Invoke new functions version_controller and + version_get to simplify this code. + (plan_b): "/dev/tty" -> NULL_DEVICE + + * pch.h (pch_timestamp): New decl. + * pch.c (p_timestamp): New var; takes over from global timestamp array. + (pch_timestamp): New function to export p_timestamp. + (there_is_another_patch): Use blander wording when you can't intuit + the file name. + Say "Skipping patch." when deciding to skip patch. + (intuit_diff_type): Look for version-controlled but nonexistent files + when intuiting file names; set invc accordingly. + Ignore Index: line if either old or new line is present, and if + POSIXLY_CORRECT is not set. + (do_ed_script): Flush stdout before invoking popen, since it may + send output to stdout. + + * util.h (version_controller, version_get): New decls. + * util.c: Include earlier. + (raise): New macro, if ! HAVE_RAISE. + (move_file): Create empty unreadable file when backing up a nonexistent + file. + (DEV_NULL): New constant. + (SCCSPREFIX, GET. GET_LOCKED, SCCSDIFF1, SCCSDIFF2, + RCSSUFFIX, CHECKOUT, CHECKOUT_LOCKED, RCSDIFF1): Moved here from inp.c. + (version_controller, version_get): New functions. + (ask): Look only at /dev/tty for answers; and when standard output is + not a terminal and ! posixly_correct, don't even look there. + Remove unnecessary fflushes of stdout. + (ok_to_reverse): Say "Skipping patch." when deciding to skip patch.. + (sigs): SIGPIPE might not be defined. + (exit_with_signal): Use `raise' instead of `kill'. + (systemic): fflush stdout before invoking subsidiary command. + + * patch.man: Document recent changes. + Add "COMPATIBILITY ISSUES" section. + + * NEWS: New COMPATIBILITY ISSUES for man page. + Changed verbosity when fuzz is found. + File name intuition is changed, again. + Backups are made unreadable when the file did not exist. + + * pc/djgpp/config.h (HAVE_STRUCT_UTIMBUF): Define. + (HAVE_RAISE): New macro. + (HAVE_UTIME_H): Define. + (TZ_is_unset): Do not define; it's not a serious problem with `patch' + to have TZ be unset in DOS. + +1997-06-08 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.6. + (AC_CHECK_HEADERS): Add utime.h. + * acconfig.h, configure.in, pc/djgpp/config.h (HAVE_STRUCT_UTIMBUF): + New macro. + * pc/djgpp/config.h (HAVE_UTIME_H, TZ_is_unset): New macros. + + * NEWS, patch.man: Describe new -Z, -T options, new numeric + option for -G, retired -G, and more verbose default behavior + with fuzz. + + * pch.c (intuit_diff_type): Record times reported for files in headers. + Remove head_says_nonexistent[x], since it's now equivalent to + !timestamp[x]. + * util.h (fetchname): Change argument head_says_nonexistent to + timestamp. + * util.c: #include for TM_LOCAL_ZONE. + Don't include since common.h now includes it. + (ok_to_reverse): noreverse and batch cases now output regardless of + verbosity. + (fetchname): Change argument head_says_nonexistent to pstamp, and + store header timestamp into *pstamp. + If -T or -Z option is given, match time stamps more precisely. + (ask): Remove unnecessary close of ttyfd. + When there is no terminal at all, output a newline to make the + output look nicer. After reporting EOF, flush stdout; + when an input error, report the error type. + + * inp.c (get_input_file): + Ask user whether to get file if patch_get is negative. + + * Makefile.in (clean): Don't clean */*.o; clean core* and *core. + +1997-06-04 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.5. + + * util.c (ok_to_reverse): + Be less chatty if verbosity is SILENT and we don't + have to ask the user. If force is nonzero, apply the patch anyway. + + * pch.c (there_is_another_patch): + Before skipping rest of patch, skip to + the patch start, so that another_hunk can skip it properly. + (intuit_diff_type): Slight wording change for missing headers, to + regularize with other diagnostics. Fix off-by-one error when setting + p_input_line when scanning the first hunk to check for deleted files. + +1997-06-03 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.4. + + * NEWS: Now matches more generously against nonexistent or empty files. + + * pch.c (there_is_another_patch): Move warning about not being + able to intuit file names here from skip_to. + (intuit_diff_type): Fatal error if we find a headless unified + or context diff. + + * util.c (ask): Null-terminate buffer properly even if it grew. + (fetchname): No need to test for null first argument. + +1997-06-02 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.3. + * pch.c (p_says_nonexistent, pch_says_nonexistent): Is now 1 for empty, + 2 for nonexistent. + (intuit_diff_type): Set p_says_nonexistent according to new meaning. + Treat empty files like nonexistent files when reversing. + (skip_to): Output better diagnostic when we can't intuit a file name. + * patch.c (main): + Count bytes, not lines, when testing whether a file is empty, + since it may contain only non-newline chars. + pch_says_nonexistent now returns 2 for nonexistent files. + +1997-06-01 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.2. + * pch.c (open_patch_file): + Fix bug when computing size of patch read from a pipe. + +1997-05-30 Paul Eggert + + * configure.in (VERSION): Bump to 2.3.1. + + * Makefile.in (transform, patch_name): New vars, + for proper implementation of AC_ARG_PROGRAM. + (install, uninstall): Use them. + (install-strip): New rule. + * pc/djgpp/config.sed (program_transform_name): Set to empty. + +1997-05-30 Paul Eggert + + * configure.in (VERSION), NEWS: Version 2.3 released. + * patch.man: Fix two font typos. + * util.c (doprogram): Fix misspelled decl. + +1997-05-26 Paul Eggert + + * configure.in (VERSION): Bump to 2.2.93. + + * pch.c (open_patch_file): + Fatal error if binary_transput and stdin is a tty. + + * pc/djgpp/config.sed (chdirsaf.c): + Use sed instead of cp, since cp might not be installed. + * pc/djgpp/configure.bat: + Prepend %srcdir% to pathname of config.sed, for crosscompiles. + +1997-05-25 Paul Eggert + + * configure.in (VERSION): Bump to 2.2.92. + (D_INO_IN_DIRENT): New macro. + * pc/djgpp/config.h, acconfig.h (D_INO_IN_DIRENT): New macro. + * backupfile.c (REAL_DIR_ENTRY): + Depend on D_INO_IN_DIRENT, not _POSIX_VERSION. + + * addext.c (addext): Adjust slen when adjusting s for DOS 8.3 limit. + Do not use xxx.h -> xxxh~ hack. + + * util.c: (move_file): Avoid makedirs test when possible even + if FILESYSTEM_PREFIX_LEN (p) is nonzero. Don't play + case-changing tricks to come up with backup file name; it's + not portable to case-insensitive file systems. + * common.h (ISLOWER): Remove. + + * inp.c (scan_input): Don't use Plan A if (debug & 16). + + * patch.c (shortopts): Add -g, -G. + (longopts): --help now maps to 132, not 'h', to avoid confusion. + (get_some_switches): Likewise. + Don't invoke setmode on input if --binary; wait until needed. + Don't ever invoke setmode on stdout. + * pch.c (open_patch_file): Setmode stdin to binary if binary_transput. + + * patch.man: Fix documentation of backup file name to match behavior. + Add advice for ordering of patches of derived files. + Add /dev/tty to list of files used. + * README: Adjust instructions for building on DOS. + * pc/djgpp/README: Remove tentative wording. + * NEWS: The DOS port is now tested. + Backup file names are no longer computed by switching case. + + * pc/chdirsaf.c (ERANGE): Include to define it. + (restore_wd): chdir unconditionally. + (chdir_safer): Invoke atexit successfully at most once. + * pc/djgpp/config.sed: Use chdirsaf.o, not pc/chdirsaf.o. + Replace CONFIG_HDRS, don't append. + Use $(srcdir) in CONFIG_STATUS. + Don't apply $(SHELL) to $(CONFIG_STATUS). + Append rules for chdirsaf.o, chdirsaf.c; clean chdirsaf.c at the end. + * pc/djgpp/configure.bat: Append CR to each line; DOS needs this. + Don't use | as sed s delimiter; DOS can't handle it. + +1997-05-21 Paul Eggert + + * configure.in (VERSION): Bump to 2.2.91. + + * pch.c (another_hunk): + Fix bug with computing size of prefix and suffix context + with ordinary context diffs. Report malformed patch if a unified diff + has nothing but context. + + * inp.c (get_input_file): + Use patch_get, not backup_type, to decide whether to + get from RCS or SCCS. Use the word `get' in diagnostics. + * patch.c (main): Initialize patch_get from PATCH_GET. + Omit DEFAULT_VERSION_CONTROL hook; it just leads to nonstandarization. + (longopts, option_help, get_some_switches): Add support for -g, -G. + (option_help): Add bug report address. + * common.h (patch_get): New decl. + * patch.man: Add -g and -G options; use `get' instead of `check out'. + Add PATCH_GET. Recommend -Naur instead of -raNU2 for diff. + * NEWS: Describe -g, -G, PATCH_GET. + + * version.c (copyright_string): Use only most recent copyright year, + as per GNU standards. + + * Makefile.in (DISTFILES_PC): Remove pc/quotearg.c. + * pc/djgpp/config.sed: Remove unnecessary hooks for quotearg and SHELL. + +1997-05-18 Paul Eggert + + * configure.in (VERSION): Increase to 2.2.9. + (AC_TYPE_MODE_T): Add. + + * pch.h (another_hunk): New parameter REV. + * pch.c (hunkmax): Now of type LINENUM. + (malformed): Add decl. + (there_is_another_patch): Skip inname-detection if skip_rest_of_patch. + (intuit_diff_type): To determine whether file appears to have been + deleted, look at replacement, not pattern. + If there is a mismatch between existence of file and whether the + patch claims to change whether the file exists, ask whether to + reverse the patch. + (another_hunk): New parameter REV specifying whether to reverse the + hunk. All callers changed. + (do_ed_script): Add assertion to ensure input file exists. + + * util.h (create_file): New function. + (copy_file): Now takes mode, not struct stat. + (makedirs): No longer exported. + (move_file): Now takes mode, not struct stat. + * util.c (makedirs): No longer exported. + (move_file): Accept mode of destination, not struct stat. + All callers changed. + Quote file names in diagnostics. + Create parent dir of destination if necessary. + Don't use ENOTDIR. + Don't unlink source; it will be unlinked later. + Unlink destination if FROM is zero. + (create_file): New function. + (copy_file): Accept mode of destination, not struct stat. + All callers changed. + Use create_file to create file. + (ok_to_reverse): Moved here from patch.c. Now accepts format and args; + all callers changed. + (mkdir): 2nd arg is now mode_t, for better compatibility. + (replace_slashes): Ignore slashes at the end of the filename. + + * common.h (noreverse): New decl. + (ok_to_reverse): Remove decl. + + * patch.c (noreverse): Now extern. + (main): New environment var PATCH_VERSION_CONTROL overrides VERSION_CONTROL. + Don't assert(hunk) if we're skipping the patch; we may not have any hunks. + When removing a file, back it up if backups are desired. + Don't chmod output file if input file did not exist. + chmod rej file to input file's mode minus executable bits. + (locate_hunk): Go back to old way of a single fuzz parameter, but + handle it more precisely: context diffs with partial contexts + can only match file ends, since the partial context can occur + only at the start or end of file. + All callers changed. + (create_output_file): Use create_file to create files. + (ok_to_reverse): Move to util.c. + + * inp.c (scan_input, get_input_file): Quote file names in diagnostics. + (get_input_file): Set inerrno if it's not already set. + Don't create file; it's now the caller's responsibility. + (plan_b): Use /dev/null if input size is zero, since it might not exist. + Use create_file to create temporary file. + + * NEWS: Add PATCH_VERSION_CONTROL; DOS port is untested. + + * pc/djgpp/config.h: Add comment for mode_t. + + * pc/djgpp/README: Note that it's not tested. + + * patch.man: PATCH_VERSION_CONTROL overrides VERSION_CONTROL. + +1997-05-15 Paul Eggert + + * configure.in: Add AC_PREREQ(2.12). + (VERSION): Bump to 2.2.8. + (ed_PROGRAM): Rename from ED_PROGRAM. + + * pch.c (prefix_components): Support DOS file names better. + Fix typo that caused fn to almost always yield 0. + + * util.c (, ): Include. + (move_file, copy_file): Add support for DOS filenames. + Preserve mode of input files when creating temp files. + Add binary file support. + (doprogram, rmdir): New functions. + (mkdir): Use doprogram. + (replace_slashes): Add support for DOS filenames. + (removedirs): New function. + (init_time)): New function. + (initial_time): New var. + (fetchname): Add support for deleted files, DOS filenames. + + * basename.c (FILESYSTEM_PREFIX_LEN, ISSLASH): + New macros, for DOS port. + (base_name): Use them. + + * addext.c (HAVE_DOS_FILE_NAMES): New macro. + : Include if HAVE_LIMITS_H. + (addext): Handle hosts with DOS file name limits. + + * common.h (LONG_MIN): New macro. + (FILESYSTEM_PREFIX_LEN, ISSLASH): New macros, for DOS port. + (ok_to_create_file): Remove. + (reverse): Now int. + (ok_to_reverse): New function decl. + (O_WRONLY, _O_BINARY, O_BINARY, O_CREAT, O_TRUNC): New macros. + (binary_transput): New var decl. + + * Makefile.in (ed_PROGRAM): Renamed from ED_PROGRAM. + (CONFIG_HDRS, CONFIG_STATUS): New vars. + (SRCS): Add maketime.c, partime.c. + (OBJS): Likewise. + (HDRS): Add maketime.h, partime.h. + (DISTFILES_PC, DISTFILES_PC_DJGPP): New vars. + (Makefile, config.status): Use CONFIG_STATUS, not config.status. + (clean): Remove */*.o. + (dist): Add pc and pc/djgpp subdirectories. + ($(OBJS)): Depend on $(CONFIG_HDRS) instead of config.h. + (maketime.o, partime.o): New rules. + (util.o): Depend on maketime.h. + + * patch.c (main): + Call init_time. Add DEFAULT_VERSION_CONTROL hook for people who + prefer the old ways. Build temp file names before we might invoke cleanup. + Add support for deleted files and clean up the patch-swapping code a bit. + Delete empty ancestors of deleted files. + When creating temporaries, use file modes of original files. + (longopts, get_some_switches): New option --binary. + (get_some_switches): Report non-errno errors with `fatal', not `pfatal'. + (create_output_file): New function, which preserves modes of original files + and supports binary transput. + (init_output, init_reject): Use it. + (ok_to_reverse): New function. + (TMPDIR): New macro. + (make_temp): Use $TMPDIR, $TMP, $TEMP, or TMPDIR, whichever comes first. + + * pch.c (p_says_nonexistent): New var. + (open_patch_file): Add binary transput support. + Apply stat to file names retrieved from user. + Reject them if they don't exist. + (intuit_diff_type): Add support for deleting files. + Don't treat trivial directories any differently. + Avoid stating the same file twice in common case of context diffs. + (prefix_components): Don't treat trivial directories any differently. + Add support for DOS filenames. + (pch_says_nonexistent): New function. + (do_ed_script): Preserve mode of input files when creating temp files. + Add support for binary transput. + + * pch.h (pch_says_nonexistent): New decl. + + * util.h (replace_slashes): No longer exported. + (fetchname): Add support for deleted files. + (copy_file, move_file): Add support for preserving file modes. + (init_time, removedirs): New functions. + + * argmatch.c: Converge with fileutils. + + * backupfile.c: Converge with fileutils. + (find_backup_file_name): Treat .~N~ suffix just like any other suffix + when handling file names that are too long. + + * inp.c: + In messages, put quotes around file names and spaces around "--". + (get_input_file): Allow files to be deleted. Do the expense of + makedirs only if we can't create the file. + (plan_a, plan_b): Add support for binary transput. + + * pc/chdirsaf.c, pc/djgpp/README, pc/djgpp/config.h, pc/djgpp/config.sed, pc/djgpp/configure.bat, pc/quotearg.c: + New file. + + * NEWS: + New methods for removing files; adjust file name intuition again. + Add description of MS-DOS and MS-Windows ports. + + * patch.man: + Simplify file name intuition slightly (no distinction for trivial dirs). + Add --binary. Describe how files and directories are deleted. + Suggest diff -a. Include caveats about what context diffs cannot represent. + +1997-05-06 Paul Eggert + + * configure.in (VERSION): Now 2.2.7. + (CPPFLAGS, LDFLAGS, LIBS): If the user has not set any of these vars, + prefer support for large files if available. + + * common.h (_LARGEFILE_SOURCE): Define. + (file_offset): New typedef. + (file_seek, file_tell): New macros. + + * patch.c (main): + Remove empty files by default unless POSIXLY_CORRECT is set. + + * util.c, util.h (Fseek): + Use file_offset instead of long, for portability to large-file hosts. + + * pch.c: (p_base, p_start, next_intuit_at, skip_to, open_patch_file, + intuit_diff_type, another_hunk, incomplete_line, do_ed_script): + Use file_offset instead of long, for portability to large-file hosts. + (prefix_components): Renamed from path_name_components; count only + nontrivial prefix components, and take a 2nd EXISTING arg. + (existing_prefix_components): Remove; subsumed by prefix_components. + (intuit_diff_type): When creating files, try for the creation of the + fewest directories. + + * configure.in (VERSION): Now 2.2.6. + + * pch.c (existing_prefix_components): New function. + (intuit_diff_type): When creating a file, use a name whose existing + directory prefix contains the most nontrivial path name components. + (best_name): Don't check for null 2nd arg. + + * util.h (replace_slashes): New decl. + + * util.c (replace_slashes): Now external. + (fetchname): Don't assume chars are nonnegative. + + * patch.man: + When creating a file, use a name whose existing directory prefix + contains the most nontrivial path name components. + Add advice for creating patches and applying them. + +1997-05-06 Paul Eggert + + * configure.in (VERSION): Now 2.2.6. + + * pch.c (existing_prefix_components): New function. + (intuit_diff_type): When creating a file, use a name whose existing + directory prefix contains the most nontrivial path name components. + (best_name): Don't check for null 2nd arg. + + * util.h (replace_slashes): New decl. + * util.c (replace_slashes): Now external. + (fetchname): Don't assume chars are nonnegative. + + * patch.man: Describe above change to pch.c. + Add advice for creating patches and applying them. + +1997-05-05 Paul Eggert + + * configure.in (VERSION): Update to 2.2.5. + + * quotearg.h, quotearg.c: New files. + * Makefile.in (SRCS, OBJS, HDRS): Mention new files. + (inp.o, util.o): Now depends on quotearg.h. + (quotearg.o): New makefile rule. + + * common.h (posixly_correct): New var. + * patch.c (main): Initialize it. + If ! posixly_correct, default backup type is now `existing'. + SIMPLE_BACKUP_SUFFIX no longer affects backup type. + (backup): Remove var. + + * util.h: (countdirs): Remove. + (systemic): New decl. + * util.c (move_file): Try making the parent directory of TO + if backup prefix or suffix contain a slash. + (ask): Remove arbitrary limit on size of result. + (systemic): New function. + (mkdir): Work even if arg contains shell metacharacters. + (replace_slashes): Return 0 if none were replaced. + Don't replace slash after . or .. since it's redundant. + (countdirs): Remove. + (makedirs): Ignore mkdir failures. + + * NEWS, patch.man: More POSIXLY_CORRECT adjustments. + Describe new rules for how file names are intuited. + +1997-04-17 Paul Eggert + + * configure.in (VERSION): Version 2.2 released. + + * Makefile.in (config.hin): + Remove before building; we always want the timestamp updated. + + * inp.c (get_input_file): + Look for RCS files only if backup_type == numbered_existing. + + * NEWS, patch.man: + Remove mention of never-implemented -V rcs and -V sccs options. + * patch.man: `pathname' -> `file name' + Correct the description of how file names are found in diff headers. + Clarify the distinction between ordinary and unified context diffs. + +1997-04-13 Paul Eggert + + * configure.in (VERSION): Update to 2.1.7. + + * patch.c (numeric_optarg): New function. + (get_some_switches): Use it. + + * pch.c (intuit_diff_type): When creating a file, prefer a name whose + existing dir prefix is the longest. + + * util.h (countdirs): New function. + * util.c (replace_slashes, countdirs): New functions. + (makedirs): Use replace_slashes, to be more like countdirs. + + * patch.man: Explain -pN vs -p N. Recommend --new-file. + Explain possible incompatibility with strip count. + +1997-04-10 Paul Eggert + + * configure.in (VERSION): Bump to 2.1.6. + (AC_CHECK_HEADERS): Remove stdlib.h (i.e. remove HAVE_STDLIB_H). + + * Makefile.in: (HDRS, patchlevel.h, TAGS, distclean, maintainer-clean): + Don't distribute patchlevel.h; let the user do it. + This works around some obscure (possibly nonexistent?) `make' bugs. + + * common.h (program_name): extern, not XTERN. + (): Include if STDC_HEADERS, not if HAVE_STDLIB_H. + (atol, getenv, malloc, realloc): Don't worry whether they're #defined. + + * patch.c (get_some_switches): + Add special hack for backwards compatibility with CVS 1.9. + (-B, -Y, -z): Now set backup_type = simple. + + * NEWS: Fix misspellings; minor reformatting. + * README: Report POSIX.2 compliance. + +1997-04-06 Paul Eggert + + Move all old RCS $Log entries into ChangeLog. + #include all files with < >, not " ". + + * addext.c, argmatch.c, argmatch.h, memchr.c, install-sh: + New files. + * EXTERN.h, INTERN.h: Removed. + * config.hin: Renamed from config.h.in. + + * acconfig.h (NODIR): Remove. + (HAVE_MEMCHR): Add. + + * configure.in (AC_ARG_PROGRAM, AC_PROG_MAKE_SET, HAVE_MEMCHR): Add. + (AC_CHECK_HEADERS): Replaces obsolescent AC_HAVE_HEADERS. + Add stdlib.h, string.h, unistd.h, varargs.h. + Delete obsolete call to AC_UNISTD_H. + (AC_CONFIG_HEADER): Rename config.h.in to config.hin. + (AC_C_CONST): Replaces obsolescent AC_CONST. + (AC_CHECK_FUNC): Check for getopt_long; define LIBOBJS and substitute + for it accordingly. + (AC_CHECK_FUNCS): Replaces obsolescent AC_HAVE_FUNCS. + Add _doprintf, isascii, mktemp, sigaction, sigprocmask, sigsetmask. + Remove strerror. + (AC_FUNC_CLOSEDIR_VOID, AC_FUNC_VPRINTF): Add. + (AC_HEADER_DIRENT): Replaces obsolescent AC_DIR_HEADER. + (AC_HEADER_STDC): Replaces obsolescent AC_STDC_HEADERS. + (AC_SYS_LONG_FILE_NAMES): Replaces obsolescent AC_LONG_FILE_NAMES. + (AC_TYPE_OFF_T): Replaces obsolescent AC_OFF_T. + (AC_TYPE_SIGNAL): Replaces obsolescent AC_RETSIGTYPE. + (AC_TYPE_SIZE_T): Replaces obsolescent AC_SIZE_T. + (AC_XENIX_DIR): Remove. + (ED_PROGRAM): New var. + (NODIR): Remove. + (PACKAGE, VERSION): New vars; substitute them with AC_SUBST. + + * Makefile.in: Conform to current GNU build standards. + Redo dependencies. Use library getopt_long if available. + Use `&&' instead of `;' inside shell commands where applicable; + GNU make requires this. + Use double-colon rules for actions that do not build files. + (@SET_MAKE@): Added. + (CFLAGS, LDFLAGS, prefix, exec_prefix): Base on @ versions of symbols. + (COMPILE, CPPFLAGS, DEFS, ED_PROGRAM, LIBOBJS, LIBSRCS, PACKAGE, + VERSION): New symbols. + (SRCS, OBJS, HDRS, MISC): Add new files. + (man1dir): Renamed from mandir. + (man1ext): Renamed from manext. + (patch): Put -o first. + (install): Use $(transform) to allow program to be renamed by configure. + (patchlevel.h): Build from $(VERSION). + (dist): Get version number from $(VERSION) and package name from + $(PACKAGE). + (TAGS): Scan $(HDRS). + (maintainer-clean): Renamed from realclean. Remove patchlevel.h. + + * backupfile.h (simple_backup_suffix): Now const *. + (find_backup_file_name, base_name, get_version): Args are now const *. + (base_name): New decl. + * backupfile.c (): Include only if HAVE_CONFIG_H. + (): Include. + (): Include if HAVE_STRING_H, not if STDC_HEADERS. + (): Include if !HAVE_STRING_H. + (): Do not include. + (): Redo include as per current autoconf standards. + (): Include if HAVE_LIMITS_H. Define CHAR_BIT if not defined. + (NLENGTH): Now returns size_t. + (CLOSEDIR, INT_STRLEN_BOUND): New macros. + (ISDIGIT): Use faster method. + (find_backup_file_name): No longer depends on NODIR. + Remove redundant code. + (make_version_name): Remove; do it more portably. + (max_backup_version): Args are now const *. + (version_number): Simplify digit checking. + (basename, concat, dirname): Remove. + (argmatch, invalid_arg): Move to argmatch.c. Simplify test for + ambiguous args. When reporting an error, use program_name not "patch". + (addext): Move to addext.c. Treat all negative values from pathconf + like -1. Always use long extension if it fits, even if the filesystem + does not support long file names. + (backup_types): Now const. + + * common.h, inp.h (XTERN): Renamed from EXT to avoid collision + with errno.h reserved name space. + + * common.h (DEBUGGING): Now an integer; default is 1. + (enum diff): New type. + (diff_type): Use it instead of small integers. + (CONTEXT_DIFF, NORMAL_DIFF, ED_DIFF, NEW_CONTEXT_DIFF, UNI_DIFF): + Now enumerated values instead of macros. + (NO_DIFF): New enumerated value (used instead of 0). + (volatile): Default to the empty string if __STDC__ is not defined. + (): Do not include. + (Chmod, Close, Fclose, Fflush, Fputc, Signal, Sprintf, Strcat, + Strcpy, Unlink, Write): Remove these macros; casts to void are + not needed for GNU coding standards. + (INITHUNKMAX): Move to pch.c. + (malloc, realloc, INT_MIN, MAXLINELEN, strNE, strnNE, + Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7, Reg8, Reg9, Reg10, Reg11, + Reg12, Reg13, Reg14, Reg15, Reg16): Remove these macros. + (S_IXOTH, S_IWOTH, S_IROTH, S_IXGRP, S_IWGRP, + S_IRGRP, S_IXUSR, S_IWUSR, S_IRUSR, O_RDONLY, O_RDWR): + Define these macros, if not defined. + (CTYPE_DOMAIN, ISLOWER, ISSPACE, ISDIGIT, PARAMS): New macros. + (instat): Renamed from filestat; used for input file now. + (bufsize, using_plan_a, debug, strippath): Not statically initialized. + (debug): #define to 0 if not DEBUGGING, so that users of `debug' + no longer need to be surrounded by `#if DEBUGGING'. + (out_of_mem, filec, filearg, outname, toutkeep, trejkeep): Remove. + (inname, inerrno, dry_run, origbase): New variables. + (origprae): Now const*. + (TMPOUTNAME, TMPINNAME, TMPPATNAME): Now const*volatile. + (verbosity): New variable; subsumes `verbose'. + (DEFAULT_VERBOSITY, SILENT, VERBOSE): Values in a new enum. + (verbose): Removed. + (VOID): Use `#ifdef __STDC__' instead of`#if __STDC__', + for consistency elsewhere. + (__attribute__): New macro (empty if not a recent GCC). + (fatal_exit): Renamed from my_exit. + (errno): Don't define if STDC_HEADERS. + (): Include if either STDC_HEADERS or HAVE_STRING_H. + (memcmp, memcpy): Define if !STDC_HEADERS && !HAVE_STRING_H + && !HAVE_MEMCHR. + (): Include if HAVE_STDLIB_H, not if STDC_HEADERS. + (atol, getenv, malloc, realloc, lseek): Declare only if not defined + as a macro. + (popen, strcpy, strcat, mktemp): Do not declare. + (lseek): Declare to yield off_t, not long. + (): Include only if HAVE_FCNTL_H. + + * inp.h (get_input_file): New decl. + * inp.c (SCCSPREFIX, GET, GET_LOCKED, SCCSDIFF, RCSSUFFIX, CHECKOUT, + CHECKOUT_LOCKED, RCSDIFF): Moved here from common.h. + (i_ptr): Now char const **. + (i_size): Remove. + (TIBUFSIZE_MINIMUM): Define only if not already defined. + (plan_a, plan_b): Arg is now const *. + (report_revision): Declare before use. It's now the caller's + responsibility to test whether revision is 0. + (scan_input, report_revision, get_input_file): + Be less chatty unless --verbose. + (get_input_file): New function, split off from plan_a. + Reuse file status gotten by pch if possible. Allow for dry run. + Use POSIX bits for creat, not number. Check for creation and + close failure, and use fstat not stat. Use memcpy not strncpy. + (plan_a): Rewrite for speed. + Caller now assigns result to using_plan_a. + Don't bother reading empty files; during dry runs they might not exist. + Use ISSPACE, not isspace. + (plan_b): Allow for dry runs. Use ISSPACE, and handle sign extension + correctly on arg. Use POSIX symbol for open arg. + + * patch.c (backup, output, patchname, program_name): New vars. + (last_frozen_line): Moved here from inp.h. + (TMPREJNAME): Moved here from common.h. + (optind_last): Removed. + (do_defines, if_defined, not_defined, else_defined, end_defined): + Now char const. Prepend with \n (except for not_defined) to + allow for files ending in non-newline. + (Argv): Now char*const*. + (main, get_some_switches): Exit status 0 means success, + 1 means hunks were rejected, 2 means trouble. + (main, locate_hunk, patch_match): Keep track of patch prefix context + separately from suffix context; this fixes several bugs. + (main): Initialize bufsize, strippath. + Be less chatty unless --verbose. + No more NODIR; always have version control available. + Require environment variables to be nonempty to have effect. + Add support for --dry-run, --output, --verbose. + Invoke get_input_file first, before deciding among do_ed_script, + plan_a, or plan_b. + Clear ofp after closing it, to keep discipline that ofp is either + 0 or open, to avoid file descriptor leaks. Conversely, rejfp doesn't + need this trick since static analysis is enough to show when it + needs to be closed. + Don't allow file-creation patches to be applied to existing files. + Misordered hunks are now not fatal errors; just go on to the next file. + It's a fatal error to fall back on plan B when --output is given, + since the moving hand has writ. + Add support for binary files. + Check for I/O errors. + chmod output file ourselves, rather than letting move_file do it; + this saves global state. + Use better grammar when outputting hunks messages, e.g. avoid + `1 hunks'. + (main, reinitialize_almost_everything): + Remove support for multiple file arguments. + Move get_some_switches call from reinitialize_almost_everything + to main. + (reinitialize_almost_everything): No need to reinitialize things + that are no longer global variables, e.g. outname. + (shortopts): Remove leading "-"; it's no longer important to + return options and arguments in order. '-b' no longer takes operand. + -p's operand is no longer optional. Add -i, -Y, -z. Remove -S. + (longopts): --suffix is now pared with -z, not -b. --backup now + means -b. Add --input, --basename-prefix, --dry-run, --verbose. + Remove --skip. --strip's operand is now required. + (option_help): New variable. Use style of current coding standards. + Change to match current option set. + (usage): Use it. + (get_some_switches): Get all switches, since `+' is defunct. + New options -i, -Y, -z, --verbose, --dry-run. + Option -S removed. + -b now means backup (backup_type == simple), not simple_backup_suffix. + -B now implies backup, and requires nonempty operand. + -D no longer requires first char of argument to be an identifier. + `-o -' is now disallowed (formerly output to regular file named "-"). + -p operand is now required. + -v no longer needs to cleanup (no temp files can exist at that point). + -V now implies backup. + Set inname, patchname from file name arguments, if any; + do not set filearg. It's now an error if extra operands are given. + (abort_junk): Check for write errors in reject file. + (apply_hunk, copy_till): Return error flag, so that failure to apply + out-of-order hunk is no longer fatal. + (apply_hunk): New arg after_newline, + for patching files not ending in newline. + Cache ofp for speed. Check for write errors. + (OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE): Now part of an enumerated type + instead of being #defined to small integers. + Change while-do to do-while when copying !-part for R_do_defines, + since condition is always true the first time through the loop. + (init_output, init_reject): Arg is now const *. + (copy_till, spew_output): Do not insert ``missing'' newlines; + propagate them via new after_newline argument. + (spew_output): Nothing to copy if last_frozen_line == input lines. + Do not close (ofp) if it's null. + (dump_line): Remove. + (similar): Ignore presence or absence of trailing newlines. + Check for only ' ' or '\t', not isspace (as per POSIX.2). + (make_temp): Use tmpnam if mktemp is not available. + (cleanup): New function. + (fatal_exit): Use it. Renamed from my_exit. + Take signal to exit with, not exit status (which is now always 2). + + * pch.h, pch.c (pch_prefix_context, pch_suffix_context): + New fns replacing pch_context. + (another_hunk): Now yields int, not bool; -1 means out of memory. + Now takes difftype as argument. + (pch_write_line): Now returns boolean indicating whether we're after + a newline just after the write, for supporting non-text files. + * pch.c (isdigit): Remove; use ISDIGIT instead. + (INITHUNKMAX): Moved here from common.h. + (p_context): Removed. We need to keep track of the pre- and post- + context separately, in: + (p_prefix_context, p_suffix_context): New variables. + (bestguess): Remove. + (open_patch_file): Arg is now char const *. + Copy file a buffer at a time, not a char at a time, for speed. + (grow_hunkmax): Now returns success indicator. + (there_is_another_patch, skip_to, another_hunk, do_ed_script): + Be less chatty unless --verbose. + (there_is_another_patch): + Avoid infinite loop if user input keeps yielding EOF. + (intuit_diff_type): New returns enum diff, not int. + Strip paths as they're being fetched. + Set ok_to_create_file correctly even if patch is reversed. + Set up file names correctly with unidiff output. + Use algorithm specified by POSIX 1003.2b/D11 to deduce + name of file to patch, with the exception of patches + that can create files. + (skip_to): Be verbose if !inname, since we're about to ask the + user for a file name and the context will help the user choose. + (another_hunk): Keep context as LINENUM, not int. + If the replacement is missing, calculate its context correctly. + Don't assume input ends in newline. + Keep track of patch prefix context separately from suffix context; + this fixes several bugs. + Don't assume blank lines got chopped if the replacement is missing. + Report poorly-formed hunks instead of aborting. + Do not use strcpy on overlapping strings; it's not portable. + Work even if lines are incomplete. + Fix bugs associated with context-less context hunks, + particularly when patching in reverse. + (pget_line): Now takes just 1 arg; instead of second arg, + just examine using_plan_a global. Return -1 if we ran out + of memory. + (do_ed_script): Now takes output FILE * argument. + Take name of editor from ED_PROGRAM instead of hardwiring /bin/ed. + Don't bother unlinking TMPOUTNAME. + Check for popen failure. + Flush pipe to check for output errors. + If ofp is nonzero, copy result to it, instead of trying to + move the result. + + * util.h, util.c (say1, say2, say3, say4, fatal1, fatal2, fatal3, + fatal4, pfatal1, pfatal2, pfatal3, pfatal4, ask1, ask2, ask3, ask4): + Remove; replaced with following. + (ask, say, fatal, pfatal): New stdarg functions. + (fetchname): Remove last, `assume_exists' parameter. + (savebuf, savestr, move_file, copy_file): Args are now const *. + (exit_with_signal): New function, for proper process status if + a signal is received as per POSIX.2. + (basename): Rename to `base_name' and move to backupfile. + * util.c (): Include here, not in common.h. + (vararg_start): New macro. + (va_dcl, va_start, va_arg, va_end): Define if neither + nor are available. + (SIGCHLD): Define to SIGCLD if SIGCLD is defined and + SIGCHLD isn't. + (private_strerror): Remove. + (move_file): Remove option of moving to stdout. + Add support for -Y, -z. + Don't assume chars in file name are nonnegative. + Use copy_file if rename fails due to EXDEV; + report failure if rename fails for any other reason. + (copy_file, makedirs): Use POSIX symbols for permissions. + (copy_file): Open source before destination. + (remove_prefix): New function. + (vfprintf): New function, if !HAVE_VPRINTF. + (afatal, apfatal, zfatal, zpfatal, errnum): Remove. + (fatal, pfatal, say): New functions that use stdarg. + All callers changed. + (zask): Renamed from `ask'. Now uses stdarg. Output to stdout, + and read from /dev/tty, or if that cannot be opened, from + stderr, stdout, stdin, whichever is first a tty. + Print "EOF" when an EOF is read. Do not echo input. + (sigs): New array. + (sigset_t, sigemptyset, sigmask, sigaddset, sigismember, SIG_BLOCK, + SIG_UNBLOCK, SIG_SETMASK, sigprocmask, sigblock, sigsetmask): + Define substitutes if not available. + (initial_signal_mask, signals_to_block): New vars. + (fatal_exit_handler): New function, if !HAVE_SIGACTION. + (set_signals, ignore_signals): Use sigaction and sigprocmask style + signal-handling if possible; it doesn't lose signals. + (set_signals): Default SIGCHLD to work around SysV fork+wait bug. + (mkdir): First arg is now const *. + (makedirs): Handle multiple adjacent slashes correctly. + (fetchname): Do not worry about whether the file exists + (that is now the caller's responsibility). + Treat a sequence of one or more slashes like one slash. + Do not unstrip leading directories if they all exist and if + no -p option was given; POSIX doesn't allow this. + (memcmp): Remove (now a macro in common.h). + + * version.c (copyright_string, free_software_msgid, authorship_msgid): + New constants. + (version): Use them. Use program_name instead of hardwiring it. + + * patch.man: Generate date from RCS Id. + Rewrite to match the above changes. + +Fri Jul 30 02:02:51 1993 Paul Eggert (eggert@twinsun.com) + + * configure.in (AC_HAVE_FUNCS): Add mkdir. + + * common.h (Chmod, Fputc, Write, VOID): New macros. + (malloc, realloc): Yield `VOID *', not `char *'. + + * util.h (makedirs): Omit `striplast' argument. Remove `aask'. + + * inp.c (plan_a): Remove fixed internal buffer. Remove lint. + + * util.c (set_signals, ignore_signals): Trap SIGTERM, too. + (makedirs): Removed fixed internal buffer. Omit `striplast' argument. + (mkdir): New function, if !HAVE_MKDIR. + (fetchname): Remove fixed internal buffer. + Remove lint from various functions. + + * patch.c, pch.c: Remove lint. + +Thu Jul 29 20:52:07 1993 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * Makefile.in (config.status): Run config.status --recheck, not + configure, to get the right args passed. + +Thu Jul 29 07:46:16 1993 Paul Eggert (eggert@twinsun.com) + + * The following changes remove all remaining fixed limits on memory, + and fix bugs in patch's handling of null bytes and files that do not + end in newline. `Patch' now works on binary files. + + * backupfile.c (find_backup_file_name): Don't dump core if malloc fails. + + * EXTERN.h, INTERN.h (EXITING): New macro. + * backupfile.[ch], patch.c, pch.c: Add PARAMS to function declarations. + + * common.h (bool): Change to int, so ANSI C prototype promotion works. + (CANVARARG): Remove varargs hack; it wasn't portable. + (filearg): Now a pointer, not an array, so that it can be reallocated. + (GET*, SCCSDIFF, CHECKOUT*, RCSDIFF): Quote operands to commands. + (my_exit): Declare here. + (BUFFERSIZE, Ctl, filemode, Fseek, Fstat, Lseek, MAXFILEC, MAXHUNKSIZE, + Mktemp, myuid, Null, Nullch, Nullfp, Nulline, Pclose, VOIDUSED): Remove. + All invokers changed. + (Argc, Argv, *define[sd], last_offset, maxfuzz, noreverse, ofp, + optind_last, rejfp, rejname): No longer externally visible; all + definers changed. + (INT_MAX, INT_MIN, STD*_FILENO, SEEK_SET): Define if the underlying + system doesn't. Include for this. + + * configure.in: Add limits.h, memcmp. Delete getline. + + * inp.c (tibufsize): New variable; buffers grow as needed. + (TIBUFSIZE_MINIMUM): New macro. + (report_revision): New function. + (plan_a): Do not search patch as a big string, since that fails + if it contains null bytes. + Prepend `./' to filenames starting with `-', for RCS and SCCS. + If file does not match default RCS/SCCS version, go ahead and patch + it anyway; warn about the problem but do not report a fatal error. + (plan_b): Do not use a fixed buffer to read lines; read byte by byte + instead, so that the lines can be arbitrarily long. Do not search + lines as strings, since they may contain null bytes. + (plan_a, plan_b): Report I/O errors. + + * inp.c, inp.h (rev_in_string): Remove. + (ifetch): Yield size of line too, since strlen no longer applies. + (plan_a, plan_b): No longer exported. + + * patch.c (abort_hunk, apply_hunk, patch_match, similar): + Lines may contain NUL and need not end in newline. + (copy_till, dump_line): Insert newline if appending after partial line. + All invokers changed. + (main, get_some_switches, apply_hunk): Allocate *_define[ds], filearg, + rejname dynamically. + (make_temp): New function. + (main): Use it. + (main, spew_output, dump_line) Check for I/O errors. + + * pch.c (open_patch_file): Don't copy stdin to a temporary file if + it's a regular file, since we can seek on it directly. + (open_patch_file, skip_to, another_hunk): The patch file may contain + NULs. + (another_hunk): The patch file may contain lines starting with '\', + which means the preceding line lacked a trailing newline. + (pgetline): Rename to pget_line. + (get_line, incomplete_line, pch_write_line): New functions. + (pch_line_len): Return size_t, not short; lines may be very long. + (do_ed_script): Check for I/O errors. Allow scripts to contain + 'i' and 's' commands, too. + + * pch.h (pfp, grow_hunkmax, intuit_diff_type, next_intuit_at, skip_to, + pfetch, pgetline): No longer exported. + (pch_write_line): New declaration. + (getline): Removed. + + * util.c (move_file, fetchname): Use private stat buffer, so that + filestat isn't lost. Check for I/O errors. + (savestr): Use savebuf. + (zask): Use STD*_FILENO instead of 0, 1, 2. + (fetchname): strip_leading defaults to INT_MAX instead of 957 (!). + (memcmp): Define if !HAVE_MEMCMP. + + * util.c, util.h (say*, fatal*, pfatal*, ask*): Delete; these + pseudo-varargs functions weren't ANSI C. Replace by macros + that invoke [fs]printf directly, and invoke new functions + [az]{say,fatal,pfatal,ask} before and after. + (savebuf, read_fatal, write_fatal, memory_fatal, Fseek): New functions. + (fatal*): Output trailing newline after message. All invokers changed. + + * version.c (version): Don't exit. + + * Makefile.in (SRCS): Remove getline.c. + +Thu Jul 22 15:24:24 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * EXTERN.h, INTERN.h (PARAMS): Define. + * backupfile.h, common.h, inp.h, pch.h, util.h: Use. + * backupfile.c: Include EXTERN.h. + +Wed Jul 21 13:14:05 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * getline.c: New file. + * configure.in: Check for getline (GNU libc has it). + * pch.c: Use it instead of fgets. + (pgetline): Renamed from pgets. Change callers. + * pch.h: Change decl. + + * pch.c (pgets): Tab adjusts by 8 - (indent % 8), not % 7. + Be consistent with similar code in pch.c::intuit_diff_type. + + * common.h (MEM): Typedef removed. + inp.c, pch.c, util.c: Use size_t instead of MEM. + inp.c, pch.c: Use off_t. + configure.in: Add AC_SIZE_T and AC_OFF_T. + + * common.h: Make buf a pointer and add a bufsize variable. + * util.c, pch.c, inp.c: Replace sizeof buf with bufsize. + * patch.c: malloc buf to bufsize bytes. + +Tue Jul 20 20:40:03 1993 Paul Eggert (eggert@twinsun.com) + + * common.h (BUFFERSIZE): Grow it to 8k too, just in case. + (buf): Turn `buf' back into an array; making it a pointer broke + things seriously. + * patch.c (main): Likewise. + +Tue Jul 20 20:02:40 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * Move Reg[1-16] and CANVARARG decls from config.h.in to common.h. + * acconfig.h: New file. + * Makefile (HDRS): Add it. + +Tue Jul 20 16:35:27 1993 Paul Eggert (eggert@twinsun.com) + + * Makefile.in: Remove alloca.[co]; getopt no longer needs it. + * configure.in (AC_ALLOCA): Remove. + + * util.c (set_signals, ignore_signals): Do nothing if SIGHUP + and SIGINT aren't defined. + +Tue Jul 20 17:59:56 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * patch.c (main): Call xmalloc, not malloc. xmalloc buf. + * common.h: Declare xmalloc. Make buf a pointer, not an array. + + * util.c (xmalloc): Call fatal1, not fatal. + + * common.h [MAXLINELEN]: Bump from 1k to 8k. + +Thu Jul 8 19:56:16 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * Makefile.in (installdirs): New target. + (install): Use it. + (Makefile, config.status, configure): New targets. + +Wed Jul 7 13:25:40 1993 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * patch.c (get_some_switches, longopts): Recognize --help + option, and call usage. + (usage): New function. + +Fri Jun 25 07:49:45 1993 Paul Eggert (eggert@twinsun.com) + + * backupfile.c (find_backup_file_name): Don't use .orig if + numbered_existing with no existing numbered backup. + (addext): Don't use ext if !HAVE_LONG_FILE_NAMES, + even if it would fit. This matches patch's historical behavior. + (simple_backup_suffix): Default to ".orig". + * patch.c (main): Just use that default. + +Tue Jun 15 22:32:14 1993 Paul Eggert (eggert@twinsun.com) + + * config.h.in (HAVE_ALLOCA_H): This #undef was missing. + * Makefile.in (info, check, installcheck): New rules. + +Sun Jun 13 14:31:29 1993 Paul Eggert (eggert@twinsun.com) + + * config.h.in (index, rindex): Remove unused macro + definitions; they get in the way when porting to AIX. + * config.h.in, configure.in (HAVE_STRING_H): Remove unused defn. + +Thu Jun 10 21:13:47 1993 Paul Eggert (eggert@twinsun.com) + + * patchlevel.h: PATCH_VERSION 2.1. + (The name `patch-2.0.12g12' is too long for traditional Unix.) + + * patchlevel.h (PATCH_VERSION): Renamed from PATCHLEVEL. + Now contains the entire patch version number. + * version.c (version): Use it. + +Wed Jun 9 21:43:23 1993 Paul Eggert (eggert@twinsun.com) + + * common.h: Remove declarations of index and rindex. + * backupfile.c: Likewise. + (addext, basename, dirname): Avoid rindex. + +Tue Jun 8 15:24:14 1993 Paul Eggert (eggert@twinsun.com) + + * inp.c (plan_a): Check that RCS and working files are not the + same. This check is needed on hosts that do not report file + name length limits and have short limits. + +Sat Jun 5 22:56:07 1993 Paul Eggert (eggert@twinsun.com) + + * Makefile.in (.c.o): Put $(CFLAGS) after other options. + (dist): Switch from .z to .gz. + +Wed Jun 2 10:37:15 1993 Paul Eggert (eggert@twinsun.com) + + * backupfile.c (find_backup_file_name): Initialize copy of + file name properly. + +Mon May 31 21:55:21 1993 Paul Eggert (eggert@twinsun.com) + + * patchlevel.h: Patch level 12g11. + + * pch.c (p_Char): Renamed from p_char, which is a system type + in Tex XD88's . + + * backupfile.c: Include "config.h" first, so that `const' is + treated consistently in system headers. + +Mon May 31 16:06:23 1993 Paul Eggert (eggert@twinsun.com) + + * patchlevel.h: Patch level 12g10. + + * configure.in: Add AC_CONST. + * config.h.in: Add `const'. + * Makefile.in (.c.o): Add -DHAVE_CONFIG_H. + (getopt.o getopt1.o): Depend on config.h. + + * util.c (xmalloc): New function; alloca.c needs this. + +Mon May 31 00:49:40 1993 Paul Eggert (eggert@twinsun.com) + + * patchlevel.h: PATCHLEVEL 12g9. + + * backupfile.c, backupfile.h (addext): New function. + It uses pathconf(), if available, to determine maximum file + name length. + * patch.c (main): Use it for reject file name. + * common.h (ORIGEXT): Moved to patch.c. + * config.h.in (HAVE_PATHCONF): New macro. + * configure.in: Define it. + + * Makefile.in (dist): Use gzip, not compress. + +Sat May 29 09:42:18 1993 Paul Eggert (eggert@twinsun.com) + + * patch.c (main): Use pathconf to decide reject file name. + * common.h (REJEXT): Remove. + + * inp.c (plan_a): Don't lock the checked-out file if `patch -o' + redirected the output elsewhere. + * common.h (CHECKOUT_LOCKED, GET_LOCKED): New macros. GET and + CHECKOUT now just checkout unlocked copies. + +Fri May 28 08:44:50 1993 Paul Eggert (eggert@twinsun.com) + + * backupfile.c (basename): Define even if NODIR isn't defined. + * patch.c (main): Ask just once to apply a reversed patch. + +Tue Nov 24 08:09:04 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu) + + * config.h.in, common.h: Use HAVE_FCNTL_H and HAVE_STRING_H + instead of USG. + + * backupfile.c: Use SYSDIR and NDIR instead of USG. + Define direct as dirent, not vice-versa. + +Wed Sep 16 17:11:48 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patch.c (get_some_switches): optc should be int, not char. + +Tue Sep 15 00:36:46 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12g8. + +Mon Sep 14 22:01:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Makefile.in: Add uninstall target. + + * util.c (fatal, pfatal): Add some asterisks to make fatal + messages stand out more. + +Tue Aug 25 22:13:36 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patch.c (main, get_some_switches), common.h, inp.c (plan_a, + plan_b), pch.c (there_is_another_patch): Add -t --batch + option, similar to -f --force. + +Mon Jul 27 11:27:07 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * common.h: Define SCCSDIFF and RCSDIFF. + * inp.c (plan_a): Use them to make sure it's safe to check out + the default RCS or SCCS version. + From Paul Eggert. + +Mon Jul 20 14:10:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.h: Declare basename. + * inp.c (plan_a), util.c (fetchname): Use it to isolate the + leading path when testing for RCS and SCCS files. + +Fri Jul 10 16:03:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (makedirs): Only make the directories that don't exist. + From chip@tct.com (Chip Salzenberg). + +Wed Jul 8 01:20:56 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patch.c (main): Open ofp after checking for ed script. + Close ofp and rejfp before trying plan B. + From epang@sfu.ca (Eugene Pang). + + * util.c (fatal, pfatal): Print "patch: " before message. + * pch.c, inp.c, patch.c, util.c: Remove "patch: " from the + callers that had it. + + * common.h (myuid): New variable. + * patch.c (main): Initialize it. + * inp.c (myuid): Function removed. + (plan_a): Use the variable, not the function. + + * patch.c: Add back -E --remove-empty-files option. + +Tue Jul 7 23:19:28 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (myuid): New function. + (plan_a): Call it. Optimize stat calls. Be smarter about + detecting checked out RCS and SCCS files. + From Paul Eggert (eggert@twinsun.com). + + * inp.c, util.c, patch.c: Don't bother checking for stat() > 0. + +Mon Jul 6 13:01:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (move_file): Use rename instead of link and copying. + + * util.c (pfatal): New function. + * util.h: Declare it and pfatal[1-4] macros. + * various files: Use it instead of fatal where appropriate. + + * common.h, patch.c: Replace Arg[cv]_last with optind_last. + + * patch.c (main, get_some_switches): Use getopt_long. Update + usage message. + (nextarg): Function removed. + + * Rename FLEXFILENAMES to HAVE_LONG_FILE_NAMES, + VOIDSIG to RETSIGTYPE. + + * backupfile.c, common.h: Use STDC header files if available. + backupfile.h: Declare get_version. + + * COPYING, COPYING.LIB, INSTALL, Makefile.in, alloca.c, + config.h.in, configure, configure.in, getopt.[ch], getopt1.c, + rename.c: New files. + * Configure, MANIFEST, Makefile.SH, config.H, config.h.SH, + malloc.c: Files removed. + + * version.c (version): Don't print the RCS stuff, since we're + not updating it regularly. + + * patchlevel.h: PATCHLEVEL 12u7. + + * Makefile.SH (dist): New target. + Makedist: File removed. + + * inp.c (plan_a): Check whether the user can write to the + file, not whether anyone can write to the file. + +Sat Jul 4 00:06:58 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (plan_a): Try to check out read-only files from RCS or SCCS. + + * util.c (move_file): If backing up by linking fails, try copying. + From cek@sdc.boeing.com (Conrad Kimball). + + * patch.c (get_some_switches): Eliminate -E option; always + remove empty output files. + + * util.c (fetchname): Only undo slash removal for relative + paths if -p was not given. + + * Makefile.sh: Add mostlyclean target. + +Fri Jul 3 23:48:14 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (fetchname): Accept whitespace between `Index:' and filename. + Also plug a small memory leak for diffs against /dev/null. + From eggert@twinsun.com (Paul Eggert). + + * common.h: Don't define TRUE and FALSE if already defined. + From phk@data.fls.dk (Poul-Henning Kamp). + +Wed Apr 29 10:19:33 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * backupfile.c (get_version): Exit if given a bad backup type. + +Fri Mar 27 09:57:14 1992 Karl Berry (karl at hayley) + + * common.h (S_ISDIR, S_ISREG): define these. + * inp.c (plan_a): use S_ISREG, not S_IFREG. + * util.c (fetchname): use S_ISDIR, not S_IFDIR. + +Mon Mar 16 14:10:42 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u6. + +Sat Mar 14 13:13:29 1992 David J. MacKenzie (djm at frob.eng.umd.edu) + + * Configure, config.h.SH: Check for directory header and unistd.h. + + * patch.c (main): If -E was given and output file is empty after + patching, remove it. + (get_some_switches): Recognize -E option. + + * patch.c (copy_till): Make garbled output an error, not a warning + that doesn't change the exit status. + + * common.h: Protect against system declarations of malloc and realloc. + + * Makedist: Add backupfile.[ch]. + + * Configure: Look for C library where NeXT and SVR4 put it. + Look in /usr/ucb after /bin and /usr/bin for utilities, + and look in /usr/ccs/bin, to make SVR4 happier. + Recognize m68k predefine. + + * util.c (fetchname): Test of stat return value was backward. + From csss@scheme.cs.ubc.ca. + + * version.c (version): Exit with status 0, not 1. + + * Makefile.SH: Add backupfile.[cho]. + * patch.c (main): Initialize backup file generation. + (get_some_switches): Add -V option. + * common.h, util,c, patch.c: Replace origext with simple_backup_suffix. + * util.c (move_file): Use find_backup_file_name. + +Tue Dec 3 11:27:16 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u5. + + * Makefile.SH: Change clean, distclean, and realclean targets a + little so they agree with the GNU coding standards. + Add Makefile to addedbyconf, so distclean removes it. + + * Configure: Recognize Domain/OS C library in /lib/libc. + From mmuegel@mot.com (Michael S. Muegel). + + * pch.c: Fixes from Wayne Davison: + Patch now accepts no-context context diffs that are + specified with an assumed one line hunk (e.g. "*** 10 ****"). + Fixed a bug in both context and unified diff processing that would + put a zero-context hunk in the wrong place (one line too soon). + Fixed a minor problem with p_max in unified diffs where it would + set p_max to hunkmax unnecessarily (the only adverse effect was to + not supply empty lines at eof by assuming they were truncated). + +Tue Jul 2 03:25:51 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * Configure: Check for signal declaration in + /usr/include/sys/signal.h as well as /usr/include/signal.h. + + * Configure, common.h, config.h.SH: Comment out the sprintf + declaration and tests to determine its return value type. It + conflicts with ANSI C systems' prototypes in stdio.h and the + return value of sprintf is never used anyway -- it's always cast + to void. + +Thu Jun 27 13:05:32 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u4. + +Thu Feb 21 15:18:14 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * pch.c (another_hunk): Fix off by 1 error. From + iverson@xstor.com (Tim Iverson). + +Sun Jan 20 20:18:58 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * Makefile.SH (all): Don't make a dummy `all' file. + + * patchlevel.h: PATCHLEVEL 12u3. + + * patch.c (nextarg): New function. + (get_some_switches): Use it, to prevent dereferencing a null + pointer if an option that takes an arg is not given one (is last + on the command line). From Paul Eggert. + + * pch.c (another_hunk): Fix from Wayne Davison to recognize + single-line hunks in unified diffs (with a single line number + instead of a range). + + * inp.c (rev_in_string): Don't use `s' before defining it. From + Wayne Davison. + +Mon Jan 7 06:25:11 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u2. + + * pch.c (intuit_diff_type): Recognize `+++' in diff headers, for + unified diff format. From unidiff patch 1. + +Mon Dec 3 00:14:25 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * patch.c (get_some_switches): Make the usage message more + informative. + +Sun Dec 2 23:20:18 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Configure: When checking for C preprocessor, look for 'abc.*xyz' + instead of 'abc.xyz', so ANSI C preprocessors work. + + * Apply fix for -D from ksb@mentor.cc.purdue.edu (Kevin Braunsdorf). + +1990-05-01 Wayne Davison + * patch.c, pch.c: unidiff support added + +Wed Mar 7 23:47:25 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * pch.c: Call malformed instead of goto malformed + (just allows easier debugging). + +Tue Jan 23 21:27:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * common.h (TMP*NAME): Make these char *, not char []. + patch.c (main): Use TMPDIR (if present) to set TMP*NAME. + common.h: Declare getenv. + +Sun Dec 17 17:29:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * patch.c (reverse_flag_specified): New variable. + (get_some_switches, reinitialize_almost_everything): Use it. + +1988-06-22 Larry Wall + patch12: + * common.h: sprintf was declared wrong + * patch.c: rindex() wasn't declared + * patch.man: now avoids Bell System Logo + +1988-06-03 Larry Wall + patch10: + * common.h: support for shorter extensions. + * inp.c: made a little smarter about sccs files + * patch.c: exit code improved. + better support for non-flexfilenames. + * patch.man: -B switch was contributed. + * pch.c: Can now find patches in shar scripts. + Hunks that swapped and then swapped back could core dump. + +1987-06-04 Larry Wall + * pch.c: pch_swap didn't swap p_bfake and p_efake. + +1987-02-16 Larry Wall + * patch.c: Short replacement caused spurious "Out of sync" message. + +1987-01-30 Larry Wall + * patch.c: Improved diagnostic on sync error. + Moved do_ed_script() to pch.c. + * pch.c: Improved responses to mangled patches. + * pch.h: Added do_ed_script(). + +1987-01-05 Larry Wall + * pch.c: New-style context diffs caused double call to free(). + +1986-11-21 Larry Wall + * patch.c: Fuzz factor caused offset of installed lines. + +1986-11-14 Larry Wall + * pch.c: Fixed problem where a long pattern wouldn't grow the hunk. + Also restored p_input_line when backtracking so error messages are + right. + +1986-11-03 Larry Wall + * pch.c: New-style delete triggers spurious assertion error. + +1986-10-29 Larry Wall + * patch.c: Backwards search could terminate prematurely. + * pch.c: Could falsely report new-style context diff. + +1986-09-17 Larry Wall + * common.h, inp.c, inp.h, patch.c, patch.man, pch.c, pch.h, + util.h, version.c, version.h: Baseline for netwide release. + +1986-08-01 Larry Wall + * patch.c: Fixes for machines that can't vararg. + Added fuzz factor. Generalized -p. General cleanup. + Changed some %d's to %ld's. Linted. + * patch.man: Documented -v, -p, -F. + Added notes to patch senders. + +1985-08-15 van%ucbmonet@berkeley + Changes for 4.3bsd diff -c. + +1985-03-26 Larry Wall + * patch.c: Frozen. + * patch.man: Frozen. + +1985-03-12 Larry Wall + * patch.c: Now checks for normalness of file to patch. + Check i_ptr and i_womp to make sure they aren't null before freeing. + Also allow ed output to be suppressed. + Changed pfp->_file to fileno(pfp). + Added -p option from jromine@uci-750a. + Added -D (#ifdef) option from joe@fluke. + * patch.man: Documented -p, -D. + +1984-12-06 Larry Wall + * patch.c: Made smarter about SCCS subdirectories. + +1984-12-05 Larry Wall + * patch.c: Added -l switch to do loose string comparison. + * patch.man: Added -l switch, and noted bistability bug. + +1984-12-04 Larry Wall + Branch for sdcrdcf changes. + * patch.c: Failed hunk count not reset on multiple patch file. + * patch.man: Baseline version. + +1984-11-29 Larry Wall + * patch.c: Linted. Identifiers uniquified. Fixed i_ptr malloc() bug. + Fixed multiple calls to mktemp(). Will now work on machines that can + only read 32767 chars. Added -R option for diffs with new and old + swapped. Various cosmetic changes. + +1984-11-09 Larry Wall + * patch.c: Initial revision + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +end: diff --git a/contrib/patch/NEWS b/contrib/patch/NEWS new file mode 100644 index 0000000..de0f757 --- /dev/null +++ b/contrib/patch/NEWS @@ -0,0 +1,158 @@ +Known problems: + +* The diffutils 2.7 documentation for `patch' is obsolete; this should be + fixed in diffutils 2.8. Until then, see `patch --help' or `man patch'. + +Change in version 2.4: + +* New options: + -Z or --set-utc sets times of patched files, assuming diff uses UTC (GMT). + -T or --set-time is similar, assuming local time (not recommended). + --backup-if-mismatch makes a backup if the patch does not match exactly + --no-backup-if-mismatch makes a backup only if otherwise requested + +* The default is now --backup-if-mismatch unless POSIXLY_CORRECT is set. + +* The -B or --prefix, -Y or --basename-prefix, and -z or --suffix options + no longer affect whether backups are made (as they did in patch 2.2 and 2.3); + they now merely specify the file names used when simple backups are made. + +* When patching a nonexistent file and making backups, an empty backup file + is now made (just as with traditional patch); but the backup file is + unreadable, as a way of indicating that it represents a nonexistent file. + +* `patch' now matches against empty and nonexistent files more generously. + A patch against an empty file applies to a nonexistent file, and vice versa. + +* -g or --get and PATCH_GET now have a numeric value that specifies + whether `patch' is getting files. + If the value is positive, working files are gotten from RCS or SCCS files; + if zero, `patch' ignores RCS and SCCS and working files are not gotten; + and if negative, `patch' asks the user whether to get each file. + The default is normally negative, but it is zero if POSIXLY_CORRECT is set. + +* The -G or --no-get option introduced in GNU patch 2.3 has been removed; + use -g0 instead. + +* The method used to intuit names of files to be patched is changed again: + `Index:' lines are normally ignored for context diffs, + and RCS and SCCS files are normally looked for when files do not exist. + The complete new method is described in the man page. + +* By default, `patch' is now more verbose when patches do not match exactly. + +* The manual page has a new COMPATIBILITY ISSUES section. + +Changes in version 2.3: + +* Unless the POSIXLY_CORRECT environment variable is set: + + - `patch' now distinguishes more accurately between empty and + nonexistent files if the input is a context diff. + A file is assumed to not exist if its context diff header + suggests that it is empty, and if the header timestamp + looks like it might be equivalent to 1970-01-01 00:00:00 UTC. + - Files that ``become nonexistent'' after patching are now removed. + When a file is removed, any empty ancestor directories are also removed. + +* Files are now automatically gotten from RCS and SCCS + if the -g or --get option is specified. + (The -G or --no-get option, also introduced in 2.3, was withdrawn in 2.4.) + +* If the PATCH_VERSION_CONTROL environment variable is set, + it overrides the VERSION_CONTROL environment variable. + +* The method used to intuit names of files to be patched is changed. + (It was further revised in 2.4; see above.) + +* The new --binary option makes `patch' read and write files in binary mode. + This option has no effect on POSIX-compliant hosts; + it is useful only in on operating systems like DOS + that distinguish between text and binary I/O. + +* The environment variables TMP and TEMP are consulted for the name of + the temporary directory if TMPDIR is not set. + +* A port to MS-DOS and MS-Windows is available; see the `pc' directory. + +* Backup file names are no longer ever computed by uppercasing characters, + since this isn't portable to systems with case-insensitive file names. + +Changes in version 2.2: + +* Arbitrary limits removed (e.g. line length, file name length). + +* On POSIX.1-compliant hosts, you can now patch binary files using the output + of GNU `diff -a'. + +* New options: + --dry-run + --help + --verbose + -i FILE or --input=FILE + -y PREF or --basename-prefix=PREF + +* patch is now quieter by default; use --verbose for the old chatty behavior. + +* Patch now complies better with POSIX.2 if your host complies with POSIX.1. + + Therefore: + - By default, no backups are made. + (But this was changed again in patch 2.4; see above.) + - The simple backup file name for F defaults to F.orig + regardless of whether the file system supports long file names, + and F~ is used only if F.orig is too long for that particular file. + - Similarly for the reject file names F.rej and F#. + + Also: + - The pseudo-option `+' has been withdrawn. + - -b is equivalent to --version-control=simple; + `-z SUFF' has the meaning that `-b SUFF' used to. + - Names of files to be patched are taken first from *** line and then from + --- line of context diffs; then from Index: line; /dev/tty is + consulted if none of the above files exist. However, if the patch + appears to create a file, the file does not have to exist: instead, + the first name with the longest existing directory prefix is taken. + (These rules were changed again in patch 2.3 and 2.4; see above.) + - Exit status 0 means success, 1 means hunks were rejected, 2 means trouble. + - `-l' ignores changes only in spaces and tabs, not in other white space. + - If no `-p' option is given, `-pINFINITY' is assumed, instead of trying + to guess the proper value. + - `-p' now requires an operand; use `-p 0' to get the effect of the old plain + `-p' option. + - `-p' treats two or more adjacent slashes as if it were one slash. + - The TERM signal is caught. + - New option `-i F' reads patch from F instead of stdin. + +* The `patch' options and build procedure conform to current GNU standards. + For example, the `--version' option now outputs copyright information. + +* When the patch is creating a file, but a nonempty file of that name already + exists, `patch' now asks for confirmation before patching. + +* RCS is used only if the version control method is `existing' + and there is already an RCS file. Similarly for SCCS. + (But this was changed again in patch 2.3 and 2.4; see above.) + +* Copyright notices have been clarified. Every file in this version of `patch' + can be distributed under the GNU General Public License. See README for + details. + +Changes in version 2.1: + +* A few more portability bugs have been fixed. The version number has + been changed from 2.0.12g11 to 2.1, because the name + `patch-2.0.12g10' was too long for traditional Unix file systems. + +Versions 2.0.12g9 through 2.0.12g11 fix various portability bugs. + +Changes in version 2.0.12g8: + +* Start of the 12g series, with a GNU-style configure script and + long-named options. +* Added the -t --batch option, similar to -f. +* Improved detection of files that are locked under RCS or SCCS. +* Reinstate the -E option to remove output files that are empty after + being patched. +* Print the system error message when system calls fail. +* Fixed various bugs and portability problems. diff --git a/contrib/patch/README b/contrib/patch/README new file mode 100644 index 0000000..e3aa259 --- /dev/null +++ b/contrib/patch/README @@ -0,0 +1,52 @@ +This version of `patch' has many changes made by the Free Software Foundation. +They add support for: + * handling arbitrary binary data and large files + * the unified context diff format that GNU diff can produce + * making GNU Emacs-style backup files + * improved interaction with RCS and SCCS + * the GNU conventions for option parsing and configuring and compilation. + * better POSIX.2 compliance +They also fix some bugs. See the NEWS and ChangeLog files for details. + +Tutorial-style documentation for patch is included in the GNU +diffutils package. Unfortunately, the diffutils 2.7 documentation +for `patch' is obsolete; this should be fixed in diffutils 2.8. +In the mean time, see `patch --help', or consult the man page +in this distribution. + +For GNU and Unix build and installation instructions, see the file INSTALL. +For MS-DOS using DJGPP tools, see the file pc/djgpp/README. +For other systems, copy config.hin to config.h and change +#undef statements in it to #define as appropriate for your system, +and copy Makefile.in to Makefile and set the variables that are +enclosed in @ signs as appropriate for your system. + +Please send bug reports for this version of patch to +bug-gnu-utils@prep.ai.mit.edu. + +The Free Software Foundation is distributing this version of patch +independently because as of this writing, Larry Wall has not released a +new version of patch since mid-1988. We have heard that he has been +too busy working on other things, like Perl. He has graciously agreed +to let GNU `patch' be distributed under the terms of the GNU General +Public License. + +------ + +Copyright 1984, 1985, 1986, 1987, 1988 Larry Wall +Copyright 1989, 1990, 1991, 1992, 1993, 1997 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 file; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. diff --git a/contrib/patch/acconfig.h b/contrib/patch/acconfig.h new file mode 100644 index 0000000..42e3e60 --- /dev/null +++ b/contrib/patch/acconfig.h @@ -0,0 +1,14 @@ +/* Local acconfig.h for autoheader. + Descriptive text for the C preprocessor macros that + the patch configure.in can define. + autoheader copies the comments into config.hin. */ + +/* Define if there is a member named d_ino in the struct describing + directory headers. */ +#undef D_INO_IN_DIRENT + +/* Define if memchr works. */ +#undef HAVE_MEMCHR + +/* Define if `struct utimbuf' is declared -- usually in . */ +#undef HAVE_STRUCT_UTIMBUF diff --git a/contrib/patch/addext.c b/contrib/patch/addext.c new file mode 100644 index 0000000..56d0e4f --- /dev/null +++ b/contrib/patch/addext.c @@ -0,0 +1,106 @@ +/* addext.c -- add an extension to a file name + Copyright (C) 1990, 1997 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; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by David MacKenzie and Paul Eggert */ + +#if HAVE_CONFIG_H +# include +#endif + +#ifndef HAVE_DOS_FILE_NAMES +#define HAVE_DOS_FILE_NAMES 0 +#endif +#ifndef HAVE_LONG_FILE_NAMES +#define HAVE_LONG_FILE_NAMES 0 +#endif + +#include + +#if HAVE_LIMITS_H +# include +#endif +#ifndef _POSIX_NAME_MAX +#define _POSIX_NAME_MAX 14 +#endif + +#include +#if HAVE_STRING_H +# include +#else +# include +#endif + +#if HAVE_UNISTD_H +# include +#endif + +/* Append to FILENAME the extension EXT, unless the result would be too long, + in which case just append the character E. */ + +void +addext (filename, ext, e) + char *filename; + char const *ext; + int e; +{ + char *s = base_name (filename); + size_t slen = strlen (s), extlen = strlen (ext); + long slen_max = -1; + +#if HAVE_PATHCONF && defined _PC_NAME_MAX + if (slen + extlen <= _POSIX_NAME_MAX && ! HAVE_DOS_FILE_NAMES) + /* The file name is so short there's no need to call pathconf. */ + slen_max = _POSIX_NAME_MAX; + else if (s == filename) + slen_max = pathconf (".", _PC_NAME_MAX); + else + { + char c = *s; + *s = 0; + slen_max = pathconf (filename, _PC_NAME_MAX); + *s = c; + } +#endif + if (slen_max < 0) + slen_max = HAVE_LONG_FILE_NAMES ? 255 : 14; + + if (HAVE_DOS_FILE_NAMES && slen_max <= 12) + { + /* Live within DOS's 8.3 limit. */ + char *dot = strchr (s, '.'); + if (dot) + { + slen -= dot + 1 - s; + s = dot + 1; + slen_max = 3; + } + else + slen_max = 8; + extlen = 9; /* Don't use EXT. */ + } + + if (slen + extlen <= slen_max) + strcpy (s + slen, ext); + else + { + if (slen_max <= slen) + slen = slen_max - 1; + s[slen] = e; + s[slen + 1] = 0; + } +} diff --git a/contrib/patch/argmatch.c b/contrib/patch/argmatch.c new file mode 100644 index 0000000..aa55933 --- /dev/null +++ b/contrib/patch/argmatch.c @@ -0,0 +1,92 @@ +/* argmatch.c -- find a match for a string in an array + Copyright (C) 1990, 1997 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; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by David MacKenzie */ + +#if HAVE_CONFIG_H +# include +#endif + +#include + +#include + +#include +#if HAVE_STRING_H +# include +#else +# include +#endif + +/* If ARG is an unambiguous match for an element of the + null-terminated array OPTLIST, return the index in OPTLIST + of the matched element, else -1 if it does not match any element + or -2 if it is ambiguous (is a prefix of more than one element). */ + +int +argmatch (arg, optlist) + const char *arg; + const char *const *optlist; +{ + int i; /* Temporary index in OPTLIST. */ + size_t arglen; /* Length of ARG. */ + int matchind = -1; /* Index of first nonexact match. */ + int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ + + arglen = strlen (arg); + + /* Test all elements for either exact match or abbreviated matches. */ + for (i = 0; optlist[i]; i++) + { + if (!strncmp (optlist[i], arg, arglen)) + { + if (strlen (optlist[i]) == arglen) + /* Exact match found. */ + return i; + else if (matchind == -1) + /* First nonexact match found. */ + matchind = i; + else + /* Second nonexact match found. */ + ambiguous = 1; + } + } + if (ambiguous) + return -2; + else + return matchind; +} + +/* Error reporting for argmatch. + KIND is a description of the type of entity that was being matched. + VALUE is the invalid value that was given. + PROBLEM is the return value from argmatch. */ + +void +invalid_arg (kind, value, problem) + const char *kind; + const char *value; + int problem; +{ + fprintf (stderr, "%s: ", program_name); + if (problem == -1) + fprintf (stderr, "invalid"); + else /* Assume -2. */ + fprintf (stderr, "ambiguous"); + fprintf (stderr, " %s `%s'\n", kind, value); +} diff --git a/contrib/patch/argmatch.h b/contrib/patch/argmatch.h new file mode 100644 index 0000000..e95ff62 --- /dev/null +++ b/contrib/patch/argmatch.h @@ -0,0 +1,12 @@ +/* argmatch.h -- declarations for matching arguments against option lists */ + +#if defined __STDC__ || __GNUC__ +# define __ARGMATCH_P(args) args +#else +# define __ARGMATCH_P(args) () +#endif + +int argmatch __ARGMATCH_P ((const char *, const char * const *)); +void invalid_arg __ARGMATCH_P ((const char *, const char *, int)); + +extern char const program_name[]; diff --git a/contrib/patch/backupfile.c b/contrib/patch/backupfile.c new file mode 100644 index 0000000..3b91be0 --- /dev/null +++ b/contrib/patch/backupfile.c @@ -0,0 +1,251 @@ +/* backupfile.c -- make Emacs style backup file names + Copyright (C) 1990,1991,1992,1993,1995,1997 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; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by David MacKenzie . + Some algorithms adapted from GNU Emacs. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include +#include +#if HAVE_STRING_H +# include +#else +# include +#endif + +#if HAVE_DIRENT_H +# include +# define NLENGTH(direct) strlen ((direct)->d_name) +#else +# define dirent direct +# define NLENGTH(direct) ((size_t) (direct)->d_namlen) +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#if CLOSEDIR_VOID +/* Fake a return value. */ +# define CLOSEDIR(d) (closedir (d), 0) +#else +# define CLOSEDIR(d) closedir (d) +#endif + +#if STDC_HEADERS +# include +#else +char *malloc (); +#endif + +#if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H +# define HAVE_DIR 1 +#else +# define HAVE_DIR 0 +#endif + +#if HAVE_LIMITS_H +# include +#endif +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif +/* Upper bound on the string length of an integer converted to string. + 302 / 1000 is ceil (log10 (2.0)). Subtract 1 for the sign bit; + add 1 for integer division truncation; add 1 more for a minus sign. */ +#define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2) + +/* ISDIGIT differs from isdigit, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char. + - It's guaranteed to evaluate its argument exactly once. + - It's typically faster. + Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that + only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless + it's important to use the locale's definition of `digit' even when the + host does not conform to Posix. */ +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#if D_INO_IN_DIRENT +# define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) +#else +# define REAL_DIR_ENTRY(dp) 1 +#endif + +/* Which type of backup file names are generated. */ +enum backup_type backup_type = none; + +/* The extension added to file names to produce a simple (as opposed + to numbered) backup file name. */ +const char *simple_backup_suffix = ".orig"; + +static int max_backup_version __BACKUPFILE_P ((const char *, const char *)); +static int version_number __BACKUPFILE_P ((const char *, const char *, size_t)); + +/* Return the name of the new backup file for file FILE, + allocated with malloc. Return 0 if out of memory. + FILE must not end with a '/' unless it is the root directory. */ + +char * +find_backup_file_name (file) + const char *file; +{ + size_t backup_suffix_size_max; + size_t file_len = strlen (file); + size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4; + char *s; + const char *suffix = simple_backup_suffix; + + /* Allow room for simple or `.~N~' backups. */ + backup_suffix_size_max = strlen (simple_backup_suffix) + 1; + if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max) + backup_suffix_size_max = numbered_suffix_size_max; + + s = malloc (file_len + backup_suffix_size_max + numbered_suffix_size_max); + if (s) + { + strcpy (s, file); + +#if HAVE_DIR + if (backup_type != simple && backup_type != none) + { + int highest_backup; + size_t dir_len = base_name (s) - s; + + strcpy (s + dir_len, "."); + highest_backup = max_backup_version (file + dir_len, s); + if (! (backup_type == numbered_existing && highest_backup == 0)) + { + char *numbered_suffix = s + (file_len + backup_suffix_size_max); + sprintf (numbered_suffix, ".~%d~", highest_backup + 1); + suffix = numbered_suffix; + } + strcpy (s, file); + } +#endif /* HAVE_DIR */ + + addext (s, suffix, '~'); + } + return s; +} + +#if HAVE_DIR + +/* Return the number of the highest-numbered backup file for file + FILE in directory DIR. If there are no numbered backups + of FILE in DIR, or an error occurs reading DIR, return 0. + */ + +static int +max_backup_version (file, dir) + const char *file; + const char *dir; +{ + DIR *dirp; + struct dirent *dp; + int highest_version; + int this_version; + size_t file_name_length; + + dirp = opendir (dir); + if (!dirp) + return 0; + + highest_version = 0; + file_name_length = strlen (file); + + while ((dp = readdir (dirp)) != 0) + { + if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) < file_name_length + 4) + continue; + + this_version = version_number (file, dp->d_name, file_name_length); + if (this_version > highest_version) + highest_version = this_version; + } + if (CLOSEDIR (dirp)) + return 0; + return highest_version; +} + +/* If BACKUP is a numbered backup of BASE, return its version number; + otherwise return 0. BASE_LENGTH is the length of BASE. + */ + +static int +version_number (base, backup, base_length) + const char *base; + const char *backup; + size_t base_length; +{ + int version; + const char *p; + + version = 0; + if (strncmp (base, backup, base_length) == 0 + && backup[base_length] == '.' + && backup[base_length + 1] == '~') + { + for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p) + version = version * 10 + *p - '0'; + if (p[0] != '~' || p[1]) + version = 0; + } + return version; +} +#endif /* HAVE_DIR */ + +static const char * const backup_args[] = +{ + "never", "simple", "nil", "existing", "t", "numbered", 0 +}; + +static const enum backup_type backup_types[] = +{ + simple, simple, numbered_existing, numbered_existing, numbered, numbered +}; + +/* Return the type of backup indicated by VERSION. + Unique abbreviations are accepted. */ + +enum backup_type +get_version (version) + const char *version; +{ + int i; + + if (version == 0 || *version == 0) + return numbered_existing; + i = argmatch (version, backup_args); + if (i < 0) + { + invalid_arg ("version control type", version, i); + exit (2); + } + return backup_types[i]; +} diff --git a/contrib/patch/backupfile.h b/contrib/patch/backupfile.h new file mode 100644 index 0000000..dad1984 --- /dev/null +++ b/contrib/patch/backupfile.h @@ -0,0 +1,50 @@ +/* backupfile.h -- declarations for making Emacs style backup file names + Copyright (C) 1990, 1991, 1992, 1997 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; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* When to make backup files. */ +enum backup_type +{ + /* Never make backups. */ + none, + + /* Make simple backups of every file. */ + simple, + + /* Make numbered backups of files that already have numbered backups, + and simple backups of the others. */ + numbered_existing, + + /* Make numbered backups of every file. */ + numbered +}; + +extern enum backup_type backup_type; +extern char const *simple_backup_suffix; + +#ifndef __BACKUPFILE_P +# if defined __STDC__ || __GNUC__ +# define __BACKUPFILE_P(args) args +# else +# define __BACKUPFILE_P(args) () +# endif +#endif + +char *base_name __BACKUPFILE_P ((char const *)); +char *find_backup_file_name __BACKUPFILE_P ((char const *)); +enum backup_type get_version __BACKUPFILE_P ((char const *)); +void addext __BACKUPFILE_P ((char *, char const *, int)); diff --git a/contrib/patch/basename.c b/contrib/patch/basename.c new file mode 100644 index 0000000..8d18a8f --- /dev/null +++ b/contrib/patch/basename.c @@ -0,0 +1,32 @@ +/* basename.c -- return the last element in a path */ + +#if HAVE_CONFIG_H +# include +#endif + +#include + +#ifndef FILESYSTEM_PREFIX_LEN +#define FILESYSTEM_PREFIX_LEN(f) 0 +#endif + +#ifndef ISSLASH +#define ISSLASH(c) ((c) == '/') +#endif + +/* In general, we can't use the builtin `basename' function if available, + since it has different meanings in different environments. + In some environments the builtin `basename' modifies its argument. */ + +char * +base_name (name) + char const *name; +{ + char const *base = name += FILESYSTEM_PREFIX_LEN (name); + + for (; *name; name++) + if (ISSLASH (*name)) + base = name + 1; + + return (char *) base; +} diff --git a/contrib/patch/common.h b/contrib/patch/common.h new file mode 100644 index 0000000..b2895d4 --- /dev/null +++ b/contrib/patch/common.h @@ -0,0 +1,307 @@ +/* common definitions for `patch' */ + +/* $Id: common.h,v 1.18 1997/06/13 06:28:37 eggert Exp $ */ + +/* +Copyright 1986, 1988 Larry Wall +Copyright 1990, 1991, 1992, 1993, 1997 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; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef DEBUGGING +#define DEBUGGING 1 +#endif + +/* We must define `volatile' and `const' first (the latter inside config.h), + so that they're used consistently in all system includes. */ +#ifndef __STDC__ +# ifndef volatile +# define volatile +# endif +#endif + +/* Enable support for fseeko and ftello on hosts + where it is available but is turned off by default. + This must be defined before any system file is included. */ +#define _LARGEFILE_SOURCE 1 + +#include + +#include +#include +#include +#include + +#include +#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 +#ifndef S_IXOTH +#define S_IXOTH 1 +#endif +#ifndef S_IWOTH +#define S_IWOTH 2 +#endif +#ifndef S_IROTH +#define S_IROTH 4 +#endif +#ifndef S_IXGRP +#define S_IXGRP (S_IXOTH << 3) +#endif +#ifndef S_IWGRP +#define S_IWGRP (S_IWOTH << 3) +#endif +#ifndef S_IRGRP +#define S_IRGRP (S_IROTH << 3) +#endif +#ifndef S_IXUSR +#define S_IXUSR (S_IXOTH << 6) +#endif +#ifndef S_IWUSR +#define S_IWUSR (S_IWOTH << 6) +#endif +#ifndef S_IRUSR +#define S_IRUSR (S_IROTH << 6) +#endif + +#if HAVE_LIMITS_H +# include +#endif +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif +#ifndef LONG_MIN +#define LONG_MIN (-1-2147483647L) +#endif + +#include +/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given + as an argument to macros like `isspace'. */ +#if STDC_HEADERS +#define CTYPE_DOMAIN(c) 1 +#else +#define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) +#endif +#ifndef ISSPACE +#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) +#endif + +#ifndef ISDIGIT +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) +#endif + + +#ifndef FILESYSTEM_PREFIX_LEN +#define FILESYSTEM_PREFIX_LEN(f) 0 +#endif + +#ifndef ISSLASH +#define ISSLASH(c) ((c) == '/') +#endif + + +/* constants */ + +/* AIX predefines these. */ +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif +#define TRUE 1 +#define FALSE 0 + +/* handy definitions */ + +#define strEQ(s1,s2) (!strcmp(s1, s2)) +#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l)) + +/* typedefs */ + +typedef int bool; /* must promote to itself */ +typedef long LINENUM; /* must be signed */ + +/* globals */ + +extern char const program_name[]; + +XTERN char *buf; /* general purpose buffer */ +XTERN size_t bufsize; /* allocated size of buf */ + +XTERN bool using_plan_a; /* try to keep everything in memory */ + +XTERN char *inname; +XTERN char *outfile; +XTERN int inerrno; +XTERN int invc; +XTERN struct stat instat; +XTERN bool dry_run; +XTERN bool posixly_correct; + +XTERN char const *origprae; +XTERN char const *origbase; + +XTERN char const * volatile TMPOUTNAME; +XTERN char const * volatile TMPINNAME; +XTERN char const * volatile TMPPATNAME; + +#ifdef DEBUGGING +XTERN int debug; +#else +# define debug 0 +#endif +XTERN bool force; +XTERN bool batch; +XTERN bool noreverse; +XTERN int reverse; +XTERN enum { DEFAULT_VERBOSITY, SILENT, VERBOSE } verbosity; +XTERN bool skip_rest_of_patch; +XTERN int strippath; +XTERN bool canonicalize; +XTERN int patch_get; +XTERN int set_time; +XTERN int set_utc; + +enum diff + { + NO_DIFF, + CONTEXT_DIFF, + NORMAL_DIFF, + ED_DIFF, + NEW_CONTEXT_DIFF, + UNI_DIFF + }; + +XTERN enum diff diff_type; + +XTERN char *revision; /* prerequisite revision, if any */ + +#ifdef __STDC__ +# define GENERIC_OBJECT void +#else +# define GENERIC_OBJECT char +#endif + +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) || __STRICT_ANSI__ +# define __attribute__(x) +#endif + +#ifndef PARAMS +# ifdef __STDC__ +# define PARAMS(args) args +# else +# define PARAMS(args) () +# endif +#endif + +GENERIC_OBJECT *xmalloc PARAMS ((size_t)); +void fatal_exit PARAMS ((int)) __attribute__ ((noreturn)); + +#include +#if !STDC_HEADERS && !defined errno +extern int errno; +#endif + +#if STDC_HEADERS || HAVE_STRING_H +# include +#else +# if !HAVE_MEMCHR +# define memcmp(s1, s2, n) bcmp (s1, s2, n) +# define memcpy(d, s, n) bcopy (s, d, n) +GENERIC_OBJECT *memchr (); +# endif +#endif + +#if STDC_HEADERS +# include +#else +long atol (); +char *getenv (); +GENERIC_OBJECT *malloc (); +GENERIC_OBJECT *realloc (); +#endif + +#if HAVE_UNISTD_H +# include +#endif +#ifndef lseek +off_t lseek (); +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif +#if _LFS_LARGEFILE + typedef off_t file_offset; +# define file_seek fseeko +# define file_tell ftello +#else + typedef long file_offset; +# define file_seek fseek +# define file_tell ftell +#endif + +#if HAVE_FCNTL_H +# include +#endif +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif +#ifndef O_WRONLY +#define O_WRONLY 1 +#endif +#ifndef O_RDWR +#define O_RDWR 2 +#endif +#ifndef _O_BINARY +#define _O_BINARY 0 +#endif +#ifndef O_BINARY +#define O_BINARY _O_BINARY +#endif +#ifndef O_CREAT +#define O_CREAT 0 +#endif +#ifndef O_TRUNC +#define O_TRUNC 0 +#endif + +#if HAVE_SETMODE + XTERN int binary_transput; /* O_BINARY if binary i/o is desired */ +#else +# define binary_transput 0 +#endif + +#ifndef NULL_DEVICE +#define NULL_DEVICE "/dev/null" +#endif + +#ifndef TTY_DEVICE +#define TTY_DEVICE "/dev/tty" +#endif diff --git a/contrib/patch/getopt.c b/contrib/patch/getopt.c new file mode 100644 index 0000000..300f86d --- /dev/null +++ b/contrib/patch/getopt.c @@ -0,0 +1,1053 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#include +#endif /* GNU C library. */ + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif + +#if defined (WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +#include +#define getpid() GetCurrentProcessId() +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +#ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, 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. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 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 -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +extern pid_t __libc_pid; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +text_set_element (__libc_subinit, store_args_and_env); + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* 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. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); + memset (&new_str[nonoption_flags_max_len], '\0', + top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + { + memcpy (__getopt_nonoption_flags, orig_str, len); + memset (&__getopt_nonoption_flags[len], '\0', + nonoption_flags_max_len - len); + } + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* 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 (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + + nextchar += strlen (nextchar); + + optopt = pfound->val; + 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); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[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; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#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 == -1) + 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/contrib/patch/getopt.h b/contrib/patch/getopt.h new file mode 100644 index 0000000..69256fd --- /dev/null +++ b/contrib/patch/getopt.h @@ -0,0 +1,133 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +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 -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/contrib/patch/getopt1.c b/contrib/patch/getopt1.c new file mode 100644 index 0000000..6507ba1 --- /dev/null +++ b/contrib/patch/getopt1.c @@ -0,0 +1,189 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "getopt.h" + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + 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/contrib/patch/inp.c b/contrib/patch/inp.c new file mode 100644 index 0000000..ebc2a03 --- /dev/null +++ b/contrib/patch/inp.c @@ -0,0 +1,442 @@ +/* inputting files to be patched */ + +/* $Id: inp.c,v 1.16 1997/06/13 06:28:37 eggert Exp $ */ + +/* +Copyright 1986, 1988 Larry Wall +Copyright 1991, 1992, 1993, 1997 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; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define XTERN extern +#include +#include +#include +#include +#undef XTERN +#define XTERN +#include + +/* Input-file-with-indexable-lines abstract type */ + +static char const **i_ptr; /* pointers to lines in plan A buffer */ + +static size_t tibufsize; /* size of plan b buffers */ +#ifndef TIBUFSIZE_MINIMUM +#define TIBUFSIZE_MINIMUM (8 * 1024) /* minimum value for tibufsize */ +#endif +static int tifd = -1; /* plan b virtual string array */ +static char *tibuf[2]; /* plan b buffers */ +static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ +static LINENUM lines_per_buf; /* how many lines per buffer */ +static size_t tireclen; /* length of records in tmp file */ +static size_t last_line_size; /* size of last input line */ + +static bool plan_a PARAMS ((char const *));/* yield FALSE if memory runs out */ +static void plan_b PARAMS ((char const *)); +static void report_revision PARAMS ((int)); + +/* New patch--prepare to edit another file. */ + +void +re_input() +{ + if (using_plan_a) { + if (i_ptr) { + free (i_ptr); + i_ptr = 0; + } + } + else { + close (tifd); + tifd = -1; + free(tibuf[0]); + tibuf[0] = 0; + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Construct the line index, somehow or other. */ + +void +scan_input(filename) +char *filename; +{ + using_plan_a = ! (debug & 16) && plan_a (filename); + if (!using_plan_a) + plan_b(filename); + switch (verbosity) + { + case SILENT: + break; + + case VERBOSE: + say ("Patching file `%s' using Plan %s...\n", + filename, using_plan_a ? "A" : "B"); + break; + + case DEFAULT_VERBOSITY: + say ("patching file `%s'\n", filename); + break; + } +} + +/* Report whether a desired revision was found. */ + +static void +report_revision (found_revision) + int found_revision; +{ + if (found_revision) + { + if (verbosity == VERBOSE) + say ("Good. This file appears to be the %s version.\n", revision); + } + else if (force) + { + if (verbosity != SILENT) + say ("Warning: this file doesn't appear to be the %s version -- patching anyway.\n", + revision); + } + else if (batch) + { + fatal ("This file doesn't appear to be the %s version -- aborting.", + revision); + } + else + { + ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal ("aborted"); + } +} + + +void +get_input_file (filename, outname) + char const *filename; + char const *outname; +{ + int elsewhere = strcmp (filename, outname); + char const *cs; + char *diffbuf; + char *getbuf; + + if (inerrno == -1) + inerrno = stat (inname, &instat) == 0 ? 0 : errno; + + /* Perhaps look for RCS or SCCS versions. */ + if (patch_get + && invc != 0 + && (inerrno + || (! elsewhere + && (/* No one can write to it. */ + (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0 + /* Only the owner (who's not me) can write to it. */ + || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0 + && instat.st_uid != geteuid ())))) + && (invc = !! (cs = (version_controller + (filename, elsewhere, + inerrno ? (struct stat *) 0 : &instat, + &getbuf, &diffbuf))))) { + + if (!inerrno) { + if (!elsewhere + && (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) + /* Somebody can write to it. */ + fatal ("file `%s' seems to be locked by somebody else under %s", + filename, cs); + /* It might be checked out unlocked. See if it's safe to + check out the default version locked. */ + if (verbosity == VERBOSE) + say ("Comparing file `%s' to default %s version...\n", + filename, cs); + if (systemic (diffbuf) != 0) + { + say ("warning: patching file `%s', which does not match default %s version\n", + filename, cs); + cs = 0; + } + } + + if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf, + &instat)) + inerrno = 0; + + free (getbuf); + free (diffbuf); + + } else if (inerrno && !pch_says_nonexistent (reverse)) + { + errno = inerrno; + pfatal ("can't find file `%s'", filename); + } + + if (inerrno) + { + instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + instat.st_size = 0; + } + else if (! S_ISREG (instat.st_mode)) + fatal ("`%s' is not a regular file -- can't patch", filename); +} + + +/* Try keeping everything in memory. */ + +static bool +plan_a(filename) + char const *filename; +{ + register char const *s; + register char const *lim; + register char const **ptr; + register char *buffer; + register LINENUM iline; + size_t size = instat.st_size; + size_t allocated_bytes_per_input_byte = sizeof *i_ptr + sizeof (char); + size_t allocated_bytes = (size + 2) * allocated_bytes_per_input_byte; + + /* Fail if arithmetic overflow occurs during size calculations, + or if storage isn't available. */ + if (size != instat.st_size + || size + 2 < 2 + || allocated_bytes / allocated_bytes_per_input_byte != size + 2 + || ! (ptr = (char const **) malloc (allocated_bytes))) + return FALSE; + + buffer = (char *) (ptr + (size + 2)); + + /* Read the input file, but don't bother reading it if it's empty. + When creating files, the files do not actually exist. */ + if (size) + { + int ifd = open (filename, O_RDONLY|binary_transput); + size_t buffered = 0, n; + if (ifd < 0) + pfatal ("can't open file `%s'", filename); + while (size - buffered != 0 + && (n = read (ifd, buffer + buffered, size - buffered)) != 0) + { + if (n == -1) + { + /* Perhaps size is too large for this host. */ + close (ifd); + free (ptr); + return FALSE; + } + buffered += n; + } + + /* Some non-POSIX hosts exaggerate st_size in text mode; + or the file may have shrunk! */ + size = buffered; + + if (close (ifd) != 0) + read_fatal (); + } + + /* Scan the buffer and build array of pointers to lines. */ + iline = 0; + lim = buffer + size; + for (s = buffer; ; s++) + { + ptr[++iline] = s; + if (! (s = (char *) memchr (s, '\n', lim - s))) + break; + } + if (size && lim[-1] != '\n') + ptr[++iline] = lim; + input_lines = iline - 1; + + if (revision) + { + char const *rev = revision; + int rev0 = rev[0]; + int found_revision = 0; + size_t revlen = strlen (rev); + + if (revlen <= size) + { + char const *limrev = lim - revlen; + + for (s = buffer; (s = (char *) memchr (s, rev0, limrev - s)); s++) + if (memcmp (s, rev, revlen) == 0 + && (s == buffer || ISSPACE ((unsigned char) s[-1])) + && (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen]))) + { + found_revision = 1; + break; + } + } + + report_revision (found_revision); + } + + /* Plan A will work. */ + i_ptr = ptr; + return TRUE; +} + +/* Keep (virtually) nothing in memory. */ + +static void +plan_b(filename) + char const *filename; +{ + register FILE *ifp; + register int c; + register size_t len; + register size_t maxlen; + register int found_revision; + register size_t i; + register char const *rev; + register size_t revlen; + register LINENUM line; + + if (instat.st_size == 0) + filename = NULL_DEVICE; + if (! (ifp = fopen (filename, binary_transput ? "rb" : "r"))) + pfatal ("can't open file `%s'", filename); + tifd = create_file (TMPINNAME, O_RDWR | O_BINARY, (mode_t) 0); + i = 0; + len = 0; + maxlen = 1; + rev = revision; + found_revision = !rev; + revlen = rev ? strlen (rev) : 0; + + while ((c = getc (ifp)) != EOF) + { + len++; + + if (c == '\n') + { + if (maxlen < len) + maxlen = len; + len = 0; + } + + if (!found_revision) + { + if (i == revlen) + { + found_revision = ISSPACE ((unsigned char) c); + i = (size_t) -1; + } + else if (i != (size_t) -1) + i = rev[i]==c ? i + 1 : (size_t) -1; + + if (i == (size_t) -1 && ISSPACE ((unsigned char) c)) + i = 0; + } + } + + if (revision) + report_revision (found_revision); + Fseek (ifp, (off_t) 0, SEEK_SET); /* rewind file */ + for (tibufsize = TIBUFSIZE_MINIMUM; tibufsize < maxlen; tibufsize <<= 1) + continue; + lines_per_buf = tibufsize / maxlen; + tireclen = maxlen; + tibuf[0] = xmalloc (2 * tibufsize); + tibuf[1] = tibuf[0] + tibufsize; + + for (line = 1; ; line++) + { + char *p = tibuf[0] + maxlen * (line % lines_per_buf); + char const *p0 = p; + if (! (line % lines_per_buf)) /* new block */ + if (write (tifd, tibuf[0], tibufsize) != tibufsize) + write_fatal (); + if ((c = getc (ifp)) == EOF) + break; + + for (;;) + { + *p++ = c; + if (c == '\n') + { + last_line_size = p - p0; + break; + } + + if ((c = getc (ifp)) == EOF) + { + last_line_size = p - p0; + line++; + goto EOF_reached; + } + } + } + EOF_reached: + if (ferror (ifp) || fclose (ifp) != 0) + read_fatal (); + + if (line % lines_per_buf != 0) + if (write (tifd, tibuf[0], tibufsize) != tibufsize) + write_fatal (); + input_lines = line - 1; +} + +/* Fetch a line from the input file. */ + +char const * +ifetch (line, whichbuf, psize) +register LINENUM line; +int whichbuf; /* ignored when file in memory */ +size_t *psize; +{ + register char const *q; + register char const *p; + + if (line < 1 || line > input_lines) { + *psize = 0; + return ""; + } + if (using_plan_a) { + p = i_ptr[line]; + *psize = i_ptr[line + 1] - p; + return p; + } else { + LINENUM offline = line % lines_per_buf; + LINENUM baseline = line - offline; + + if (tiline[0] == baseline) + whichbuf = 0; + else if (tiline[1] == baseline) + whichbuf = 1; + else { + tiline[whichbuf] = baseline; + if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize), + SEEK_SET) == -1 + || read (tifd, tibuf[whichbuf], tibufsize) < 0) + read_fatal (); + } + p = tibuf[whichbuf] + (tireclen*offline); + if (line == input_lines) + *psize = last_line_size; + else { + for (q = p; *q++ != '\n'; ) + continue; + *psize = q - p; + } + return p; + } +} diff --git a/contrib/patch/inp.h b/contrib/patch/inp.h new file mode 100644 index 0000000..fd636bf --- /dev/null +++ b/contrib/patch/inp.h @@ -0,0 +1,10 @@ +/* inputting files to be patched */ + +/* $Id: inp.h,v 1.4 1997/04/07 01:07:00 eggert Exp $ */ + +XTERN LINENUM input_lines; /* how long is input file in lines */ + +char const *ifetch PARAMS ((LINENUM, int, size_t *)); +void get_input_file PARAMS ((char const *, char const *)); +void re_input PARAMS ((void)); +void scan_input PARAMS ((char *)); diff --git a/contrib/patch/maketime.c b/contrib/patch/maketime.c new file mode 100644 index 0000000..e66f9df --- /dev/null +++ b/contrib/patch/maketime.c @@ -0,0 +1,391 @@ +/* Convert struct partime into time_t. */ + +/* Copyright 1992, 1993, 1994, 1995, 1997 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + + This file is part of RCS. + + RCS 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. + + RCS 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 RCS; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + + */ + +#if has_conf_h +# include +#else +# if HAVE_CONFIG_H +# include +# else +# ifndef __STDC__ +# define const +# endif +# endif + /* MIPS RISCOS4.52 defines time_t in not . */ +# include +# if HAVE_LIMITS_H +# include +# endif +# ifndef LONG_MIN +# define LONG_MIN (-1-2147483647L) +# endif +# if STDC_HEADERS +# include +# endif +# include +# ifdef __STDC__ +# define P(x) x +# else +# define P(x) () +# endif +#endif + +#include +#include + +char const maketId[] = + "$Id: maketime.c,v 5.15 1997/06/17 16:54:36 eggert Exp $"; + +static int isleap P ((int)); +static int month_days P ((struct tm const *)); +static time_t maketime P ((struct partime const *, time_t)); + +/* For maximum portability, use only localtime and gmtime. + Make no assumptions about the time_t epoch or the range of time_t values. + Avoid mktime because it's not universal and because there's no easy, + portable way for mktime to yield the inverse of gmtime. */ + +#define TM_YEAR_ORIGIN 1900 + +static int +isleap (y) + int y; +{ + return (y & 3) == 0 && (y % 100 != 0 || y % 400 == 0); +} + +/* days in year before start of months 0-12 */ +static int const month_yday[] = +{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 +}; + +/* Yield the number of days in TM's month. */ +static int +month_days (tm) + struct tm const *tm; +{ + int m = tm->tm_mon; + return (month_yday[m + 1] - month_yday[m] + + (m == 1 && isleap (tm->tm_year + TM_YEAR_ORIGIN))); +} + +/* Convert UNIXTIME to struct tm form. + Use gmtime if available and if !LOCALZONE, localtime otherwise. */ +struct tm * +time2tm (unixtime, localzone) + time_t unixtime; + int localzone; +{ + struct tm *tm; +#ifdef TZ_is_unset + static char const *TZ; + if (!TZ && !(TZ = getenv ("TZ"))) + TZ_is_unset ("The TZ environment variable is not set; please set it to your timezone"); +#endif + if (localzone || !(tm = gmtime (&unixtime))) + tm = localtime (&unixtime); + return tm; +} + +/* Yield A - B, measured in seconds. */ +time_t +difftm (a, b) + struct tm const *a; + struct tm const *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int ac = ay / 100 - (ay % 100 < 0); + int bc = by / 100 - (by % 100 < 0); + int difference_in_day_of_year = a->tm_yday - b->tm_yday; + int intervening_leap_days = (((ay >> 2) - (by >> 2)) + - (ac - bc) + + ((ac >> 2) - (bc >> 2))); + time_t difference_in_years = ay - by; + time_t difference_in_days + = (difference_in_years * 365 + + (intervening_leap_days + difference_in_day_of_year)); + return (((((difference_in_days * 24 + + (a->tm_hour - b->tm_hour)) + * 60) + + (a->tm_min - b->tm_min)) + * 60) + + (a->tm_sec - b->tm_sec)); +} + +/* + * Adjust time T by adding SECONDS. SECONDS must be at most 24 hours' worth. + * Adjust only T's year, mon, mday, hour, min and sec members; + * plus adjust wday if it is defined. + */ +void +adjzone (t, seconds) + register struct tm *t; + long seconds; +{ + /* + * This code can be off by a second if SECONDS is not a multiple of 60, + * if T is local time, and if a leap second happens during this minute. + * But this bug has never occurred, and most likely will not ever occur. + * Liberia, the last country for which SECONDS % 60 was nonzero, + * switched to UTC in May 1972; the first leap second was in June 1972. + */ + int leap_second = t->tm_sec == 60; + long sec = seconds + (t->tm_sec - leap_second); + if (sec < 0) + { + if ((t->tm_min -= (59 - sec) / 60) < 0) + { + if ((t->tm_hour -= (59 - t->tm_min) / 60) < 0) + { + t->tm_hour += 24; + if (TM_DEFINED (t->tm_wday) && --t->tm_wday < 0) + t->tm_wday = 6; + if (--t->tm_mday <= 0) + { + if (--t->tm_mon < 0) + { + --t->tm_year; + t->tm_mon = 11; + } + t->tm_mday = month_days (t); + } + } + t->tm_min += 24 * 60; + } + sec += 24L * 60 * 60; + } + else if (60 <= (t->tm_min += sec / 60)) + if (24 <= (t->tm_hour += t->tm_min / 60)) + { + t->tm_hour -= 24; + if (TM_DEFINED (t->tm_wday) && ++t->tm_wday == 7) + t->tm_wday = 0; + if (month_days (t) < ++t->tm_mday) + { + if (11 < ++t->tm_mon) + { + ++t->tm_year; + t->tm_mon = 0; + } + t->tm_mday = 1; + } + } + t->tm_min %= 60; + t->tm_sec = (int) (sec % 60) + leap_second; +} + +/* + * Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise. + * Use only TM's year, mon, mday, hour, min, and sec members. + * Ignore TM's old tm_yday and tm_wday, but fill in their correct values. + * Yield -1 on failure (e.g. a member out of range). + * Posix 1003.1-1990 doesn't allow leap seconds, but some implementations + * have them anyway, so allow them if localtime/gmtime does. + */ +time_t +tm2time (tm, localzone) + struct tm *tm; + int localzone; +{ + /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */ + static time_t t_cache[2]; + static struct tm tm_cache[2]; + + time_t d, gt; + struct tm const *gtm; + /* + * The maximum number of iterations should be enough to handle any + * combinations of leap seconds, time zone rule changes, and solar time. + * 4 is probably enough; we use a bigger number just to be safe. + */ + int remaining_tries = 8; + + /* Avoid subscript errors. */ + if (12 <= (unsigned) tm->tm_mon) + return -1; + + tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday + - (tm->tm_mon < 2 || !isleap (tm->tm_year + TM_YEAR_ORIGIN)); + + /* Make a first guess. */ + gt = t_cache[localzone]; + gtm = gt ? &tm_cache[localzone] : time2tm (gt, localzone); + + /* Repeatedly use the error from the guess to improve the guess. */ + while ((d = difftm (tm, gtm)) != 0) + { + if (--remaining_tries == 0) + return -1; + gt += d; + gtm = time2tm (gt, localzone); + } + + /* + * Check that the guess actually matches; + * overflow can cause difftm to yield 0 even on differing times, + * or tm may have members out of range (e.g. bad leap seconds). + */ +#define TM_DIFFER(a,b) \ + ( \ + ((a)->tm_year ^ (b)->tm_year) | \ + ((a)->tm_mon ^ (b)->tm_mon) | \ + ((a)->tm_mday ^ (b)->tm_mday) | \ + ((a)->tm_hour ^ (b)->tm_hour) | \ + ((a)->tm_min ^ (b)->tm_min) | \ + ((a)->tm_sec ^ (b)->tm_sec) \ + ) + if (TM_DIFFER (tm, gtm)) + { + /* + * If gt is a leap second, try gt+1; if it is one greater than + * a leap second, try gt-1; otherwise, it doesn't matter. + * Leap seconds always fall at month end. + */ + int yd = tm->tm_year - gtm->tm_year; + gt += yd + (yd ? 0 : tm->tm_mon - gtm->tm_mon); + gtm = time2tm (gt, localzone); + if (TM_DIFFER (tm, gtm)) + return -1; + } + t_cache[localzone] = gt; + tm_cache[localzone] = *gtm; + + tm->tm_wday = gtm->tm_wday; + return gt; +} + +/* + * Check *PT and convert it to time_t. + * If it is incompletely specified, use DEFAULT_TIME to fill it out. + * Use localtime if PT->zone is the special value TM_LOCAL_ZONE. + * Yield -1 on failure. + * ISO 8601 day-of-year and week numbers are not yet supported. + */ +static time_t +maketime (pt, default_time) + struct partime const *pt; + time_t default_time; +{ + int localzone, wday; + struct tm tm; + struct tm *tm0 = 0; + time_t r; + + tm0 = 0; /* Keep gcc -Wall happy. */ + localzone = pt->zone == TM_LOCAL_ZONE; + + tm = pt->tm; + + if (TM_DEFINED (pt->ymodulus) || !TM_DEFINED (tm.tm_year)) + { + /* Get tm corresponding to default time. */ + tm0 = time2tm (default_time, localzone); + if (!localzone) + adjzone (tm0, pt->zone); + } + + if (TM_DEFINED (pt->ymodulus)) + tm.tm_year += + (tm0->tm_year + TM_YEAR_ORIGIN) / pt->ymodulus * pt->ymodulus; + else if (!TM_DEFINED (tm.tm_year)) + { + /* Set default year, month, day from current time. */ + tm.tm_year = tm0->tm_year + TM_YEAR_ORIGIN; + if (!TM_DEFINED (tm.tm_mon)) + { + tm.tm_mon = tm0->tm_mon; + if (!TM_DEFINED (tm.tm_mday)) + tm.tm_mday = tm0->tm_mday; + } + } + + /* Convert from partime year (Gregorian) to Posix year. */ + tm.tm_year -= TM_YEAR_ORIGIN; + + /* Set remaining default fields to be their minimum values. */ + if (!TM_DEFINED (tm.tm_mon)) + tm.tm_mon = 0; + if (!TM_DEFINED (tm.tm_mday)) + tm.tm_mday = 1; + if (!TM_DEFINED (tm.tm_hour)) + tm.tm_hour = 0; + if (!TM_DEFINED (tm.tm_min)) + tm.tm_min = 0; + if (!TM_DEFINED (tm.tm_sec)) + tm.tm_sec = 0; + + if (!localzone) + adjzone (&tm, -pt->zone); + wday = tm.tm_wday; + + /* Convert and fill in the rest of the tm. */ + r = tm2time (&tm, localzone); + + /* Check weekday. */ + if (r != -1 && TM_DEFINED (wday) && wday != tm.tm_wday) + return -1; + + return r; +} + +/* Parse a free-format date in SOURCE, yielding a Unix format time. */ +time_t +str2time (source, default_time, default_zone) + char const *source; + time_t default_time; + long default_zone; +{ + struct partime pt; + + if (*partime (source, &pt)) + return -1; + if (pt.zone == TM_UNDEFINED_ZONE) + pt.zone = default_zone; + return maketime (&pt, default_time); +} + +#if TEST +#include +int +main (argc, argv) + int argc; + char **argv; +{ + time_t default_time = time ((time_t *) 0); + long default_zone = argv[1] ? atol (argv[1]) : 0; + char buf[1000]; + while (fgets (buf, sizeof (buf), stdin)) + { + time_t t = str2time (buf, default_time, default_zone); + printf ("%s", asctime (gmtime (&t))); + } + return 0; +} +#endif diff --git a/contrib/patch/maketime.h b/contrib/patch/maketime.h new file mode 100644 index 0000000..3948eda --- /dev/null +++ b/contrib/patch/maketime.h @@ -0,0 +1,39 @@ +/* Yield time_t from struct partime yielded by partime. */ + +/* Copyright 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + + This file is part of RCS. + + RCS 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. + + RCS 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 RCS; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + + */ + +#if defined __STDC__ || has_prototypes +# define __MAKETIME_P(x) x +#else +# define __MAKETIME_P(x) () +#endif + +struct tm *time2tm __MAKETIME_P ((time_t, int)); +time_t difftm __MAKETIME_P ((struct tm const *, struct tm const *)); +time_t str2time __MAKETIME_P ((char const *, time_t, long)); +time_t tm2time __MAKETIME_P ((struct tm *, int)); +void adjzone __MAKETIME_P ((struct tm *, long)); diff --git a/contrib/patch/memchr.c b/contrib/patch/memchr.c new file mode 100644 index 0000000..c75217f --- /dev/null +++ b/contrib/patch/memchr.c @@ -0,0 +1,199 @@ +/* Copyright (C) 1991, 1993, 1996 Free Software Foundation, Inc. + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#undef __ptr_t +#if defined (__cplusplus) || (defined (__STDC__) && __STDC__) +# define __ptr_t void * +#else /* Not C++ or ANSI C. */ +# define __ptr_t char * +#endif /* C++ or ANSI C. */ + +#if defined (_LIBC) +# include +#endif + +#if defined (HAVE_LIMITS_H) || defined (_LIBC) +# include +#endif + +#define LONG_MAX_32_BITS 2147483647 + +#ifndef LONG_MAX +#define LONG_MAX LONG_MAX_32_BITS +#endif + +#include + + +/* Search no more than N bytes of S for C. */ + +__ptr_t +memchr (s, c, n) + const __ptr_t s; + int c; + size_t n; +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + + c = (unsigned char) c; + + /* Handle the first few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = (const unsigned char *) s; + n > 0 && ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + --n, ++char_ptr) + if (*char_ptr == c) + return (__ptr_t) char_ptr; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + + if (sizeof (longword) != 4 && sizeof (longword) != 8) + abort (); + +#if LONG_MAX <= LONG_MAX_32_BITS + magic_bits = 0x7efefeff; +#else + magic_bits = ((unsigned long int) 0x7efefefe << 32) | 0xfefefeff; +#endif + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; +#if LONG_MAX > LONG_MAX_32_BITS + charmask |= charmask << 32; +#endif + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (n >= sizeof (longword)) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C, not zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *longword_ptr++ ^ charmask; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0) + { + /* Which of the bytes was C? If none of them were, it was + a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) (longword_ptr - 1); + + if (cp[0] == c) + return (__ptr_t) cp; + if (cp[1] == c) + return (__ptr_t) &cp[1]; + if (cp[2] == c) + return (__ptr_t) &cp[2]; + if (cp[3] == c) + return (__ptr_t) &cp[3]; +#if LONG_MAX > 2147483647 + if (cp[4] == c) + return (__ptr_t) &cp[4]; + if (cp[5] == c) + return (__ptr_t) &cp[5]; + if (cp[6] == c) + return (__ptr_t) &cp[6]; + if (cp[7] == c) + return (__ptr_t) &cp[7]; +#endif + } + + n -= sizeof (longword); + } + + char_ptr = (const unsigned char *) longword_ptr; + + while (n-- > 0) + { + if (*char_ptr == c) + return (__ptr_t) char_ptr; + else + ++char_ptr; + } + + return 0; +} diff --git a/contrib/patch/partime.c b/contrib/patch/partime.c new file mode 100644 index 0000000..ce87109 --- /dev/null +++ b/contrib/patch/partime.c @@ -0,0 +1,742 @@ +/* Parse a string, yielding a struct partime that describes it. */ + +/* Copyright 1993, 1994, 1995, 1997 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + + This file is part of RCS. + + RCS 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. + + RCS 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 RCS; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + + */ + +#if has_conf_h +# include +#else +# if HAVE_CONFIG_H +# include +# else +# ifndef __STDC__ +# define const +# endif +# endif +# if HAVE_LIMITS_H +# include +# endif +# ifndef LONG_MIN +# define LONG_MIN (-1-2147483647L) +# endif +# if STDC_HEADERS +# include +# endif +# include +# ifdef __STDC__ +# define P(x) x +# else +# define P(x) () +# endif +#endif + +#include +#if STDC_HEADERS +# define CTYPE_DOMAIN(c) 1 +#else +# define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177) +#endif +#define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c)) +#define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c)) +#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) +#define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c)) +#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) + +#include + +char const partimeId[] = + "$Id: partime.c,v 5.16 1997/05/19 06:33:53 eggert Exp $"; + + +/* Lookup tables for names of months, weekdays, time zones. */ + +#define NAME_LENGTH_MAXIMUM 4 + +struct name_val + { + char name[NAME_LENGTH_MAXIMUM]; + int val; + }; + + +static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *)); +static char const *parse_fixed P ((char const *, int, int *)); +static char const *parse_pattern_letter P ((char const *, int, struct partime *)); +static char const *parse_prefix P ((char const *, struct partime *, int *)); +static char const *parse_ranged P ((char const *, int, int, int, int *)); +static int lookup P ((char const *, struct name_val const[])); +static int merge_partime P ((struct partime *, struct partime const *)); +static void undefine P ((struct partime *)); + + +static struct name_val const month_names[] = +{ + {"jan", 0}, + {"feb", 1}, + {"mar", 2}, + {"apr", 3}, + {"may", 4}, + {"jun", 5}, + {"jul", 6}, + {"aug", 7}, + {"sep", 8}, + {"oct", 9}, + {"nov", 10}, + {"dec", 11}, + {"", TM_UNDEFINED} +}; + +static struct name_val const weekday_names[] = +{ + {"sun", 0}, + {"mon", 1}, + {"tue", 2}, + {"wed", 3}, + {"thu", 4}, + {"fri", 5}, + {"sat", 6}, + {"", TM_UNDEFINED} +}; + +#define hr60nonnegative(t) ((t)/100 * 60 + (t)%100) +#define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t)) +#define zs(t,s) {s, hr60(t)} +#define zd(t,s,d) zs(t, s), zs((t)+100, d) + +static struct name_val const zone_names[] = +{ + zs (-1000, "hst"), /* Hawaii */ + zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */ + zd (- 900, "akst", "akdt"), /* Alaska */ + zd (- 800, "pst" , "pdt" ), /* Pacific */ + zd (- 700, "mst" , "mdt" ), /* Mountain */ + zd (- 600, "cst" , "cdt" ), /* Central */ + zd (- 500, "est" , "edt" ), /* Eastern */ + zd (- 400, "ast" , "adt" ), /* Atlantic */ + zd (- 330, "nst" , "ndt" ), /* Newfoundland */ + zs ( 000, "utc" ), /* Coordinated Universal */ + zs ( 000, "uct" ), /* " */ + zs ( 000, "cut" ), /* " */ + zs ( 000, "ut"), /* Universal */ + zs ( 000, "z"), /* Zulu (required by ISO 8601) */ + zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */ + zd ( 000, "wet" , "west"), /* Western European */ + zd ( 100, "cet" , "cest"), /* Central European */ + zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */ + zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */ + zd ( 200, "eet" , "eest"), /* Eastern European */ + zs ( 530, "ist" ), /* India */ + zd ( 900, "jst" , "jdt" ), /* Japan */ + zd ( 900, "kst" , "kdt" ), /* Korea */ + zd ( 1200, "nzst", "nzdt"), /* New Zealand */ + {"lt", 1}, +#if 0 + /* The following names are duplicates or are not well attested. + There are lots more where these came from. */ + zs (-1100, "sst" ), /* Samoan */ + zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */ + zd (- 500, "ast" , "adt" ), /* Acre */ + zd (- 400, "wst" , "wdt" ), /* Western Brazil */ + zd (- 400, "cst" , "cdt" ), /* Chile */ + zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */ + zs ( 000, "wat" ), /* West African */ + zs ( 100, "cat" ), /* Central African */ + zs ( 200, "sat" ), /* South African */ + zd ( 200, "ist" , "idt" ), /* Israel */ + zs ( 300, "eat" ), /* East African */ + zd ( 300, "msk" , "msd" ), /* Moscow */ + zd ( 330, "ist" , "idt" ), /* Iran */ + zs ( 800, "hkt" ), /* Hong Kong */ + zs ( 800, "sgt" ), /* Singapore */ + zd ( 800, "cst" , "cdt" ), /* China */ + zd ( 800, "wst" , "wst" ), /* Western Australia */ + zd ( 930, "cst" , "cst" ), /* Central Australia */ + zs ( 1000, "gst" ), /* Guam */ + zd ( 1000, "est" , "est" ), /* Eastern Australia */ +#endif + {"", -1} +}; + +/* Look for a prefix of S in TABLE, returning val for first matching entry. */ +static int +lookup (s, table) + char const *s; + struct name_val const table[]; +{ + int j; + char buf[NAME_LENGTH_MAXIMUM]; + + for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) + { + unsigned char c = *s++; + if (! ISALPHA (c)) + { + buf[j] = '\0'; + break; + } + buf[j] = ISUPPER (c) ? tolower (c) : c; + } + + for (;; table++) + for (j = 0; ; j++) + if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j]) + return table[0].val; + else if (buf[j] != table[0].name[j]) + break; +} + + +/* Set *T to ``undefined'' values. */ +static void +undefine (t) + struct partime *t; +{ + t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon + = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday + = t->ymodulus = t->yweek + = TM_UNDEFINED; + t->zone = TM_UNDEFINED_ZONE; +} + +/* Array of patterns to look for in a date string. + Order is important: we look for the first matching pattern + whose values do not contradict values that we already know about. + See `parse_pattern_letter' below for the meaning of the pattern codes. */ +static char const *const patterns[] = +{ + /* These traditional patterns must come first, + to prevent an ISO 8601 format from misinterpreting their prefixes. */ + "E_n_y", "x", /* RFC 822 */ + "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */ + "y/N/D$", /* traditional RCS */ + + /* ISO 8601:1988 formats, generalized a bit. */ + "y-N-D$", "4ND$", "Y-N$", + "RND$", "-R=N$", "-R$", "--N=D$", "N=DT", + "--N$", "---D$", "DT", + "Y-d$", "4d$", "R=d$", "-d$", "dT", + "y-W-X", "yWX", "y=W", + "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W", + "-w-X", "w-XT", "---X$", "XT", "4$", + "T", + "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$", + "Y", "Z", + + 0 +}; + +/* Parse an initial prefix of STR, setting *T accordingly. + Return the first character after the prefix, or 0 if it couldn't be parsed. + Start with pattern *PI; if success, set *PI to the next pattern to try. + Set *PI to -1 if we know there are no more patterns to try; + if *PI is initially negative, give up immediately. */ +static char const * +parse_prefix (str, t, pi) + char const *str; + struct partime *t; + int *pi; +{ + int i = *pi; + char const *pat; + unsigned char c; + + if (i < 0) + return 0; + + /* Remove initial noise. */ + while (! ISALNUM (c = *str) && c != '-' && c != '+') + { + if (! c) + { + undefine (t); + *pi = -1; + return str; + } + str++; + } + + /* Try a pattern until one succeeds. */ + while ((pat = patterns[i++]) != 0) + { + char const *s = str; + undefine (t); + do + { + if (! (c = *pat++)) + { + *pi = i; + return s; + } + } + while ((s = parse_pattern_letter (s, c, t)) != 0); + } + + return 0; +} + +/* Parse an initial prefix of S of length DIGITS; it must be a number. + Store the parsed number into *RES. + Return the first character after the prefix, or 0 if it wasn't parsed. */ +static char const * +parse_fixed (s, digits, res) + char const *s; + int digits, *res; +{ + int n = 0; + char const *lim = s + digits; + while (s < lim) + { + unsigned d = *s++ - '0'; + if (9 < d) + return 0; + n = 10 * n + d; + } + *res = n; + return s; +} + +/* Parse an initial prefix of S of length DIGITS; + it must be a number in the range LO through HI. + Store the parsed number into *RES. + Return the first character after the prefix, or 0 if it wasn't parsed. */ +static char const * +parse_ranged (s, digits, lo, hi, res) + char const *s; + int digits, lo, hi, *res; +{ + s = parse_fixed (s, digits, res); + return s && lo <= *res && *res <= hi ? s : 0; +} + +/* Parse an initial prefix of S of length DIGITS; + it must be a number in the range LO through HI + and it may be followed by a fraction to be computed using RESOLUTION. + Store the parsed number into *RES; store the fraction times RESOLUTION, + rounded to the nearest integer, into *FRES. + Return the first character after the prefix, or 0 if it wasn't parsed. */ +static char const * +parse_decimal (s, digits, lo, hi, resolution, res, fres) + char const *s; + int digits, lo, hi, resolution, *res, *fres; +{ + s = parse_fixed (s, digits, res); + if (s && lo <= *res && *res <= hi) + { + int f = 0; + if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1])) + { + char const *s1 = ++s; + int num10 = 0, denom10 = 10, product; + while (ISDIGIT (*++s)) + { + int d = denom10 * 10; + if (d / 10 != denom10) + return 0; /* overflow */ + denom10 = d; + } + s = parse_fixed (s1, (int) (s - s1), &num10); + product = num10 * resolution; + f = (product + (denom10 >> 1)) / denom10; + f -= f & (product % denom10 == denom10 >> 1); /* round to even */ + if (f < 0 || product/resolution != num10) + return 0; /* overflow */ + } + *fres = f; + return s; + } + return 0; +} + +/* Parse an initial prefix of S; it must denote a time zone. + Set *ZONE to the number of seconds east of GMT, + or to TM_LOCAL_ZONE if it is the local time zone. + Return the first character after the prefix, or 0 if it wasn't parsed. */ +char * +parzone (s, zone) + char const *s; + long *zone; +{ + char sign; + int hh, mm, ss; + int minutesEastOfUTC; + long offset, z; + + /* The formats are LT, n, n DST, nDST, no, o + where n is a time zone name + and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */ + switch (*s) + { + case '-': + case '+': + z = 0; + break; + + default: + minutesEastOfUTC = lookup (s, zone_names); + if (minutesEastOfUTC == -1) + return 0; + + /* Don't bother to check rest of spelling. */ + while (ISALPHA ((unsigned char) *s)) + s++; + + /* Don't modify LT. */ + if (minutesEastOfUTC == 1) + { + *zone = TM_LOCAL_ZONE; + return (char *) s; + } + + z = minutesEastOfUTC * 60L; + + /* Look for trailing " DST". */ + if ((s[-1] == 'T' || s[-1] == 't') + && (s[-2] == 'S' || s[-2] == 's') + && (s[-3] == 'D' || s[-3] == 'd')) + goto trailing_dst; + while (ISSPACE ((unsigned char) *s)) + s++; + if ((s[0] == 'D' || s[0] == 'd') + && (s[1] == 'S' || s[1] == 's') + && (s[2] == 'T' || s[2] == 't')) + { + s += 3; + trailing_dst: + *zone = z + 60*60; + return (char *) s; + } + + switch (*s) + { + case '-': + case '+': + break; + + default: + *zone = z; + return (char *) s; + } + + break; + } + + sign = *s++; + + if (! (s = parse_ranged (s, 2, 0, 23, &hh))) + return 0; + mm = ss = 0; + if (*s == ':') + s++; + if (ISDIGIT (*s)) + { + if (! (s = parse_ranged (s, 2, 0, 59, &mm))) + return 0; + if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1]) + && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss))) + return 0; + } + if (ISDIGIT (*s)) + return 0; + offset = (hh * 60 + mm) * 60L + ss; + *zone = z + (sign == '-' ? -offset : offset); + /* ?? Are fractions allowed here? If so, they're not implemented. */ + return (char *) s; +} + +/* Parse an initial prefix of S, matching the pattern whose code is C. + Set *T accordingly. + Return the first character after the prefix, or 0 if it wasn't parsed. */ +static char const * +parse_pattern_letter (s, c, t) + char const *s; + int c; + struct partime *t; +{ + switch (c) + { + case '$': /* The next character must be a non-digit. */ + if (ISDIGIT (*s)) + return 0; + break; + + case '-': + case '/': + case ':': + /* These characters stand for themselves. */ + if (*s++ != c) + return 0; + break; + + case '4': /* 4-digit year */ + s = parse_fixed (s, 4, &t->tm.tm_year); + break; + + case '=': /* optional '-' */ + s += *s == '-'; + break; + + case 'A': /* AM or PM */ + /* This matches the regular expression [AaPp][Mm]?. + It must not be followed by a letter or digit; + otherwise it would match prefixes of strings like "PST". */ + switch (*s++) + { + case 'A': + case 'a': + if (t->tm.tm_hour == 12) + t->tm.tm_hour = 0; + break; + + case 'P': + case 'p': + if (t->tm.tm_hour != 12) + t->tm.tm_hour += 12; + break; + + default: + return 0; + } + switch (*s) + { + case 'M': + case 'm': + s++; + break; + } + if (ISALNUM ((unsigned char) *s)) + return 0; + break; + + case 'D': /* day of month [01-31] */ + s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday); + break; + + case 'd': /* day of year [001-366] */ + s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday); + t->tm.tm_yday--; + break; + + case 'E': /* extended day of month [1-9, 01-31] */ + s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 31, + &t->tm.tm_mday); + break; + + case 'h': /* hour [00-23 followed by optional fraction] */ + { + int frac; + s = parse_decimal (s, 2, 0, 23, 60 * 60, &t->tm.tm_hour, &frac); + t->tm.tm_min = frac / 60; + t->tm.tm_sec = frac % 60; + } + break; + + case 'm': /* minute [00-59 followed by optional fraction] */ + s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec); + break; + + case 'n': /* month name [e.g. "Jan"] */ + if (! TM_DEFINED (t->tm.tm_mon = lookup (s, month_names))) + return 0; + /* Don't bother to check rest of spelling. */ + while (ISALPHA ((unsigned char) *s)) + s++; + break; + + case 'N': /* month [01-12] */ + s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon); + t->tm.tm_mon--; + break; + + case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */ + s = parse_fixed (s, 1, &t->tm.tm_year); + t->ymodulus = 10; + break; + + case_R: + case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */ + s = parse_fixed (s, 2, &t->tm.tm_year); + t->ymodulus = 100; + break; + + case 's': /* second [00-60 followed by optional fraction] */ + { + int frac; + s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac); + t->tm.tm_sec += frac; + } + break; + + case 'T': /* 'T' or 't' */ + switch (*s++) + { + case 'T': + case 't': + break; + default: + return 0; + } + break; + + case 't': /* traditional hour [1-9 or 01-12] */ + s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12, + &t->tm.tm_hour); + break; + + case 'w': /* 'W' or 'w' only (stands for current week) */ + switch (*s++) + { + case 'W': + case 'w': + break; + default: + return 0; + } + break; + + case 'W': /* 'W' or 'w', followed by a week of year [00-53] */ + switch (*s++) + { + case 'W': + case 'w': + break; + default: + return 0; + } + s = parse_ranged (s, 2, 0, 53, &t->yweek); + break; + + case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */ + s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday); + t->tm.tm_wday--; + break; + + case 'x': /* weekday name [e.g. "Sun"] */ + if (! TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names))) + return 0; + /* Don't bother to check rest of spelling. */ + while (ISALPHA ((unsigned char) *s)) + s++; + break; + + case 'y': /* either R or Y */ + if (ISDIGIT (s[0]) && ISDIGIT (s[1]) && ! ISDIGIT (s[2])) + goto case_R; + /* fall into */ + case 'Y': /* year in full [4 or more digits] */ + { + int len = 0; + while (ISDIGIT (s[len])) + len++; + if (len < 4) + return 0; + s = parse_fixed (s, len, &t->tm.tm_year); + } + break; + + case 'Z': /* time zone */ + s = parzone (s, &t->zone); + break; + + case '_': /* possibly empty sequence of non-alphanumerics */ + while (! ISALNUM ((unsigned char) *s) && *s) + s++; + break; + + default: /* bad pattern */ + return 0; + } + + return s; +} + +/* If there is no conflict, merge into *T the additional information in *U + and return 0. Otherwise do nothing and return -1. */ +static int +merge_partime (t, u) + struct partime *t; + struct partime const *u; +{ +# define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b)) + if (conflict (t->tm.tm_sec, u->tm.tm_sec) + || conflict (t->tm.tm_min, u->tm.tm_min) + || conflict (t->tm.tm_hour, u->tm.tm_hour) + || conflict (t->tm.tm_mday, u->tm.tm_mday) + || conflict (t->tm.tm_mon, u->tm.tm_mon) + || conflict (t->tm.tm_year, u->tm.tm_year) + || conflict (t->tm.tm_wday, u->tm.tm_yday) + || conflict (t->ymodulus, u->ymodulus) + || conflict (t->yweek, u->yweek) + || (t->zone != u->zone + && t->zone != TM_UNDEFINED_ZONE + && u->zone != TM_UNDEFINED_ZONE)) + return -1; +# undef conflict +# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b); + merge_ (t->tm.tm_sec, u->tm.tm_sec) + merge_ (t->tm.tm_min, u->tm.tm_min) + merge_ (t->tm.tm_hour, u->tm.tm_hour) + merge_ (t->tm.tm_mday, u->tm.tm_mday) + merge_ (t->tm.tm_mon, u->tm.tm_mon) + merge_ (t->tm.tm_year, u->tm.tm_year) + merge_ (t->tm.tm_wday, u->tm.tm_yday) + merge_ (t->ymodulus, u->ymodulus) + merge_ (t->yweek, u->yweek) +# undef merge_ + if (u->zone != TM_UNDEFINED_ZONE) + t->zone = u->zone; + return 0; +} + +/* Parse a date/time prefix of S, putting the parsed result into *T. + Return the first character after the prefix. + The prefix may contain no useful information; + in that case, *T will contain only undefined values. */ +char * +partime (s, t) + char const *s; + struct partime *t; +{ + struct partime p; + + undefine (t); + + while (*s) + { + int i = 0; + char const *s1; + + do + { + if (! (s1 = parse_prefix (s, &p, &i))) + return (char *) s; + } + while (merge_partime (t, &p) != 0); + + s = s1; + } + + return (char *) s; +} diff --git a/contrib/patch/partime.h b/contrib/patch/partime.h new file mode 100644 index 0000000..bcc8165 --- /dev/null +++ b/contrib/patch/partime.h @@ -0,0 +1,67 @@ +/* Parse a string, yielding a struct partime that describes it. */ + +/* Copyright 1993, 1994, 1995, 1997 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + + This file is part of RCS. + + RCS 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. + + RCS 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 RCS; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + + */ + +#define TM_UNDEFINED (-1) +#define TM_DEFINED(x) (0 <= (x)) + +/* #include if you want to use these symbols. */ +#define TM_LOCAL_ZONE LONG_MIN +#define TM_UNDEFINED_ZONE (LONG_MIN + 1) + +struct partime + { + /* This structure describes the parsed time. + Only the following tm_* values in it are used: + sec, min, hour, mday, mon, year, wday, yday. + If TM_UNDEFINED (value), the parser never found the value. + The tm_year field is the actual year, not the year - 1900; + but see ymodulus below. */ + struct tm tm; + + /* If !TM_UNDEFINED (ymodulus), + then tm.tm_year is actually modulo ymodulus. */ + int ymodulus; + + /* Week of year, ISO 8601 style. + If TM_UNDEFINED (yweek), the parser never found yweek. + Weeks start on Mondays. + Week 1 includes Jan 4. */ + int yweek; + + /* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE. */ + long zone; + }; + +#if defined __STDC__ || has_prototypes +# define __PARTIME_P(x) x +#else +# define __PARTIME_P(x) () +#endif + +char *partime __PARTIME_P ((char const *, struct partime *)); +char *parzone __PARTIME_P ((char const *, long *)); diff --git a/contrib/patch/patch.1 b/contrib/patch/patch.1 new file mode 100644 index 0000000..13fa1b9 --- /dev/null +++ b/contrib/patch/patch.1 @@ -0,0 +1,1063 @@ +.\" patch man page +.de Id +.ds Dt \\$4 +.. +.Id $Id: patch.man,v 1.20 1997/06/17 22:32:49 eggert Exp $ +.ds = \-\^\- +.de Sp +.if t .sp .3 +.if n .sp +.. +.TH PATCH 1 \*(Dt GNU +.ta 3n +.SH NAME +patch \- apply a diff file to an original +.SH SYNOPSIS +.B patch +.RI [ options ] +.RI [ originalfile +.RI [ patchfile ]] +.Sp +but usually just +.Sp +.BI "patch \-p" "num" +.BI < patchfile +.SH DESCRIPTION +.B patch +takes a patch file +.I patchfile +containing a difference listing produced by the +.B diff +program and applies those differences to one or more original files, +producing patched versions. +Normally the patched versions are put in place of the originals. +Backups can be made; see the +.B \-V +or +.B \*=version\-control +option. +The names of the files to be patched are usually taken from the patch file, +but if there's just one file to be patched it can specified on the +command line as +.IR originalfile . +.PP +Upon startup, patch attempts to determine the type of the diff listing, +unless overruled by a +\fB\-c\fP (\fB\*=context\fP), +\fB\-e\fP (\fB\*=ed\fP), +\fB\-n\fP (\fB\*=normal\fP), +or +\fB\-u\fP (\fB\*=unified\fP) +option. +Context diffs (old-style, new-style, and unified) and +normal diffs are applied by the +.B patch +program itself, while +.B ed +diffs are simply fed to the +.BR ed (1) +editor via a pipe. +.PP +.B patch +tries to skip any leading garbage, apply the diff, +and then skip any trailing garbage. +Thus you could feed an article or message containing a +diff listing to +.BR patch , +and it should work. +If the entire diff is indented by a consistent amount, +this is taken into account. +.PP +With context diffs, and to a lesser extent with normal diffs, +.B patch +can detect when the line numbers mentioned in the patch are incorrect, +and attempts to find the correct place to apply each hunk of the patch. +As a first guess, it takes the line number mentioned for the hunk, plus or +minus any offset used in applying the previous hunk. +If that is not the correct place, +.B patch +scans both forwards and backwards for a set of lines matching the context +given in the hunk. +First +.B patch +looks for a place where all lines of the context match. +If no such place is found, and it's a context diff, and the maximum fuzz factor +is set to 1 or more, then another scan takes place ignoring the first and last +line of context. +If that fails, and the maximum fuzz factor is set to 2 or more, +the first two and last two lines of context are ignored, +and another scan is made. +(The default maximum fuzz factor is 2.) +If +.B patch +cannot find a place to install that hunk of the patch, it puts the +hunk out to a reject file, which normally is the name of the output file +plus a +.B \&.rej +suffix, or +.B # +if +.B \&.rej +would generate a file name that is too long +(if even appending the single character +.B # +makes the file name too long, then +.B # +replaces the file name's last character). +(The rejected hunk comes out in ordinary context diff form regardless of +the input patch's form. +If the input was a normal diff, many of the contexts are simply null.) +The line numbers on the hunks in the reject file may be different than +in the patch file: they reflect the approximate location patch thinks the +failed hunks belong in the new file rather than the old one. +.PP +As each hunk is completed, you are told if the hunk +failed, and if so which line (in the new file) +.B patch +thought the hunk should go on. +If the hunk is installed at a different line +from the line number specified in the diff you +are told the offset. +A single large offset +.I may +indicate that a hunk was installed in the +wrong place. +You are also told if a fuzz factor was used to make the match, in which +case you should also be slightly suspicious. +If the +.B \*=verbose +option is given, you are also told about hunks that match exactly. +.PP +If no original file +.I origfile +is specified on the command line, +.B patch +tries to figure out from the leading garbage what the name of the file +to edit is, using the following rules. +.TP 3 +.B " \(bu" +If the header is that of a context diff, +.B patch +takes the old and new file names in the header. +Any +.B /dev/null +names are ignored. +.TP +.B " \(bu" +If there is an +.B Index:\& +line in the leading garbage +and if either the old and new names are both absent or the +.B POSIXLY_CORRECT +environment variable is set, +.B patch +takes the name in the +.B Index:\& +line. +.TP +.B " \(bu" +For the purpose of the following rules, +the names are considered to be in the order (old, new, index), +regardless of the order that they appear in the header. +.TP +.B " \(bu" +If some of the named files exist, +.B patch +uses the first name if the +.B POSIXLY_CORRECT +environment variable is set, and the best name otherwise. +.TP +.B " \(bu" +If +.B patch +is not ignoring \s-1RCS\s0 and \s-1SCCS\s0 (see the +.BI "\-g " num +or +.BI \*=get= num +option), and no named files exist +but an \s-1RCS\s0 or \s-1SCCS\s0 master is found, +.B patch +uses the first named file with an \s-1RCS\s0 or \s-1SCCS\s0 master. +.TP +.B " \(bu" +If no named files exist, no \s-1RCS\s0 or \s-1SCCS\s0 master was found, +some names are given, +.B POSIXLY_CORRECT +is not set, and the patch appears to create a file, +.B patch +uses the best name requiring the creation of the fewest directories. +.TP +.B " \(bu" +If no file name results from the above heuristics, you are asked +for the name of the file to patch. +.LP +To determine the +.I best +of a nonempty list of file names, +.B patch +first takes all the names with the fewest path name components; +of those, it then takes all the names with the shortest basename; +of those, it then takes all the shortest names; +finally, it takes the first remaining name. +.PP +Additionally, if the leading garbage contains a +.B Prereq:\& +line, +.B patch +takes the first word from the prerequisites line (normally a version +number) and checks the original file to see if that word can be found. +If not, +.B patch +asks for confirmation before proceeding. +.PP +The upshot of all this is that you should be able to say, while in a news +interface, something like the following: +.Sp + \fB| patch \-d /usr/src/local/blurfl\fP +.Sp +and patch a file in the +.B blurfl +directory directly from the article containing +the patch. +.PP +If the patch file contains more than one patch, +.B patch +tries to apply each of them as if they came from separate patch files. +This means, among other things, that it is assumed that the name of the file +to patch must be determined for each diff listing, +and that the garbage before each diff listing +contains interesting things such as file names and revision level, as +mentioned previously. +.SH OPTIONS +.TP 3 +\fB\-b\fP or \fB\*=backup\fP +Make backup files. +That is, when patching a file, +rename or copy the original instead of removing it. +When backing up a file that does not exist, +an empty, unreadable backup file is created +as a placeholder to represent the nonexistent file. +.Sp +This option is equivalent to +.BR \*=version\-control=simple ; +see the +.B \-V +or +.B \*=version\-control +option. +.TP +.B \*=backup\-if\-mismatch +Back up a file if the patch does not match the file exactly +and if backups are not otherwise requested. +The backup file name is calculated as usual, +except that if the version control method is +.BR none , +a simple backup name is used. +This is the default unless the +.B POSIXLY_CORRECT +environment variable is set. +.TP +.B \*=no\-backup\-if\-mismatch +Do not back up a file if the patch does not match the file exactly +and if backups are not otherwise requested. +This is the default if the +.B POSIXLY_CORRECT +environment variable is set. +.TP +\fB\-B\fP \fIpref\fP or \fB\*=prefix=\fP\fIpref\fP +Prefix simple backup file names with +.IR pref . +For example, with +.B "\-B /junk/" +the simple backup file name for +.B src/patch/util.c +is +.BR /junk/src/patch/util.c . +.TP +\fB\*=binary\fP +Read and write all files in binary mode, +except for standard output and +.BR /dev/tty . +This option has no effect on \s-1POSIX\s0-compliant systems. +On systems like \s-1DOS\s0 where this option makes a difference, +the patch should be generated by +.BR "diff\ \-a\ \*=binary" . +.TP +\fB\-c\fP or \fB\*=context\fP +Interpret the patch file as a ordinary context diff. +.TP +\fB\-d\fP \fIdir\fP or \fB\*=directory=\fP\fIdir\fP +Change to the directory +.I dir +immediately, before doing +anything else. +.TP +\fB\-D\fP \fIdefine\fP or \fB\*=ifdef=\fP\fIdefine\fP +Use the +.BR #ifdef " .\|.\|. " #endif +construct to mark changes, with +.I define +as the differentiating symbol. +.TP +.B "\*=dry\-run" +Print the results of applying the patches without actually changing any files. +.TP +\fB\-e\fP or \fB\*=ed\fP +Interpret the patch file as an +.B ed +script. +.TP +\fB\-E\fP or \fB\*=remove\-empty\-files\fP +Remove output files that are empty after the patches have been applied. +Normally this option is unnecessary, since +.B patch +can examine the time stamps on the header to determine whether a file +should exist after patching. +However, if the input is not a context diff or if the +.B POSIXLY_CORRECT +environment variable is set, +.B patch +does not remove empty patched files unless this option is given. +When +.B patch +removes a file, it also attempts to remove any empty ancestor directories. +.TP +\fB\-f\fP or \fB\*=force\fP +Assume that the user knows exactly what he or she is doing, and do not +ask any questions. Skip patches whose headers +do not say which file is to be patched; patch files even though they have the +wrong version for the +.B Prereq:\& +line in the patch; and assume that +patches are not reversed even if they look like they are. +This option does not suppress commentary; use +.B \-s +for that. +.TP +\fB\-F\fP \fInum\fP or \fB\*=fuzz=\fP\fInum\fP +Set the maximum fuzz factor. +This option only applies to diffs that have context, and causes +.B patch +to ignore up to that many lines in looking for places to install a hunk. +Note that a larger fuzz factor increases the odds of a faulty patch. +The default fuzz factor is 2, and it may not be set to more than +the number of lines of context in the context diff, ordinarily 3. +.TP +\fB\-g\fP \fInum\fP or \fB\*=get=\fP\fInum\fP +This option controls +.BR patch 's +actions when a file is under \s-1RCS\s0 or \s-1SCCS\s0 control, +and does not exist or is read-only and matches the default version. +If +.I num +is positive, +.B patch +gets (or checks out) the file from the revision control system; if zero, +.B patch +ignores \s-1RCS\s0 and \s-1SCCS\s0 and does not get the file; and if negative, +.B patch +asks the user whether to get the file. +The default value of this option is given by the value of the +.B PATCH_GET +environment variable if it is set; if not, the default value is zero if +.B POSIXLY_CORRECT +is set, negative otherwise. +.TP +.B "\*=help" +Print a summary of options and exit. +.TP +\fB\-i\fP \fIpatchfile\fP or \fB\*=input=\fP\fIpatchfile\fP +Read the patch from +.IR patchfile . +If +.I patchfile +is +.BR \- , +read from standard input, the default. +.TP +\fB\-l\fP or \fB\*=ignore\-whitespace\fP +Match patterns loosely, in case tabs or spaces +have been munged in your files. +Any sequence of one or more blanks in the patch file matches any sequence +in the original file, and sequences of blanks at the ends of lines are ignored. +Normal characters must still match exactly. +Each line of the context must still match a line in the original file. +.TP +\fB\-n\fP or \fB\*=normal\fP +Interpret the patch file as a normal diff. +.TP +\fB\-N\fP or \fB\*=forward\fP +Ignore patches that seem to be reversed or already applied. +See also +.BR \-R . +.TP +\fB\-o\fP \fIoutfile\fP or \fB\*=output=\fP\fIoutfile\fP +Send output to +.I outfile +instead of patching files in place. +.TP +\fB\-p\fP\fInum\fP or \fB\*=strip\fP\fB=\fP\fInum\fP +Strip the smallest prefix containing +.I num +leading slashes from each file name found in the patch file. +A sequence of one or more adjacent slashes is counted as a single slash. +This controls how file names found in the patch file are treated, in case +you keep your files in a different directory than the person who sent +out the patch. +For example, supposing the file name in the patch file was +.Sp + \fB/u/howard/src/blurfl/blurfl.c\fP +.Sp +setting +.B \-p0 +gives the entire file name unmodified, +.B \-p1 +gives +.Sp + \fBu/howard/src/blurfl/blurfl.c\fP +.Sp +without the leading slash, +.B \-p4 +gives +.Sp + \fBblurfl/blurfl.c\fP +.Sp +and not specifying +.B \-p +at all just gives you \fBblurfl.c\fP. +Whatever you end up with is looked for either in the current directory, +or the directory specified by the +.B \-d +option. +.TP +\fB\-r\fP \fIrejectfile\fP or \fB\*=reject\-file=\fP\fIrejectfile\fP +Put rejects into +.I rejectfile +instead of the default +.B \&.rej +file. +.TP +\fB\-R\fP or \fB\*=reverse\fP +Assume that this patch was created with the old and new files swapped. +(Yes, I'm afraid that does happen occasionally, human nature being what it +is.) +.B patch +attempts to swap each hunk around before applying it. +Rejects come out in the swapped format. +The +.B \-R +option does not work with +.B ed +diff scripts because there is too little +information to reconstruct the reverse operation. +.Sp +If the first hunk of a patch fails, +.B patch +reverses the hunk to see if it can be applied that way. +If it can, you are asked if you want to have the +.B \-R +option set. +If it can't, the patch continues to be applied normally. +(Note: this method cannot detect a reversed patch if it is a normal diff +and if the first command is an append (i.e. it should have been a delete) +since appends always succeed, due to the fact that a null context matches +anywhere. +Luckily, most patches add or change lines rather than delete them, so most +reversed normal diffs begin with a delete, which fails, triggering +the heuristic.) +.TP +\fB\-s\fP or \fB\*=silent\fP or \fB\*=quiet\fP +Work silently, unless an error occurs. +.TP +\fB\-t\fP or \fB\*=batch\fP +Suppress questions like +.BR \-f , +but make some different assumptions: +skip patches whose headers do not contain file names (the same as \fB\-f\fP); +skip patches for which the file has the wrong version for the +.B Prereq:\& +line +in the patch; and assume that patches are reversed if they look like +they are. +.TP +\fB\-T\fP or \fB\*=set\-time\fP +Set the modification and access times of patched files from time stamps +given in context diff headers, assuming that the context diff headers +use local time. This option is not recommended, because patches using +local time cannot easily be used by people in other time zones, and +because local time stamps are ambiguous when local clocks move backwards +during daylight-saving time adjustments. Instead of using this option, +generate patches with \s-1UTC\s0 and use the +.B \-Z +or +.B \*=set\-utc +option instead. +.TP +\fB\-u\fP or \fB\*=unified\fP +Interpret the patch file as a unified context diff. +.TP +\fB\-v\fP or \fB\*=version\fP +Print out +.BR patch 's +revision header and patch level, and exit. +.TP +\fB\-V\fP \fImethod\fP or \fB\*=version\-control=\fP\fImethod\fP +Use +.I method +to determine +backup file names. The method can also be given by the +.B PATCH_VERSION_CONTROL +(or, if that's not set, the +.BR VERSION_CONTROL ) +environment variable, which is overridden by this option. +.Sp +The value of +.I method +is like the \s-1GNU\s0 +Emacs `version-control' variable; +.B patch +also recognizes synonyms that +are more descriptive. The valid values for +.I method +are (unique abbreviations are +accepted): +.RS +.TP 3 +\fBexisting\fP or \fBnil\fP +Make numbered backups of files that already have them, +otherwise simple backups. +.TP +\fBnone\fP +Do not make backups, unless backup-if-mismatch is in effect +and patches do not match files. +This is the default. +.TP +\fBnumbered\fP or \fBt\fP +Make numbered backups. The numbered backup file name for +.I F +is +.IB F .~ N ~ +where +.I N +is the version number. +.TP +\fBsimple\fP or \fBnever\fP +Make simple backups. +The +.B \-B +or +.BR \*=prefix , +.B \-y +or +.BR \*=basename\-prefix , +and +.B \-z +or +.BR \*=suffix +options specify the simple backup file name. +If none of these options are given, then a simple backup suffix is used; +it is the value of the +.B SIMPLE_BACKUP_SUFFIX +environment variable if set, and is +.B \&.orig +otherwise. +.PP +With numbered or simple backups, +if the backup file name is too long, the backup suffix +.B ~ +is used instead; if even appending +.B ~ +would make the name too long, then +.B ~ +replaces the last character of the file name. +.RE +.TP +\fB\*=verbose\fP +Output extra information about the work being done. +.TP +\fB\-x\fP \fInum\fP or \fB\*=debug=\fP\fInum\fP +Set internal debugging flags of interest only to +.B patch +patchers. +.TP +\fB\-y\fP \fIpref\fP or \fB\*=basename\-prefix=\fP\fIpref\fP +Prefix the basename of the simple backup file name with +.IR pref . +For example, with +.B "\-y .del/" +the backup file name for +.B src/patch/util.c +is +.BR src/patch/.del/util.c . +.TP +\fB\-z\fP \fIsuffix\fP or \fB\*=suffix=\fP\fIsuffix\fP +Use +.I suffix +as the simple backup suffix. +The backup extension may also be specified by the +.B SIMPLE_BACKUP_SUFFIX +environment variable, which is overridden by this option. +.TP +\fB\-Z\fP or \fB\*=set\-utc\fP +Set the modification and access times of patched files from time stamps +given in context diff headers, assuming that the context diff headers +use Coordinated Universal Time (\s-1UTC\s0, often known as \s-1GMT\s0). +Also see the +.B \-T +or +.B \*=set\-time +option. +.Sp +The +.B \-Z +or +.B \*=set\-utc +and +.B \-T +or +.B \*=set\-time +options normally refrain from setting a file's time if the file's original time +does not match the time given in the patch header, or if its +contents do not match the patch exactly. However, if the +.B \-f +or +.B \*=force +option is given, the file time is set regardless. +.Sp +Due to the limitations of +.B diff +output format, these options cannot update the times of files whose +contents have not changed. Also, if you use these options, you should remove +(e.g. with +.BR "make\ clean" ) +all files that depend on the patched files, so that later invocations of +.B make +do not get confused by the patched files' times. +.SH ENVIRONMENT +.TP 3 +\fBPATCH_GET\fP +This specifies whether +.B patch +gets missing or read-only files from \s-1RCS\s0 or \s-1SCCS\s0 +by default; see the +.B \-g +or +.B \*=get +option. +.TP +.B POSIXLY_CORRECT +If set, +.B patch +conforms more strictly to the \s-1POSIX\s0 standard: +it takes the first existing file from the list (old, new, index) +when intuiting file names from diff headers, +it does not remove files that are empty after patching, +it does not ask whether to get files from \s-1RCS\s0 or \s-1SCCS\s0, +it requires that all options precede the +files in the command line, +and by default it does not make backup files. +.TP +.B SIMPLE_BACKUP_SUFFIX +Extension to use for simple backup file names instead of +.BR \&.orig . +.TP +\fBTMPDIR\fP, \fBTMP\fP, \fBTEMP\fP +Directory to put temporary files in; +.B patch +uses the first environment variable in this list that is set. +If none are set, the default is system-dependent; +it is normally +.B /tmp +on Unix hosts. +.TP +\fBVERSION_CONTROL\fP or \fBPATCH_VERSION_CONTROL\fP +Selects version control style; see the +.B \-v +or +.B \*=version\-control +option. +.SH FILES +.TP 3 +.IB $TMPDIR "/p\(**" +temporary files +.TP +.B /dev/tty +controlling terminal; used to get answers to questions asked of the user +.SH "SEE ALSO" +.BR diff (1), +.BR ed (1) +.SH "NOTES FOR PATCH SENDERS" +There are several things you should bear in mind if you are going to +be sending out patches. +.PP +Create your patch systematically. +A good method is the command +.BI "diff\ \-Naur\ " "old\ new" +where +.I old +and +.I new +identify the old and new directories. +The names +.I old +and +.I new +should not contain any slashes. +The +.B diff +command's headers should have dates +and times in Universal Time using traditional Unix format, +so that patch recipients can use the +.B \-Z +or +.B \*=set\-utc +option. +Here is an example command, using Bourne shell syntax: +.Sp + \fBLC_ALL=C TZ=UTC0 diff \-Naur gcc\-2.7 gcc\-2.8\fP +.PP +Tell your recipients how to apply the patch +by telling them which directory to +.B cd +to, and which +.B patch +options to use. The option string +.B "\-Np1" +is recommended. +Test your procedure by pretending to be a recipient and applying +your patch to a copy of the original files. +.PP +You can save people a lot of grief by keeping a +.B patchlevel.h +file which is patched to increment the patch level +as the first diff in the patch file you send out. +If you put a +.B Prereq:\& +line in with the patch, it won't let them apply +patches out of order without some warning. +.PP +You can create a file by sending out a diff that compares +.B /dev/null +or an empty file dated the Epoch (1970-01-01 00:00:00 \s-1UTC\s0) +to the file you want to create. +This only works if the file you want to create doesn't exist already in +the target directory. +Conversely, you can remove a file by sending out a context diff that compares +the file to be deleted with an empty file dated the Epoch. +The file will be removed unless the +.B POSIXLY_CORRECT +environment variable is set and the +.B \-E +or +.B \*=remove\-empty\-files +option is not given. +An easy way to generate patches that create and remove files +is to use \s-1GNU\s0 +.BR diff 's +.B \-N +or +.B \*=new\-file +option. +.PP +If the recipient is supposed to use the +.BI \-p N +option, do not send output that looks like this: +.Sp +.ft B +.ne 3 + diff \-Naur v2.0.29/prog/README prog/README +.br + \-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 1997 +.br + +\^+\^+ prog/README Mon Mar 17 14:58:22 1997 +.ft +.Sp +because the two file names have different numbers of slashes, +and different versions of +.B patch +interpret the file names differently. +To avoid confusion, send output that looks like this instead: +.Sp +.ft B +.ne 3 + diff \-Naur v2.0.29/prog/README v2.0.30/prog/README +.br + \-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 1997 +.br + +\^+\^+ v2.0.30/prog/README Mon Mar 17 14:58:22 1997 +.ft +.Sp +.PP +Avoid sending patches that compare backup file names like +.BR README.orig , +since this might confuse +.B patch +into patching a backup file instead of the real file. +Instead, send patches that compare the same base file names +in different directories, e.g.\& +.B old/README +and +.BR new/README . +.PP +Take care not to send out reversed patches, since it makes people wonder +whether they already applied the patch. +.PP +Try not to have your patch modify derived files +(e.g. the file +.B configure +where there is a line +.B "configure: configure.in" +in your makefile), since the recipient should be +able to regenerate the derived files anyway. +If you must send diffs of derived files, +generate the diffs using \s-1UTC\s0, +have the recipients apply the patch with the +.B \-Z +or +.B \*=set\-utc +option, and have them remove any unpatched files that depend on patched files +(e.g. with +.BR "make\ clean" ). +.PP +While you may be able to get away with putting 582 diff listings into +one file, it may be wiser to group related patches into separate files in +case something goes haywire. +.SH DIAGNOSTICS +Diagnostics generally indicate that +.B patch +couldn't parse your patch file. +.PP +If the +.B \*=verbose +option is given, the message +.B Hmm.\|.\|.\& +indicates that there is unprocessed text in +the patch file and that +.B patch +is attempting to intuit whether there is a patch in that text and, if so, +what kind of patch it is. +.PP +.BR patch 's +exit status is +0 if all hunks are applied successfully, +1 if some hunks cannot be applied, +and 2 if there is more serious trouble. +When applying a set of patches in a loop it behooves you to check this +exit status so you don't apply a later patch to a partially patched file. +.SH CAVEATS +Context diffs cannot reliably represent the creation or deletion of +empty files, empty directories, or special files such as symbolic links. +Nor can they represent changes to file metadata like ownership, permissions, +or whether one file is a hard link to another. +If changes like these are also required, separate instructions +(e.g. a shell script) to accomplish them should accompany the patch. +.PP +.B patch +cannot tell if the line numbers are off in an +.B ed +script, and can detect +bad line numbers in a normal diff only when it finds a change or deletion. +A context diff using fuzz factor 3 may have the same problem. +Until a suitable interactive interface is added, you should probably do +a context diff in these cases to see if the changes made sense. +Of course, compiling without errors is a pretty good indication that the patch +worked, but not always. +.PP +.B patch +usually produces the correct results, even when it has to do a lot of +guessing. +However, the results are guaranteed to be correct only when the patch is +applied to exactly the same version of the file that the patch was +generated from. +.SH "COMPATIBILITY ISSUES" +The \s-1POSIX\s0 standard specifies behavior that differs from +.BR patch 's +traditional behavior. +You should be aware of these differences if you must interoperate with +.B patch +versions 2.1 and earlier, which are not \s-1POSIX\s0-compliant. +.TP 3 +.B " \(bu" +In traditional +.BR patch , +the +.B \-p +option's operand was optional, and a bare +.B \-p +was equivalent to +.BR \-p0. +The +.B \-p +option now requires an operand, and +.B "\-p\ 0" +is now equivalent to +.BR \-p0 . +For maximum compatibility, use options like +.B \-p0 +and +.BR \-p1 . +.Sp +Also, +traditional +.B patch +simply counted slashes when stripping path prefixes; +.B patch +now counts pathname components. +That is, a sequence of one or more adjacent slashes +now counts as a single slash. +For maximum portability, avoid sending patches containing +.B // +in file names. +.TP +.B " \(bu" +In traditional +.BR patch , +simple backups were enabled by default. +This behavior is now enabled with the +.B \-b +or +.B \*=backup +option, or by setting the +.B VERSION_CONTROL +environment variable to +.BR simple . +.Sp +Conversely, in \s-1POSIX\s0 +.BR patch , +backups are never made, even when there is a mismatch. +In \s-1GNU\s0 +.BR patch , +this behavior is enabled with the +.B \*=no\-backup\-if\-mismatch +option or by setting the +.B POSIXLY_CORRECT +environment variable. +.Sp +The +.BI \-b " suffix" +option +of traditional +.B patch +is equivalent to the +.BI "\-b \-z" " suffix" +options of \s-1GNU\s0 +.BR patch . +.TP +.B " \(bu" +Traditional +.B patch +used a complicated (and incompletely documented) method +to intuit the name of the file to be patched from the patch header. +This method was not \s-1POSIX\s0-compliant, and had a few gotchas. +Now +.B patch +uses a different, equally complicated (but better documented) method +that is optionally \s-1POSIX\s0-compliant; we hope it has +fewer gotchas. The two methods are compatible if the +file names in the context diff header and the +.B Index:\& +line are all identical after prefix-stripping. +Your patch is normally compatible if each header's file names +all contain the same number of slashes. +.TP +.B " \(bu" +When traditional +.B patch +asked the user a question, it sent the question to standard error +and looked for an answer from +the first file in the following list that was a terminal: +standard error, standard output, +.BR /dev/tty , +and standard input. +Now +.B patch +sends questions to standard output and gets answers from +.BR /dev/tty . +Defaults for some answers have been changed so that +.B patch +never goes into an infinite loop when using default answers. +.TP +.B " \(bu" +Traditional +.B patch +exited with a status value that counted the number of bad hunks, +or with status 1 if there was real trouble. +Now +.B patch +exits with status 1 if some hunks failed, +or with 2 if there was real trouble. +.TP +.B " \(bu" +Limit yourself to the following options when sending instructions +meant to be executed by anyone running \s-1GNU\s0 +.BR patch , +traditional +.BR patch , +or a \s-1POSIX\s0-compliant +.BR patch . +Spaces are significant in the following list, and operands are required. +.Sp +.nf +.in +3 +.ne 11 +.B \-c +.BI \-d " dir" +.BI \-D " define" +.B \-e +.B \-l +.B \-n +.B \-N +.BI \-o " outfile" +.BI \-p num +.B \-R +.BI \-r " rejectfile" +.in +.fi +.SH BUGS +.B patch +could be smarter about partial matches, excessively deviant offsets and +swapped code, but that would take an extra pass. +.PP +If code has been duplicated (for instance with +\fB#ifdef OLDCODE\fP .\|.\|. \fB#else .\|.\|. #endif\fP), +.B patch +is incapable of patching both versions, and, if it works at all, will likely +patch the wrong one, and tell you that it succeeded to boot. +.PP +If you apply a patch you've already applied, +.B patch +thinks it is a reversed patch, and offers to un-apply the patch. +This could be construed as a feature. +.SH COPYING +Copyright +.if t \(co +1984, 1985, 1986, 1988 Larry Wall. +.br +Copyright +.if t \(co +1997 Free Software Foundation, Inc. +.PP +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. +.PP +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. +.PP +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the copyright holders instead of in +the original English. +.SH AUTHORS +Larry Wall wrote the original version of +.BR patch . +Paul Eggert removed +.BR patch 's +arbitrary limits; added support for binary files, +setting file times, and deleting files; +and made it conform better to \s-1POSIX\s0. +Other contributors include Wayne Davison, who added unidiff support, +and David MacKenzie, who added configuration and backup support. diff --git a/contrib/patch/patch.c b/contrib/patch/patch.c new file mode 100644 index 0000000..e976204 --- /dev/null +++ b/contrib/patch/patch.c @@ -0,0 +1,1293 @@ +/* patch - a program to apply diffs to original files */ + +/* $Id: patch.c,v 1.22 1997/06/17 22:32:49 eggert Exp $ */ + +/* +Copyright 1984, 1985, 1986, 1987, 1988 Larry Wall +Copyright 1989, 1990, 1991, 1992, 1993, 1997 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; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define XTERN +#include +#undef XTERN +#define XTERN extern +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_UTIME_H +# include +#endif +/* Some nonstandard hosts don't declare this structure even in . */ +#if ! HAVE_STRUCT_UTIMBUF +struct utimbuf +{ + time_t actime; + time_t modtime; +}; +#endif + +/* Output stream state. */ +struct outstate +{ + FILE *ofp; + int after_newline; + int zero_output; +}; + +/* procedures */ + +static FILE *create_output_file PARAMS ((char const *)); +static LINENUM locate_hunk PARAMS ((LINENUM)); +static bool apply_hunk PARAMS ((struct outstate *, LINENUM)); +static bool copy_till PARAMS ((struct outstate *, LINENUM)); +static bool patch_match PARAMS ((LINENUM, LINENUM, LINENUM, LINENUM)); +static bool similar PARAMS ((char const *, size_t, char const *, size_t)); +static bool spew_output PARAMS ((struct outstate *)); +static char const *make_temp PARAMS ((int)); +static int numeric_string PARAMS ((char const *, int, char const *)); +static void abort_hunk PARAMS ((void)); +static void cleanup PARAMS ((void)); +static void get_some_switches PARAMS ((void)); +static void init_output PARAMS ((char const *, struct outstate *)); +static void init_reject PARAMS ((char const *)); +static void reinitialize_almost_everything PARAMS ((void)); +static void usage PARAMS ((FILE *, int)) __attribute__((noreturn)); + +static int backup_if_mismatch; +static int remove_empty_files; + +/* TRUE if -R was specified on command line. */ +static int reverse_flag_specified; + +/* how many input lines have been irretractably output */ +static LINENUM last_frozen_line; + +static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */ +static char const if_defined[] = "\n#ifdef %s\n"; +static char const not_defined[] = "#ifndef %s\n"; +static char const else_defined[] = "\n#else\n"; +static char const end_defined[] = "\n#endif /* %s */\n"; + +static int Argc; +static char * const *Argv; + +static FILE *rejfp; /* reject file pointer */ + +static char const *patchname; +static char *rejname; +static char const * volatile TMPREJNAME; + +static LINENUM last_offset; +static LINENUM maxfuzz = 2; + +static char serrbuf[BUFSIZ]; + +char const program_name[] = "patch"; + +/* Apply a set of diffs as appropriate. */ + +int main PARAMS ((int, char **)); + +int +main(argc,argv) +int argc; +char **argv; +{ + char const *val; + bool somefailed = FALSE; + struct outstate outstate; + + init_time (); + + setbuf(stderr, serrbuf); + + bufsize = 8 * 1024; + buf = xmalloc (bufsize); + + strippath = INT_MAX; + + posixly_correct = getenv ("POSIXLY_CORRECT") != 0; + backup_if_mismatch = ! posixly_correct; + patch_get = ((val = getenv ("PATCH_GET")) + ? numeric_string (val, 1, "PATCH_GET value") + : posixly_correct - 1); + + { + char const *v; + + v = getenv ("SIMPLE_BACKUP_SUFFIX"); + if (v && *v) + simple_backup_suffix = v; + + v = getenv ("PATCH_VERSION_CONTROL"); + if (! v) + v = getenv ("VERSION_CONTROL"); + if (v && *v) + backup_type = get_version (v); + } + + /* Cons up the names of the global temporary files. + Do this before `cleanup' can possibly be called (e.g. by `pfatal'). */ + TMPOUTNAME = make_temp ('o'); + TMPINNAME = make_temp ('i'); + TMPREJNAME = make_temp ('r'); + TMPPATNAME = make_temp ('p'); + + /* parse switches */ + Argc = argc; + Argv = argv; + get_some_switches(); + + init_output (outfile, &outstate); + + /* Make sure we clean up in case of disaster. */ + set_signals(0); + + for ( + open_patch_file (patchname); + there_is_another_patch(); + reinitialize_almost_everything() + ) { /* for each patch in patch file */ + int hunk = 0; + int failed = 0; + int mismatch = 0; + char *outname = outfile ? outfile : inname; + + if (!skip_rest_of_patch) + get_input_file (inname, outname); + + if (diff_type == ED_DIFF) { + outstate.zero_output = 0; + if (! dry_run) + { + do_ed_script (outstate.ofp); + if (! outfile) + { + struct stat statbuf; + if (stat (TMPOUTNAME, &statbuf) != 0) + pfatal ("%s", TMPOUTNAME); + outstate.zero_output = statbuf.st_size == 0; + } + } + } else { + int got_hunk; + int apply_anyway = 0; + + /* initialize the patched file */ + if (! skip_rest_of_patch && ! outfile) + init_output (TMPOUTNAME, &outstate); + + /* initialize reject file */ + init_reject(TMPREJNAME); + + /* find out where all the lines are */ + if (!skip_rest_of_patch) + scan_input (inname); + + /* from here on, open no standard i/o files, because malloc */ + /* might misfire and we can't catch it easily */ + + /* apply each hunk of patch */ + while (0 < (got_hunk = another_hunk (diff_type, reverse))) { + LINENUM where = 0; /* Pacify `gcc -Wall'. */ + LINENUM newwhere; + LINENUM fuzz = 0; + LINENUM prefix_context = pch_prefix_context (); + LINENUM suffix_context = pch_suffix_context (); + LINENUM context = (prefix_context < suffix_context + ? suffix_context : prefix_context); + LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context); + hunk++; + if (!skip_rest_of_patch) { + do { + where = locate_hunk(fuzz); + if (! where || fuzz || last_offset) + mismatch = 1; + if (hunk == 1 && ! where && ! (force | apply_anyway) + && reverse == reverse_flag_specified) { + /* dwim for reversed patch? */ + if (!pch_swap()) { + say ( +"Not enough memory to try swapped hunk! Assuming unswapped.\n"); + continue; + } + /* Try again. */ + where = locate_hunk (fuzz); + if (where + && (ok_to_reverse + ("%s patch detected!", + (reverse + ? "Unreversed" + : "Reversed (or previously applied)")))) + reverse ^= 1; + else + { + /* Put it back to normal. */ + if (! pch_swap ()) + fatal ("lost hunk on alloc error!"); + if (where) + { + apply_anyway = 1; + fuzz--; /* Undo `++fuzz' below. */ + where = 0; + } + } + } + } while (!skip_rest_of_patch && !where + && ++fuzz <= mymaxfuzz); + + if (skip_rest_of_patch) { /* just got decided */ + if (outstate.ofp && ! outfile) + { + fclose (outstate.ofp); + outstate.ofp = 0; + } + } + } + + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { + abort_hunk(); + failed++; + if (verbosity == VERBOSE) + say ("Hunk #%d ignored at %ld.\n", hunk, newwhere); + } + else if (!where + || (where == 1 && pch_says_nonexistent (reverse) + && instat.st_size)) { + if (where) + say ("Patch attempted to create file `%s', which already exists.\n", inname); + abort_hunk(); + failed++; + if (verbosity != SILENT) + say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere); + } + else if (! apply_hunk (&outstate, where)) { + abort_hunk (); + failed++; + if (verbosity != SILENT) + say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere); + } else { + if (verbosity == VERBOSE + || (verbosity != SILENT && (fuzz || last_offset))) { + say ("Hunk #%d succeeded at %ld", hunk, newwhere); + if (fuzz) + say (" with fuzz %ld", fuzz); + if (last_offset) + say (" (offset %ld line%s)", + last_offset, last_offset==1?"":"s"); + say (".\n"); + } + } + } + + if (got_hunk < 0 && using_plan_a) { + if (outfile) + fatal ("out of memory using Plan A"); + say ("\n\nRan out of memory using Plan A -- trying again...\n\n"); + if (outstate.ofp) + { + fclose (outstate.ofp); + outstate.ofp = 0; + } + fclose (rejfp); + continue; + } + + /* finish spewing out the new file */ + if (!skip_rest_of_patch) + { + assert (hunk); + if (! spew_output (&outstate)) + { + say ("Skipping patch.\n"); + skip_rest_of_patch = TRUE; + } + } + } + + /* and put the output where desired */ + ignore_signals (); + if (! skip_rest_of_patch && ! outfile) { + if (outstate.zero_output + && (remove_empty_files + || (pch_says_nonexistent (reverse ^ 1) == 2 + && ! posixly_correct))) + { + if (verbosity == VERBOSE) + say ("Removing file `%s'%s.\n", outname, + dry_run ? " and any empty ancestor directories" : ""); + if (! dry_run) + { + move_file ((char *) 0, outname, (mode_t) 0, + (backup_type != none + || (backup_if_mismatch && (mismatch | failed)))); + removedirs (outname); + } + } + else + { + if (! outstate.zero_output + && pch_says_nonexistent (reverse ^ 1)) + { + mismatch = 1; + if (verbosity != SILENT) + say ("File `%s' is not empty after patch, as expected.\n", + outname); + } + + if (! dry_run) + { + time_t t; + + move_file (TMPOUTNAME, outname, instat.st_mode, + (backup_type != none + || (backup_if_mismatch && (mismatch | failed)))); + + if ((set_time | set_utc) + && (t = pch_timestamp (reverse ^ 1)) != (time_t) -1) + { + struct utimbuf utimbuf; + utimbuf.actime = utimbuf.modtime = t; + + if (! force && ! inerrno + && ! pch_says_nonexistent (reverse) + && (t = pch_timestamp (reverse)) != (time_t) -1 + && t != instat.st_mtime) + say ("not setting time of file `%s' (time mismatch)\n", + outname); + else if (! force && (mismatch | failed)) + say ("not setting time of file `%s' (contents mismatch)\n", + outname); + else if (utime (outname, &utimbuf) != 0) + pfatal ("can't set timestamp on file `%s'", outname); + } + + if (! inerrno && chmod (outname, instat.st_mode) != 0) + pfatal ("can't set permissions on file `%s'", outname); + } + } + } + if (diff_type != ED_DIFF) { + if (fclose (rejfp) != 0) + write_fatal (); + if (failed) { + somefailed = TRUE; + say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1), + skip_rest_of_patch ? "ignored" : "FAILED"); + if (outname) { + char *rej = rejname; + if (!rejname) { + rej = xmalloc (strlen (outname) + 5); + strcpy (rej, outname); + addext (rej, ".rej", '#'); + } + say (" -- saving rejects to %s", rej); + if (! dry_run) + { + move_file (TMPREJNAME, rej, instat.st_mode, FALSE); + if (! inerrno + && (chmod (rej, (instat.st_mode + & ~(S_IXUSR|S_IXGRP|S_IXOTH))) + != 0)) + pfatal ("can't set permissions on file `%s'", rej); + } + if (!rejname) + free (rej); + } + say ("\n"); + } + } + set_signals (1); + } + if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) + write_fatal (); + cleanup (); + if (somefailed) + exit (1); + return 0; +} + +/* Prepare to find the next patch to do in the patch file. */ + +static void +reinitialize_almost_everything() +{ + re_patch(); + re_input(); + + input_lines = 0; + last_frozen_line = 0; + + if (inname) { + free (inname); + inname = 0; + } + + last_offset = 0; + + diff_type = NO_DIFF; + + if (revision) { + free(revision); + revision = 0; + } + + reverse = reverse_flag_specified; + skip_rest_of_patch = FALSE; +} + +static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z"; +static struct option const longopts[] = +{ + {"backup", no_argument, NULL, 'b'}, + {"prefix", required_argument, NULL, 'B'}, + {"context", no_argument, NULL, 'c'}, + {"directory", required_argument, NULL, 'd'}, + {"ifdef", required_argument, NULL, 'D'}, + {"ed", no_argument, NULL, 'e'}, + {"remove-empty-files", no_argument, NULL, 'E'}, + {"force", no_argument, NULL, 'f'}, + {"fuzz", required_argument, NULL, 'F'}, + {"get", no_argument, NULL, 'g'}, + {"input", required_argument, NULL, 'i'}, + {"ignore-whitespace", no_argument, NULL, 'l'}, + {"normal", no_argument, NULL, 'n'}, + {"forward", no_argument, NULL, 'N'}, + {"output", required_argument, NULL, 'o'}, + {"strip", required_argument, NULL, 'p'}, + {"reject-file", required_argument, NULL, 'r'}, + {"reverse", no_argument, NULL, 'R'}, + {"quiet", no_argument, NULL, 's'}, + {"silent", no_argument, NULL, 's'}, + {"batch", no_argument, NULL, 't'}, + {"set-time", no_argument, NULL, 'T'}, + {"unified", no_argument, NULL, 'u'}, + {"version", no_argument, NULL, 'v'}, + {"version-control", required_argument, NULL, 'V'}, + {"debug", required_argument, NULL, 'x'}, + {"basename-prefix", required_argument, NULL, 'Y'}, + {"suffix", required_argument, NULL, 'z'}, + {"set-utc", no_argument, NULL, 'Z'}, + {"dry-run", no_argument, NULL, 129}, + {"verbose", no_argument, NULL, 130}, + {"binary", no_argument, NULL, 131}, + {"help", no_argument, NULL, 132}, + {"backup-if-mismatch", no_argument, NULL, 133}, + {"no-backup-if-mismatch", no_argument, NULL, 134}, + {NULL, no_argument, NULL, 0} +}; + +static char const *const option_help[] = +{ +"Input options:", +"", +" -p NUM --strip=NUM Strip NUM leading components from file names.", +" -F LINES --fuzz LINES Set the fuzz factor to LINES for inexact matching.", +" -l --ignore-whitespace Ignore white space changes between patch and input.", +"", +" -c --context Interpret the patch as a context difference.", +" -e --ed Interpret the patch as an ed script.", +" -n --normal Interpret the patch as a normal difference.", +" -u --unified Interpret the patch as a unified difference.", +"", +" -N --forward Ignore patches that appear to be reversed or already applied.", +" -R --reverse Assume patches were created with old and new files swapped.", +"", +" -i PATCHFILE --input=PATCHFILE Read patch from PATCHFILE instead of stdin.", +"", +"Output options:", +"", +" -o FILE --output=FILE Output patched files to FILE.", +" -r FILE --reject-file=FILE Output rejects to FILE.", +"", +" -D NAME --ifdef=NAME Make merged if-then-else output using NAME.", +" -E --remove-empty-files Remove output files that are empty after patching.", +"", +" -Z --set-utc Set times of patched files, assuming diff uses UTC (GMT).", +" -T --set-time Likewise, assuming local time.", +"", +"Backup and version control options:", +"", +" -V STYLE --version-control=STYLE Use STYLE version control.", +" STYLE is either 'simple', 'numbered', or 'existing'.", +"", +" -b --backup Back up the original contents of each file.", +" --backup-if-mismatch Back up if the patch does not match exactly.", +" --no-backup-if-mismatch Back up mismatches only if otherwise requested.", +" -B PREFIX --prefix=PREFIX Prepend PREFIX to backup file names.", +" -Y PREFIX --basename-prefix=PREFIX Prepend PREFIX to backup file basenames.", +" -z SUFFIX --suffix=SUFFIX Append SUFFIX to backup file names.", +"", +" -g NUM --get=NUM Get files from RCS or SCCS if positive; ask if negative.", +"", +"Miscellaneous options:", +"", +" -t --batch Ask no questions; skip bad-Prereq patches; assume reversed.", +" -f --force Like -t, but ignore bad-Prereq patches, and assume unreversed.", +" -s --quiet --silent Work silently unless an error occurs.", +" --verbose Output extra information about the work being done.", +" --dry-run Do not actually change any files; just print what would happen.", +"", +" -d DIR --directory=DIR Change the working directory to DIR first.", +#if HAVE_SETMODE +" --binary Read and write data in binary mode.", +#else +" --binary Read and write data in binary mode (no effect on this platform).", +#endif +"", +" -v --version Output version info.", +" --help Output this help.", +"", +"Report bugs to .", +0 +}; + +static void +usage (stream, status) + FILE *stream; + int status; +{ + char const * const *p; + + if (status != 0) + { + fprintf (stream, "%s: Try `%s --help' for more information.\n", + program_name, Argv[0]); + } + else + { + fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n", + Argv[0]); + for (p = option_help; *p; p++) + fprintf (stream, "%s\n", *p); + } + + exit (status); +} + +/* Process switches and filenames. */ + +static void +get_some_switches() +{ + register int optc; + + if (rejname) + free (rejname); + rejname = 0; + if (optind == Argc) + return; + while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0)) + != -1) { + switch (optc) { + case 'b': + /* Special hack for backward compatibility with CVS 1.9. + If the last 4 args are `-b SUFFIX ORIGFILE PATCHFILE', + treat `-b' as if it were `-z'. */ + if (Argc - optind == 3 + && strcmp (Argv[optind - 1], "-b") == 0 + && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1]) + && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1]) + && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1])) + { + optarg = Argv[optind++]; + if (verbosity != SILENT) + say ("warning: the `-b %s' option is obsolete; use `-z %s' instead\n", + optarg, optarg); + goto case_z; + } + backup_type = simple; + break; + case 'B': + if (!*optarg) + fatal ("backup prefix is empty"); + origprae = savestr (optarg); + break; + case 'c': + diff_type = CONTEXT_DIFF; + break; + case 'd': + if (chdir(optarg) < 0) + pfatal ("can't change directory to `%s'", optarg); + break; + case 'D': + do_defines = savestr (optarg); + break; + case 'e': + diff_type = ED_DIFF; + break; + case 'E': + remove_empty_files = TRUE; + break; + case 'f': + force = TRUE; + break; + case 'F': + maxfuzz = numeric_string (optarg, 0, "fuzz factor"); + break; + case 'g': + patch_get = numeric_string (optarg, 1, "get option value"); + break; + case 'i': + patchname = savestr (optarg); + break; + case 'l': + canonicalize = TRUE; + break; + case 'n': + diff_type = NORMAL_DIFF; + break; + case 'N': + noreverse = TRUE; + break; + case 'o': + if (strcmp (optarg, "-") == 0) + fatal ("can't output patches to standard output"); + outfile = savestr (optarg); + break; + case 'p': + strippath = numeric_string (optarg, 0, "strip count"); + break; + case 'r': + rejname = savestr (optarg); + break; + case 'R': + reverse = 1; + reverse_flag_specified = 1; + break; + case 's': + verbosity = SILENT; + break; + case 't': + batch = TRUE; + break; + case 'T': + set_time = 1; + break; + case 'u': + diff_type = UNI_DIFF; + break; + case 'v': + version(); + exit (0); + break; + case 'V': + backup_type = get_version (optarg); + break; +#if DEBUGGING + case 'x': + debug = numeric_string (optarg, 1, "debugging option"); + break; +#endif + case 'Y': + if (!*optarg) + fatal ("backup basename prefix is empty"); + origbase = savestr (optarg); + break; + case 'z': + case_z: + if (!*optarg) + fatal ("backup suffix is empty"); + simple_backup_suffix = savestr (optarg); + break; + case 'Z': + set_utc = 1; + break; + case 129: + dry_run = TRUE; + break; + case 130: + verbosity = VERBOSE; + break; + case 131: +#if HAVE_SETMODE + binary_transput = O_BINARY; +#endif + break; + case 132: + usage (stdout, 0); + case 133: + backup_if_mismatch = 1; + break; + case 134: + backup_if_mismatch = 0; + break; + default: + usage (stderr, 2); + } + } + + /* Process any filename args. */ + if (optind < Argc) + { + inname = savestr (Argv[optind++]); + invc = -1; + if (optind < Argc) + { + patchname = savestr (Argv[optind++]); + if (optind < Argc) + { + fprintf (stderr, "%s: extra operand `%s'\n", + program_name, Argv[optind]); + usage (stderr, 2); + } + } + } +} + +/* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero) + of type ARGTYPE_MSGID by converting it to an integer, + returning the result. */ +static int +numeric_string (string, negative_allowed, argtype_msgid) + char const *string; + int negative_allowed; + char const *argtype_msgid; +{ + int value = 0; + char const *p = string; + int sign = *p == '-' ? -1 : 1; + + p += *p == '-' || *p == '+'; + + do + { + int v10 = value * 10; + int digit = *p - '0'; + int signed_digit = sign * digit; + int next_value = v10 + signed_digit; + + if (9 < (unsigned) digit) + fatal ("%s `%s' is not a number", argtype_msgid, string); + + if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0)) + fatal ("%s `%s' is too large", argtype_msgid, string); + + value = next_value; + } + while (*++p); + + if (value < 0 && ! negative_allowed) + fatal ("%s `%s' is negative", argtype_msgid, string); + + return value; +} + +/* Attempt to find the right place to apply this hunk of patch. */ + +static LINENUM +locate_hunk(fuzz) +LINENUM fuzz; +{ + register LINENUM first_guess = pch_first () + last_offset; + register LINENUM offset; + LINENUM pat_lines = pch_ptrn_lines(); + LINENUM prefix_context = pch_prefix_context (); + LINENUM suffix_context = pch_suffix_context (); + LINENUM context = (prefix_context < suffix_context + ? suffix_context : prefix_context); + LINENUM prefix_fuzz = fuzz + prefix_context - context; + LINENUM suffix_fuzz = fuzz + suffix_context - context; + LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1; + LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz); + LINENUM max_pos_offset = max_where - first_guess; + LINENUM max_neg_offset = first_guess - min_where; + LINENUM max_offset = (max_pos_offset < max_neg_offset + ? max_neg_offset : max_pos_offset); + + if (!pat_lines) /* null range matches always */ + return first_guess; + + /* Do not try lines <= 0. */ + if (first_guess <= max_neg_offset) + max_neg_offset = first_guess - 1; + + if (prefix_fuzz < 0) + { + /* Can only match start of file. */ + + if (suffix_fuzz < 0) + /* Can only match entire file. */ + if (pat_lines != input_lines || prefix_context < last_frozen_line) + return 0; + + offset = 1 - first_guess; + return + ((last_frozen_line <= prefix_context + && offset <= max_pos_offset + && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz)) + ? first_guess : 0); + } + + if (suffix_fuzz < 0) + { + /* Can only match end of file. */ + offset = first_guess - (input_lines - pat_lines + 1); + return + ((offset <= max_neg_offset + && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0)) + ? first_guess : 0); + } + + for (offset = 0; offset <= max_offset; offset++) { + if (offset <= max_pos_offset + && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) { + if (debug & 1) + say ("Offset changing from %ld to %ld\n", last_offset, offset); + last_offset = offset; + return first_guess+offset; + } + if (0 < offset && offset <= max_neg_offset + && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) { + if (debug & 1) + say ("Offset changing from %ld to %ld\n", last_offset, -offset); + last_offset = -offset; + return first_guess-offset; + } + } + return 0; +} + +/* We did not find the pattern, dump out the hunk so they can handle it. */ + +static void +abort_hunk() +{ + register LINENUM i; + register LINENUM pat_end = pch_end (); + /* add in last_offset to guess the same as the previous successful hunk */ + LINENUM oldfirst = pch_first() + last_offset; + LINENUM newfirst = pch_newfirst() + last_offset; + LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; + LINENUM newlast = newfirst + pch_repl_lines() - 1; + char const *stars = + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : ""; + char const *minuses = + (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; + + fprintf(rejfp, "***************\n"); + for (i=0; i<=pat_end; i++) { + switch (pch_char(i)) { + case '*': + if (oldlast < oldfirst) + fprintf(rejfp, "*** 0%s\n", stars); + else if (oldlast == oldfirst) + fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); + else + fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars); + break; + case '=': + if (newlast < newfirst) + fprintf(rejfp, "--- 0%s\n", minuses); + else if (newlast == newfirst) + fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); + else + fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses); + break; + case ' ': case '-': case '+': case '!': + fprintf (rejfp, "%c ", pch_char (i)); + /* fall into */ + case '\n': + pch_write_line (i, rejfp); + break; + default: + fatal ("fatal internal error in abort_hunk"); + } + if (ferror (rejfp)) + write_fatal (); + } +} + +/* We found where to apply it (we hope), so do it. */ + +static bool +apply_hunk (outstate, where) + struct outstate *outstate; + LINENUM where; +{ + register LINENUM old = 1; + register LINENUM lastline = pch_ptrn_lines (); + register LINENUM new = lastline+1; + register enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE; + register char const *R_do_defines = do_defines; + register LINENUM pat_end = pch_end (); + register FILE *fp = outstate->ofp; + + where--; + while (pch_char(new) == '=' || pch_char(new) == '\n') + new++; + + while (old <= lastline) { + if (pch_char(old) == '-') { + assert (outstate->after_newline); + if (! copy_till (outstate, where + old - 1)) + return FALSE; + if (R_do_defines) { + if (def_state == OUTSIDE) { + fprintf (fp, outstate->after_newline + if_defined, + R_do_defines); + def_state = IN_IFNDEF; + } + else if (def_state == IN_IFDEF) { + fprintf (fp, outstate->after_newline + else_defined); + def_state = IN_ELSE; + } + if (ferror (fp)) + write_fatal (); + outstate->after_newline = pch_write_line (old, fp); + outstate->zero_output = 0; + } + last_frozen_line++; + old++; + } + else if (new > pat_end) { + break; + } + else if (pch_char(new) == '+') { + if (! copy_till (outstate, where + old - 1)) + return FALSE; + if (R_do_defines) { + if (def_state == IN_IFNDEF) { + fprintf (fp, outstate->after_newline + else_defined); + def_state = IN_ELSE; + } + else if (def_state == OUTSIDE) { + fprintf (fp, outstate->after_newline + if_defined, + R_do_defines); + def_state = IN_IFDEF; + } + if (ferror (fp)) + write_fatal (); + } + outstate->after_newline = pch_write_line (new, fp); + outstate->zero_output = 0; + new++; + } + else if (pch_char(new) != pch_char(old)) { + if (debug & 1) + say ("oldchar = '%c', newchar = '%c'\n", + pch_char (old), pch_char (new)); + fatal ("Out-of-sync patch, lines %ld,%ld -- mangled text or line numbers, maybe?", + pch_hunk_beg() + old, + pch_hunk_beg() + new); + } + else if (pch_char(new) == '!') { + assert (outstate->after_newline); + if (! copy_till (outstate, where + old - 1)) + return FALSE; + assert (outstate->after_newline); + if (R_do_defines) { + fprintf (fp, not_defined, R_do_defines); + if (ferror (fp)) + write_fatal (); + def_state = IN_IFNDEF; + } + + do + { + if (R_do_defines) { + outstate->after_newline = pch_write_line (old, fp); + } + last_frozen_line++; + old++; + } + while (pch_char (old) == '!'); + + if (R_do_defines) { + fprintf (fp, outstate->after_newline + else_defined); + if (ferror (fp)) + write_fatal (); + def_state = IN_ELSE; + } + + do + { + outstate->after_newline = pch_write_line (new, fp); + new++; + } + while (pch_char (new) == '!'); + outstate->zero_output = 0; + } + else { + assert(pch_char(new) == ' '); + old++; + new++; + if (R_do_defines && def_state != OUTSIDE) { + fprintf (fp, outstate->after_newline + end_defined, + R_do_defines); + if (ferror (fp)) + write_fatal (); + outstate->after_newline = 1; + def_state = OUTSIDE; + } + } + } + if (new <= pat_end && pch_char(new) == '+') { + if (! copy_till (outstate, where + old - 1)) + return FALSE; + if (R_do_defines) { + if (def_state == OUTSIDE) { + fprintf (fp, outstate->after_newline + if_defined, + R_do_defines); + def_state = IN_IFDEF; + } + else if (def_state == IN_IFNDEF) { + fprintf (fp, outstate->after_newline + else_defined); + def_state = IN_ELSE; + } + if (ferror (fp)) + write_fatal (); + outstate->zero_output = 0; + } + + do + { + if (! outstate->after_newline && putc ('\n', fp) == EOF) + write_fatal (); + outstate->after_newline = pch_write_line (new, fp); + outstate->zero_output = 0; + new++; + } + while (new <= pat_end && pch_char (new) == '+'); + } + if (R_do_defines && def_state != OUTSIDE) { + fprintf (fp, outstate->after_newline + end_defined, R_do_defines); + if (ferror (fp)) + write_fatal (); + outstate->after_newline = 1; + } + return TRUE; +} + +/* Create an output file. */ + +static FILE * +create_output_file (name) + char const *name; +{ + int fd = create_file (name, O_WRONLY | binary_transput, instat.st_mode); + FILE *f = fdopen (fd, binary_transput ? "wb" : "w"); + if (! f) + pfatal ("can't create `%s'", name); + return f; +} + +/* Open the new file. */ + +static void +init_output (name, outstate) + char const *name; + struct outstate *outstate; +{ + outstate->ofp = name ? create_output_file (name) : (FILE *) 0; + outstate->after_newline = 1; + outstate->zero_output = 1; +} + +/* Open a file to put hunks we can't locate. */ + +static void +init_reject(name) + char const *name; +{ + rejfp = create_output_file (name); +} + +/* Copy input file to output, up to wherever hunk is to be applied. */ + +static bool +copy_till (outstate, lastline) + register struct outstate *outstate; + register LINENUM lastline; +{ + register LINENUM R_last_frozen_line = last_frozen_line; + register FILE *fp = outstate->ofp; + register char const *s; + size_t size; + + if (R_last_frozen_line > lastline) + { + say ("misordered hunks! output would be garbled\n"); + return FALSE; + } + while (R_last_frozen_line < lastline) + { + s = ifetch (++R_last_frozen_line, 0, &size); + if (size) + { + if ((! outstate->after_newline && putc ('\n', fp) == EOF) + || ! fwrite (s, sizeof *s, size, fp)) + write_fatal (); + outstate->after_newline = s[size - 1] == '\n'; + outstate->zero_output = 0; + } + } + last_frozen_line = R_last_frozen_line; + return TRUE; +} + +/* Finish copying the input file to the output file. */ + +static bool +spew_output (outstate) + struct outstate *outstate; +{ + if (debug & 256) + say ("il=%ld lfl=%ld\n", input_lines, last_frozen_line); + + if (last_frozen_line < input_lines) + if (! copy_till (outstate, input_lines)) + return FALSE; + + if (outstate->ofp && ! outfile) + { + if (fclose (outstate->ofp) != 0) + write_fatal (); + outstate->ofp = 0; + } + + return TRUE; +} + +/* Does the patch pattern match at line base+offset? */ + +static bool +patch_match (base, offset, prefix_fuzz, suffix_fuzz) +LINENUM base; +LINENUM offset; +LINENUM prefix_fuzz; +LINENUM suffix_fuzz; +{ + register LINENUM pline = 1 + prefix_fuzz; + register LINENUM iline; + register LINENUM pat_lines = pch_ptrn_lines () - suffix_fuzz; + size_t size; + register char const *p; + + for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) { + p = ifetch (iline, offset >= 0, &size); + if (canonicalize) { + if (!similar(p, size, + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + else if (size != pch_line_len (pline) + || memcmp (p, pfetch (pline), size) != 0) + return FALSE; + } + return TRUE; +} + +/* Do two lines match with canonicalized white space? */ + +static bool +similar (a, alen, b, blen) + register char const *a; + register size_t alen; + register char const *b; + register size_t blen; +{ + /* Ignore presence or absence of trailing newlines. */ + alen -= alen && a[alen - 1] == '\n'; + blen -= blen && b[blen - 1] == '\n'; + + for (;;) + { + if (!blen || (*b == ' ' || *b == '\t')) + { + while (blen && (*b == ' ' || *b == '\t')) + b++, blen--; + if (alen) + { + if (!(*a == ' ' || *a == '\t')) + return FALSE; + do a++, alen--; + while (alen && (*a == ' ' || *a == '\t')); + } + if (!alen || !blen) + return alen == blen; + } + else if (!alen || *a++ != *b++) + return FALSE; + else + alen--, blen--; + } +} + +/* Make a temporary file. */ + +#if HAVE_MKTEMP +char *mktemp PARAMS ((char *)); +#endif + +#ifndef TMPDIR +#define TMPDIR "/tmp" +#endif + +static char const * +make_temp (letter) + int letter; +{ + char *r; +#if HAVE_MKTEMP + char const *tmpdir = getenv ("TMPDIR"); /* Unix tradition */ + if (!tmpdir) tmpdir = getenv ("TMP"); /* DOS tradition */ + if (!tmpdir) tmpdir = getenv ("TEMP"); /* another DOS tradition */ + if (!tmpdir) tmpdir = TMPDIR; + r = xmalloc (strlen (tmpdir) + 10); + sprintf (r, "%s/p%cXXXXXX", tmpdir, letter); + mktemp (r); + if (!*r) + pfatal ("mktemp"); +#else + r = xmalloc (L_tmpnam); + if (! (tmpnam (r) == r && *r)) + pfatal ("tmpnam"); +#endif + return r; +} + +/* Fatal exit with cleanup. */ + +void +fatal_exit (sig) + int sig; +{ + cleanup (); + + if (sig) + exit_with_signal (sig); + + exit (2); +} + +static void +cleanup () +{ + unlink (TMPINNAME); + unlink (TMPOUTNAME); + unlink (TMPPATNAME); + unlink (TMPREJNAME); +} diff --git a/contrib/patch/pch.c b/contrib/patch/pch.c new file mode 100644 index 0000000..64a627a --- /dev/null +++ b/contrib/patch/pch.c @@ -0,0 +1,1823 @@ +/* reading patches */ + +/* $Id: pch.c,v 1.23 1997/06/17 06:52:12 eggert Exp $ */ + +/* +Copyright 1986, 1987, 1988 Larry Wall +Copyright 1990, 1991, 1992, 1993, 1997 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; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define XTERN extern +#include +#include +#include +#include +#undef XTERN +#define XTERN +#include + +#define INITHUNKMAX 125 /* initial dynamic allocation size */ + +/* Patch (diff listing) abstract type. */ + +static FILE *pfp; /* patch file pointer */ +static int p_says_nonexistent[2]; /* [0] for old file, [1] for new; + value is 0 for nonempty, 1 for empty, 2 for nonexistent */ +static time_t p_timestamp[2]; /* timestamps in patch headers */ +static off_t p_filesize; /* size of the patch file */ +static LINENUM p_first; /* 1st line number */ +static LINENUM p_newfirst; /* 1st line number of replacement */ +static LINENUM p_ptrn_lines; /* # lines in pattern */ +static LINENUM p_repl_lines; /* # lines in replacement text */ +static LINENUM p_end = -1; /* last line in hunk */ +static LINENUM p_max; /* max allowed value of p_end */ +static LINENUM p_prefix_context; /* # of prefix context lines */ +static LINENUM p_suffix_context; /* # of suffix context lines */ +static LINENUM p_input_line; /* current line # from patch file */ +static char **p_line; /* the text of the hunk */ +static size_t *p_len; /* line length including \n if any */ +static char *p_Char; /* +, -, and ! */ +static LINENUM hunkmax = INITHUNKMAX; /* size of above arrays */ +static int p_indent; /* indent to patch */ +static file_offset p_base; /* where to intuit this time */ +static LINENUM p_bline; /* line # of p_base */ +static file_offset p_start; /* where intuit found a patch */ +static LINENUM p_sline; /* and the line number for it */ +static LINENUM p_hunk_beg; /* line number of current hunk */ +static LINENUM p_efake = -1; /* end of faked up lines--don't free */ +static LINENUM p_bfake = -1; /* beg of faked up lines */ + +enum nametype { OLD, NEW, INDEX, NONE }; + +static enum diff intuit_diff_type PARAMS ((void)); +static enum nametype best_name PARAMS ((char * const *, int const *)); +static int prefix_components PARAMS ((char *, int)); +static size_t pget_line PARAMS ((int)); +static size_t get_line PARAMS ((void)); +static bool incomplete_line PARAMS ((void)); +static bool grow_hunkmax PARAMS ((void)); +static void malformed PARAMS ((void)); +static void next_intuit_at PARAMS ((file_offset, LINENUM)); +static void skip_to PARAMS ((file_offset, LINENUM)); + +/* Prepare to look for the next patch in the patch file. */ + +void +re_patch() +{ + p_first = 0; + p_newfirst = 0; + p_ptrn_lines = 0; + p_repl_lines = 0; + p_end = -1; + p_max = 0; + p_indent = 0; +} + +/* Open the patch file at the beginning of time. */ + +void +open_patch_file(filename) + char const *filename; +{ + file_offset file_pos = 0; + struct stat st; + if (!filename || !*filename || strEQ (filename, "-")) + { + file_offset stdin_pos; +#if HAVE_SETMODE + if (binary_transput) + { + if (isatty (STDIN_FILENO)) + fatal ("cannot read binary data from tty on this platform"); + setmode (STDIN_FILENO, O_BINARY); + } +#endif + if (fstat (STDIN_FILENO, &st) != 0) + pfatal ("fstat"); + if (S_ISREG (st.st_mode) && (stdin_pos = file_tell (stdin)) != -1) + { + pfp = stdin; + file_pos = stdin_pos; + } + else + { + size_t charsread; + pfp = fopen (TMPPATNAME, "w+b"); + if (!pfp) + pfatal ("can't create `%s'", TMPPATNAME); + for (st.st_size = 0; + (charsread = fread (buf, 1, bufsize, stdin)) != 0; + st.st_size += charsread) + if (fwrite (buf, 1, charsread, pfp) != charsread) + write_fatal (); + if (ferror (stdin) || fclose (stdin) != 0) + read_fatal (); + if (fflush (pfp) != 0 + || file_seek (pfp, (file_offset) 0, SEEK_SET) != 0) + write_fatal (); + } + } + else + { + pfp = fopen (filename, binary_transput ? "rb" : "r"); + if (!pfp) + pfatal ("can't open patch file `%s'", filename); + if (fstat (fileno (pfp), &st) != 0) + pfatal ("fstat"); + } + p_filesize = st.st_size; + next_intuit_at (file_pos, (LINENUM) 1); + set_hunkmax(); +} + +/* Make sure our dynamically realloced tables are malloced to begin with. */ + +void +set_hunkmax() +{ + if (!p_line) + p_line = (char **) malloc (hunkmax * sizeof *p_line); + if (!p_len) + p_len = (size_t *) malloc (hunkmax * sizeof *p_len); + if (!p_Char) + p_Char = malloc (hunkmax * sizeof *p_Char); +} + +/* Enlarge the arrays containing the current hunk of patch. */ + +static bool +grow_hunkmax() +{ + hunkmax *= 2; + assert (p_line && p_len && p_Char); + if ((p_line = (char **) realloc (p_line, hunkmax * sizeof (*p_line))) + && (p_len = (size_t *) realloc (p_len, hunkmax * sizeof (*p_len))) + && (p_Char = realloc (p_Char, hunkmax * sizeof (*p_Char)))) + return TRUE; + if (!using_plan_a) + memory_fatal (); + /* Don't free previous values of p_line etc., + since some broken implementations free them for us. + Whatever is null will be allocated again from within plan_a (), + of all places. */ + return FALSE; +} + +/* True if the remainder of the patch file contains a diff of some sort. */ + +bool +there_is_another_patch() +{ + if (p_base != 0 && p_base >= p_filesize) { + if (verbosity == VERBOSE) + say ("done\n"); + return FALSE; + } + if (verbosity == VERBOSE) + say ("Hmm..."); + diff_type = intuit_diff_type(); + if (diff_type == NO_DIFF) { + if (verbosity == VERBOSE) + say (p_base + ? " Ignoring the trailing garbage.\ndone\n" + : " I can't seem to find a patch in there anywhere.\n"); + return FALSE; + } + if (skip_rest_of_patch) + { + Fseek (pfp, p_start, SEEK_SET); + p_input_line = p_sline - 1; + return TRUE; + } + if (verbosity == VERBOSE) + say (" %sooks like %s to me...\n", + (p_base == 0 ? "L" : "The next patch l"), + diff_type == UNI_DIFF ? "a unified diff" : + diff_type == CONTEXT_DIFF ? "a context diff" : + diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : + diff_type == NORMAL_DIFF ? "a normal diff" : + "an ed script" ); + + if (verbosity != SILENT) + { + if (p_indent) + say ("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); + if (! inname) + say ("can't find file to patch at input line %ld\n", + p_sline); + } + + skip_to(p_start,p_sline); + while (!inname) { + if (force || batch) { + say ("No file to patch. Skipping patch.\n"); + skip_rest_of_patch = TRUE; + return TRUE; + } + ask ("File to patch: "); + inname = fetchname (buf, 0, (time_t *) 0); + if (inname) + { + if (stat (inname, &instat) == 0) + { + inerrno = 0; + invc = -1; + } + else + { + perror (inname); + free (inname); + inname = 0; + } + } + if (!inname) { + ask ("Skip this patch? [y] "); + if (*buf != 'n') { + if (verbosity != SILENT) + say ("Skipping patch.\n"); + skip_rest_of_patch = TRUE; + return TRUE; + } + } + } + return TRUE; +} + +/* Determine what kind of diff is in the remaining part of the patch file. */ + +static enum diff +intuit_diff_type() +{ + register char *s; + register char *t; + register int indent; + register file_offset this_line = 0; + register file_offset previous_line; + register file_offset first_command_line = -1; + LINENUM fcl_line = 0; /* Pacify `gcc -W'. */ + register bool last_line_was_command = FALSE; + register bool this_is_a_command = FALSE; + register bool stars_last_line = FALSE; + register bool stars_this_line = FALSE; + enum nametype i; + char *name[3]; + struct stat st[3]; + int stat_errno[3]; + int version_controlled[3]; + register enum diff retval; + + name[OLD] = name[NEW] = name[INDEX] = 0; + version_controlled[OLD] = -1; + version_controlled[NEW] = -1; + version_controlled[INDEX] = -1; + p_timestamp[OLD] = p_timestamp[NEW] = (time_t) -1; + p_says_nonexistent[OLD] = p_says_nonexistent[NEW] = 0; + Fseek (pfp, p_base, SEEK_SET); + p_input_line = p_bline - 1; + for (;;) { + previous_line = this_line; + last_line_was_command = this_is_a_command; + stars_last_line = stars_this_line; + this_line = file_tell (pfp); + indent = 0; + if (! pget_line (0)) { + if (first_command_line >= 0) { + /* nothing but deletes!? */ + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + else { + p_start = this_line; + p_sline = p_input_line; + retval = NO_DIFF; + goto scan_exit; + } + } + for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { + if (*s == '\t') + indent = (indent + 8) & ~7; + else + indent++; + } + for (t = s; ISDIGIT (*t) || *t == ','; t++) + continue; + this_is_a_command = (ISDIGIT (*s) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (first_command_line < 0 && this_is_a_command) { + first_command_line = this_line; + fcl_line = p_input_line; + p_indent = indent; /* assume this for now */ + } + if (!stars_last_line && strnEQ(s, "*** ", 4)) + name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); + else if (strnEQ(s, "--- ", 4)) + name[NEW] = fetchname (s+4, strippath, &p_timestamp[NEW]); + else if (strnEQ(s, "+++ ", 4)) + /* Swap with NEW below. */ + name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); + else if (strnEQ(s, "Index:", 6)) + name[INDEX] = fetchname (s+6, strippath, (time_t *) 0); + else if (strnEQ(s, "Prereq:", 7)) { + for (t = s + 7; ISSPACE ((unsigned char) *t); t++) + continue; + revision = t; + for (t = revision; *t && !ISSPACE ((unsigned char) *t); t++) + continue; + if (t == revision) + revision = 0; + else { + char oldc = *t; + *t = '\0'; + revision = savestr (revision); + *t = oldc; + } + } + if ((diff_type == NO_DIFF || diff_type == ED_DIFF) && + first_command_line >= 0 && + strEQ(s, ".\n") ) { + p_indent = indent; + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + if ((diff_type == NO_DIFF || diff_type == UNI_DIFF) + && strnEQ(s, "@@ -", 4)) { + + /* `name' and `p_timestamp' are backwards; swap them. */ + time_t ti = p_timestamp[OLD]; + p_timestamp[OLD] = p_timestamp[NEW]; + p_timestamp[NEW] = ti; + t = name[OLD]; + name[OLD] = name[NEW]; + name[NEW] = t; + + s += 4; + if (! atol (s)) + p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; + while (*s != ' ' && *s != '\n') + s++; + while (*s == ' ') + s++; + if (! atol (s)) + p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; + p_indent = indent; + p_start = this_line; + p_sline = p_input_line; + retval = UNI_DIFF; + if (! ((name[OLD] || ! p_timestamp[OLD]) + && (name[NEW] || ! p_timestamp[NEW]))) + say ("missing header for unified diff at line %ld of patch\n", + p_sline); + goto scan_exit; + } + stars_this_line = strnEQ(s, "********", 8); + if ((diff_type == NO_DIFF + || diff_type == CONTEXT_DIFF + || diff_type == NEW_CONTEXT_DIFF) + && stars_last_line && strnEQ (s, "*** ", 4)) { + s += 4; + if (! atol (s)) + p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; + /* if this is a new context diff the character just before */ + /* the newline is a '*'. */ + while (*s != '\n') + s++; + p_indent = indent; + p_start = previous_line; + p_sline = p_input_line - 1; + retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); + + { + /* Scan the first hunk to see whether the file contents + appear to have been deleted. */ + file_offset saved_p_base = p_base; + LINENUM saved_p_bline = p_bline; + Fseek (pfp, previous_line, SEEK_SET); + p_input_line -= 2; + if (another_hunk (retval, 0) + && ! p_repl_lines && p_newfirst == 1) + p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; + next_intuit_at (saved_p_base, saved_p_bline); + } + + if (! ((name[OLD] || ! p_timestamp[OLD]) + && (name[NEW] || ! p_timestamp[NEW]))) + say ("missing header for context diff at line %ld of patch\n", + p_sline); + goto scan_exit; + } + if ((diff_type == NO_DIFF || diff_type == NORMAL_DIFF) && + last_line_was_command && + (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { + p_start = previous_line; + p_sline = p_input_line - 1; + p_indent = indent; + retval = NORMAL_DIFF; + goto scan_exit; + } + } + + scan_exit: + + /* To intuit `inname', the name of the file to patch, + use the algorithm specified by POSIX 1003.2b/D11 section 5.22.7.2 + (with some modifications if posixly_correct is zero): + + - Take the old and new names from the context header if present, + and take the index name from the `Index:' line if present and + if either the old and new names are both absent + or posixly_correct is nonzero. + Consider the file names to be in the order (old, new, index). + - If some named files exist, use the first one if posixly_correct + is nonzero, the best one otherwise. + - If patch_get is nonzero, and no named files exist, + but an RCS or SCCS master file exists, + use the first named file with an RCS or SCCS master. + - If no named files exist, no RCS or SCCS master was found, + some names are given, posixly_correct is zero, + and the patch appears to create a file, then use the best name + requiring the creation of the fewest directories. + - Otherwise, report failure by setting `inname' to 0; + this causes our invoker to ask the user for a file name. */ + + i = NONE; + + if (!inname) + { + enum nametype i0 = NONE; + + if (! posixly_correct && (name[OLD] || name[NEW]) && name[INDEX]) + { + free (name[INDEX]); + name[INDEX] = 0; + } + + for (i = OLD; i <= INDEX; i++) + if (name[i]) + { + if (i0 != NONE && strcmp (name[i0], name[i]) == 0) + { + /* It's the same name as before; reuse stat results. */ + stat_errno[i] = stat_errno[i0]; + if (! stat_errno[i]) + st[i] = st[i0]; + } + else if (stat (name[i], &st[i]) != 0) + stat_errno[i] = errno; + else + { + stat_errno[i] = 0; + if (posixly_correct) + break; + } + i0 = i; + } + + if (! posixly_correct) + { + i = best_name (name, stat_errno); + + if (i == NONE && patch_get) + { + enum nametype nope = NONE; + + for (i = OLD; i <= INDEX; i++) + if (name[i]) + { + char const *cs; + char *getbuf; + char *diffbuf; + int readonly = outfile && strcmp (outfile, name[i]) != 0; + + if (nope == NONE || strcmp (name[nope], name[i]) != 0) + { + cs = (version_controller + (name[i], readonly, (struct stat *) 0, + &getbuf, &diffbuf)); + version_controlled[i] = !! cs; + if (cs) + { + if (version_get (name[i], cs, 0, readonly, + getbuf, &st[i])) + stat_errno[i] = 0; + else + version_controlled[i] = 0; + + free (getbuf); + free (diffbuf); + + if (! stat_errno[i]) + break; + } + } + + nope = i; + } + } + + if (p_says_nonexistent[reverse ^ (i == NONE || st[i].st_size == 0)]) + { + assert (i0 != NONE); + if (ok_to_reverse + ("The next patch%s would %s the file `%s',\nwhich %s!", + reverse ? ", when reversed," : "", + (i == NONE ? "delete" + : st[i].st_size == 0 ? "empty out" + : "create"), + name[i == NONE || st[i].st_size == 0 ? i0 : i], + (i == NONE ? "does not exist" + : st[i].st_size == 0 ? "is already empty" + : "already exists"))) + reverse ^= 1; + } + + if (i == NONE && p_says_nonexistent[reverse]) + { + int newdirs[3]; + int newdirs_min = INT_MAX; + int distance_from_minimum[3]; + + for (i = OLD; i <= INDEX; i++) + if (name[i]) + { + newdirs[i] = (prefix_components (name[i], 0) + - prefix_components (name[i], 1)); + if (newdirs[i] < newdirs_min) + newdirs_min = newdirs[i]; + } + + for (i = OLD; i <= INDEX; i++) + if (name[i]) + distance_from_minimum[i] = newdirs[i] - newdirs_min; + + i = best_name (name, distance_from_minimum); + } + } + } + + if (i == NONE) + inerrno = -1; + else + { + inname = name[i]; + name[i] = 0; + inerrno = stat_errno[i]; + invc = version_controlled[i]; + instat = st[i]; + } + + for (i = OLD; i <= INDEX; i++) + if (name[i]) + free (name[i]); + + return retval; +} + +/* Count the path name components in FILENAME's prefix. + If CHECKDIRS is nonzero, count only existing directories. */ +static int +prefix_components (filename, checkdirs) + char *filename; + int checkdirs; +{ + int count = 0; + struct stat stat_buf; + int stat_result; + char *f = filename + FILESYSTEM_PREFIX_LEN (filename); + + if (*f) + while (*++f) + if (ISSLASH (f[0]) && ! ISSLASH (f[-1])) + { + if (checkdirs) + { + *f = '\0'; + stat_result = stat (filename, &stat_buf); + *f = '/'; + if (! (stat_result == 0 && S_ISDIR (stat_buf.st_mode))) + break; + } + + count++; + } + + return count; +} + +/* Return the index of the best of NAME[OLD], NAME[NEW], and NAME[INDEX]. + Ignore null names, and ignore NAME[i] if IGNORE[i] is nonzero. + Return NONE if all names are ignored. */ +static enum nametype +best_name (name, ignore) + char *const *name; + int const *ignore; +{ + enum nametype i; + int components[3]; + int components_min = INT_MAX; + size_t basename_len[3]; + size_t basename_len_min = (size_t) -1; + size_t len[3]; + size_t len_min = (size_t) -1; + + for (i = OLD; i <= INDEX; i++) + if (name[i] && !ignore[i]) + { + /* Take the names with the fewest prefix components. */ + components[i] = prefix_components (name[i], 0); + if (components_min < components[i]) + continue; + components_min = components[i]; + + /* Of those, take the names with the shortest basename. */ + basename_len[i] = strlen (base_name (name[i])); + if (basename_len_min < basename_len[i]) + continue; + basename_len_min = basename_len[i]; + + /* Of those, take the shortest names. */ + len[i] = strlen (name[i]); + if (len_min < len[i]) + continue; + len_min = len[i]; + } + + /* Of those, take the first name. */ + for (i = OLD; i <= INDEX; i++) + if (name[i] && !ignore[i] + && components[i] == components_min + && basename_len[i] == basename_len_min + && len[i] == len_min) + break; + + return i; +} + +/* Remember where this patch ends so we know where to start up again. */ + +static void +next_intuit_at(file_pos,file_line) +file_offset file_pos; +LINENUM file_line; +{ + p_base = file_pos; + p_bline = file_line; +} + +/* Basically a verbose fseek() to the actual diff listing. */ + +static void +skip_to(file_pos,file_line) +file_offset file_pos; +LINENUM file_line; +{ + register FILE *i = pfp; + register FILE *o = stdout; + register int c; + + assert(p_base <= file_pos); + if ((verbosity == VERBOSE || !inname) && p_base < file_pos) { + Fseek (i, p_base, SEEK_SET); + say ("The text leading up to this was:\n--------------------------\n"); + + while (file_tell (i) < file_pos) + { + putc ('|', o); + do + { + if ((c = getc (i)) == EOF) + read_fatal (); + putc (c, o); + } + while (c != '\n'); + } + + say ("--------------------------\n"); + } + else + Fseek (i, file_pos, SEEK_SET); + p_input_line = file_line - 1; +} + +/* Make this a function for better debugging. */ +static void +malformed () +{ + fatal ("malformed patch at line %ld: %s", p_input_line, buf); + /* about as informative as "Syntax error" in C */ +} + +/* 1 if there is more of the current diff listing to process; + 0 if not; -1 if ran out of memory. */ + +int +another_hunk (difftype, rev) + enum diff difftype; + int rev; +{ + register char *s; + register LINENUM context = 0; + register size_t chars_read; + + while (p_end >= 0) { + if (p_end == p_efake) + p_end = p_bfake; /* don't free twice */ + else + free(p_line[p_end]); + p_end--; + } + assert(p_end == -1); + p_efake = -1; + + p_max = hunkmax; /* gets reduced when --- found */ + if (difftype == CONTEXT_DIFF || difftype == NEW_CONTEXT_DIFF) { + file_offset line_beginning = file_tell (pfp); + /* file pos of the current line */ + LINENUM repl_beginning = 0; /* index of --- line */ + register LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ + register LINENUM fillsrc; /* index of first line to copy */ + register LINENUM filldst; /* index of first missing line */ + bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ + bool some_context = FALSE; /* (perhaps internal) context seen */ + register bool repl_could_be_missing = TRUE; + bool repl_missing = FALSE; /* we are now backtracking */ + file_offset repl_backtrack_position = 0; + /* file pos of first repl line */ + LINENUM repl_patch_line; /* input line number for same */ + LINENUM repl_context; /* context for same */ + LINENUM ptrn_prefix_context = -1; /* lines in pattern prefix context */ + LINENUM ptrn_suffix_context = -1; /* lines in pattern suffix context */ + LINENUM repl_prefix_context = -1; /* lines in replac. prefix context */ + register LINENUM ptrn_copiable = 0; + /* # of copiable lines in ptrn */ + + /* Pacify `gcc -Wall'. */ + fillsrc = filldst = repl_patch_line = repl_context = 0; + + chars_read = get_line (); + if (chars_read == (size_t) -1 + || chars_read <= 8 + || strncmp (buf, "********", 8) != 0) { + next_intuit_at(line_beginning,p_input_line); + return chars_read == (size_t) -1 ? -1 : 0; + } + p_hunk_beg = p_input_line + 1; + while (p_end < p_max) { + chars_read = get_line (); + if (chars_read == (size_t) -1) + return -1; + if (!chars_read) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + if (p_max - p_end < 4) { + strcpy (buf, " \n"); /* assume blank lines got chopped */ + chars_read = 3; + } else { + fatal ("unexpected end of file in patch"); + } + } + p_end++; + if (p_end == hunkmax) + fatal ("unterminated hunk starting at line %ld; giving up at line %ld: %s", + pch_hunk_beg (), p_input_line, buf); + assert(p_end < hunkmax); + p_Char[p_end] = *buf; + p_len[p_end] = 0; + p_line[p_end] = 0; + switch (*buf) { + case '*': + if (strnEQ(buf, "********", 8)) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + else + fatal ("unexpected end of hunk at line %ld", + p_input_line); + } + if (p_end != 0) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal ("unexpected `***' at line %ld: %s", + p_input_line, buf); + } + context = 0; + p_len[p_end] = strlen (buf); + if (! (p_line[p_end] = savestr (buf))) { + p_end--; + return -1; + } + for (s = buf; *s && !ISDIGIT (*s); s++) + continue; + if (!*s) + malformed (); + if (strnEQ(s,"0,0",3)) + remove_prefix (s, 2); + p_first = (LINENUM) atol(s); + while (ISDIGIT (*s)) + s++; + if (*s == ',') { + while (*s && !ISDIGIT (*s)) + s++; + if (!*s) + malformed (); + p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; + } + else if (p_first) + p_ptrn_lines = 1; + else { + p_ptrn_lines = 0; + p_first = 1; + } + p_max = p_ptrn_lines + 6; /* we need this much at least */ + while (p_max >= hunkmax) + if (! grow_hunkmax ()) + return -1; + p_max = hunkmax; + break; + case '-': + if (buf[1] != '-') + goto change_line; + if (ptrn_prefix_context == -1) + ptrn_prefix_context = context; + ptrn_suffix_context = context; + if (repl_beginning + || (p_end + != p_ptrn_lines + 1 + (p_Char[p_end - 1] == '\n'))) + { + if (p_end == 1) + { + /* `Old' lines were omitted. Set up to fill + them in from `new' context lines. */ + p_end = p_ptrn_lines + 1; + ptrn_prefix_context = ptrn_suffix_context = -1; + fillsrc = p_end + 1; + filldst = 1; + fillcnt = p_ptrn_lines; + } + else if (! repl_beginning) + fatal ("%s `---' at line %ld; check line numbers at line %ld", + (p_end <= p_ptrn_lines + ? "Premature" + : "Overdue"), + p_input_line, p_hunk_beg); + else if (! repl_could_be_missing) + fatal ("duplicate `---' at line %ld; check line numbers at line %ld", + p_input_line, p_hunk_beg + repl_beginning); + else + { + repl_missing = TRUE; + goto hunk_done; + } + } + repl_beginning = p_end; + repl_backtrack_position = file_tell (pfp); + repl_patch_line = p_input_line; + repl_context = context; + p_len[p_end] = strlen (buf); + if (! (p_line[p_end] = savestr (buf))) + { + p_end--; + return -1; + } + p_Char[p_end] = '='; + for (s = buf; *s && ! ISDIGIT (*s); s++) + continue; + if (!*s) + malformed (); + p_newfirst = (LINENUM) atol (s); + while (ISDIGIT (*s)) + s++; + if (*s == ',') + { + do + { + if (!*++s) + malformed (); + } + while (! ISDIGIT (*s)); + p_repl_lines = (LINENUM) atol (s) - p_newfirst + 1; + } + else if (p_newfirst) + p_repl_lines = 1; + else + { + p_repl_lines = 0; + p_newfirst = 1; + } + p_max = p_repl_lines + p_end; + while (p_max >= hunkmax) + if (! grow_hunkmax ()) + return -1; + if (p_repl_lines != ptrn_copiable + && (p_prefix_context != 0 + || context != 0 + || p_repl_lines != 1)) + repl_could_be_missing = FALSE; + context = 0; + break; + case '+': case '!': + repl_could_be_missing = FALSE; + change_line: + s = buf + 1; + chars_read--; + if (*s == '\n' && canonicalize) { + strcpy (s, " \n"); + chars_read = 2; + } + if (*s == ' ' || *s == '\t') { + s++; + chars_read--; + } else if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + if (! repl_beginning) + { + if (ptrn_prefix_context == -1) + ptrn_prefix_context = context; + } + else + { + if (repl_prefix_context == -1) + repl_prefix_context = context; + } + chars_read -= + (1 < chars_read + && p_end == (repl_beginning ? p_max : p_ptrn_lines) + && incomplete_line ()); + p_len[p_end] = chars_read; + if (! (p_line[p_end] = savebuf (s, chars_read))) { + p_end--; + return -1; + } + context = 0; + break; + case '\t': case '\n': /* assume spaces got eaten */ + s = buf; + if (*buf == '\t') { + s++; + chars_read--; + } + if (repl_beginning && repl_could_be_missing && + (!ptrn_spaces_eaten || difftype == NEW_CONTEXT_DIFF) ) { + repl_missing = TRUE; + goto hunk_done; + } + chars_read -= + (1 < chars_read + && p_end == (repl_beginning ? p_max : p_ptrn_lines) + && incomplete_line ()); + p_len[p_end] = chars_read; + if (! (p_line[p_end] = savebuf (buf, chars_read))) { + p_end--; + return -1; + } + if (p_end != p_ptrn_lines + 1) { + ptrn_spaces_eaten |= (repl_beginning != 0); + some_context = TRUE; + context++; + if (!repl_beginning) + ptrn_copiable++; + p_Char[p_end] = ' '; + } + break; + case ' ': + s = buf + 1; + chars_read--; + if (*s == '\n' && canonicalize) { + strcpy (s, "\n"); + chars_read = 2; + } + if (*s == ' ' || *s == '\t') { + s++; + chars_read--; + } else if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + some_context = TRUE; + context++; + if (!repl_beginning) + ptrn_copiable++; + chars_read -= + (1 < chars_read + && p_end == (repl_beginning ? p_max : p_ptrn_lines) + && incomplete_line ()); + p_len[p_end] = chars_read; + if (! (p_line[p_end] = savebuf (buf + 2, chars_read))) { + p_end--; + return -1; + } + break; + default: + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + malformed (); + } + } + + hunk_done: + if (p_end >=0 && !repl_beginning) + fatal ("no `---' found in patch at line %ld", pch_hunk_beg ()); + + if (repl_missing) { + + /* reset state back to just after --- */ + p_input_line = repl_patch_line; + context = repl_context; + for (p_end--; p_end > repl_beginning; p_end--) + free(p_line[p_end]); + Fseek (pfp, repl_backtrack_position, SEEK_SET); + + /* redundant 'new' context lines were omitted - set */ + /* up to fill them in from the old file context */ + fillsrc = 1; + filldst = repl_beginning+1; + fillcnt = p_repl_lines; + p_end = p_max; + } + else if (!some_context && fillcnt == 1) { + /* the first hunk was a null hunk with no context */ + /* and we were expecting one line -- fix it up. */ + while (filldst < p_end) { + p_line[filldst] = p_line[filldst+1]; + p_Char[filldst] = p_Char[filldst+1]; + p_len[filldst] = p_len[filldst+1]; + filldst++; + } +#if 0 + repl_beginning--; /* this doesn't need to be fixed */ +#endif + p_end--; + p_first++; /* do append rather than insert */ + fillcnt = 0; + p_ptrn_lines = 0; + } + + p_prefix_context = ((repl_prefix_context == -1 + || (ptrn_prefix_context != -1 + && ptrn_prefix_context < repl_prefix_context)) + ? ptrn_prefix_context : repl_prefix_context); + p_suffix_context = ((ptrn_suffix_context != -1 + && ptrn_suffix_context < context) + ? ptrn_suffix_context : context); + assert (p_prefix_context != -1 && p_suffix_context != -1); + + if (difftype == CONTEXT_DIFF + && (fillcnt + || (p_first > 1 + && p_prefix_context + p_suffix_context < ptrn_copiable))) { + if (verbosity == VERBOSE) + say ("%s\n%s\n%s\n", +"(Fascinating -- this is really a new-style context diff but without", +"the telltale extra asterisks on the *** line that usually indicate", +"the new style...)"); + diff_type = difftype = NEW_CONTEXT_DIFF; + } + + /* if there were omitted context lines, fill them in now */ + if (fillcnt) { + p_bfake = filldst; /* remember where not to free() */ + p_efake = filldst + fillcnt - 1; + while (fillcnt-- > 0) { + while (fillsrc <= p_end && fillsrc != repl_beginning + && p_Char[fillsrc] != ' ') + fillsrc++; + if (p_end < fillsrc || fillsrc == repl_beginning) + fatal ("replacement text or line numbers mangled in hunk at line %ld", + p_hunk_beg); + p_line[filldst] = p_line[fillsrc]; + p_Char[filldst] = p_Char[fillsrc]; + p_len[filldst] = p_len[fillsrc]; + fillsrc++; filldst++; + } + while (fillsrc <= p_end && fillsrc != repl_beginning) + { + if (p_Char[fillsrc] == ' ') + fatal ("replacement text or line numbers mangled in hunk at line %ld", + p_hunk_beg); + fillsrc++; + } + if (debug & 64) + printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", + fillsrc,filldst,repl_beginning,p_end+1); + assert(fillsrc==p_end+1 || fillsrc==repl_beginning); + assert(filldst==p_end+1 || filldst==repl_beginning); + } + } + else if (difftype == UNI_DIFF) { + file_offset line_beginning = file_tell (pfp); + /* file pos of the current line */ + register LINENUM fillsrc; /* index of old lines */ + register LINENUM filldst; /* index of new lines */ + char ch = '\0'; + + chars_read = get_line (); + if (chars_read == (size_t) -1 + || chars_read <= 4 + || strncmp (buf, "@@ -", 4) != 0) { + next_intuit_at(line_beginning,p_input_line); + return chars_read == (size_t) -1 ? -1 : 0; + } + s = buf+4; + if (!*s) + malformed (); + p_first = (LINENUM) atol(s); + while (ISDIGIT (*s)) + s++; + if (*s == ',') { + p_ptrn_lines = (LINENUM) atol(++s); + while (ISDIGIT (*s)) + s++; + } else + p_ptrn_lines = 1; + if (*s == ' ') s++; + if (*s != '+' || !*++s) + malformed (); + p_newfirst = (LINENUM) atol(s); + while (ISDIGIT (*s)) + s++; + if (*s == ',') { + p_repl_lines = (LINENUM) atol(++s); + while (ISDIGIT (*s)) + s++; + } else + p_repl_lines = 1; + if (*s == ' ') s++; + if (*s != '@') + malformed (); + if (!p_ptrn_lines) + p_first++; /* do append rather than insert */ + if (!p_repl_lines) + p_newfirst++; + p_max = p_ptrn_lines + p_repl_lines + 1; + while (p_max >= hunkmax) + if (! grow_hunkmax ()) + return -1; + fillsrc = 1; + filldst = fillsrc + p_ptrn_lines; + p_end = filldst + p_repl_lines; + sprintf (buf,"*** %ld,%ld ****\n",p_first,p_first + p_ptrn_lines - 1); + p_len[0] = strlen (buf); + if (! (p_line[0] = savestr (buf))) { + p_end = -1; + return -1; + } + p_Char[0] = '*'; + sprintf (buf,"--- %ld,%ld ----\n",p_newfirst,p_newfirst+p_repl_lines-1); + p_len[filldst] = strlen (buf); + if (! (p_line[filldst] = savestr (buf))) { + p_end = 0; + return -1; + } + p_Char[filldst++] = '='; + p_prefix_context = -1; + p_hunk_beg = p_input_line + 1; + while (fillsrc <= p_ptrn_lines || filldst <= p_end) { + chars_read = get_line (); + if (!chars_read) { + if (p_max - filldst < 3) { + strcpy (buf, " \n"); /* assume blank lines got chopped */ + chars_read = 2; + } else { + fatal ("unexpected end of file in patch"); + } + } + if (chars_read == (size_t) -1) + s = 0; + else if (*buf == '\t' || *buf == '\n') { + ch = ' '; /* assume the space got eaten */ + s = savebuf (buf, chars_read); + } + else { + ch = *buf; + s = savebuf (buf+1, --chars_read); + } + if (!s) { + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + return -1; + } + switch (ch) { + case '-': + if (fillsrc > p_ptrn_lines) { + free(s); + p_end = filldst-1; + malformed (); + } + chars_read -= fillsrc == p_ptrn_lines && incomplete_line (); + p_Char[fillsrc] = ch; + p_line[fillsrc] = s; + p_len[fillsrc++] = chars_read; + break; + case '=': + ch = ' '; + /* FALL THROUGH */ + case ' ': + if (fillsrc > p_ptrn_lines) { + free(s); + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + malformed (); + } + context++; + chars_read -= fillsrc == p_ptrn_lines && incomplete_line (); + p_Char[fillsrc] = ch; + p_line[fillsrc] = s; + p_len[fillsrc++] = chars_read; + s = savebuf (s, chars_read); + if (!s) { + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + return -1; + } + /* FALL THROUGH */ + case '+': + if (filldst > p_end) { + free(s); + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + malformed (); + } + chars_read -= filldst == p_end && incomplete_line (); + p_Char[filldst] = ch; + p_line[filldst] = s; + p_len[filldst++] = chars_read; + break; + default: + p_end = filldst; + malformed (); + } + if (ch != ' ') { + if (p_prefix_context == -1) + p_prefix_context = context; + context = 0; + } + }/* while */ + if (p_prefix_context == -1) + malformed (); + p_suffix_context = context; + } + else { /* normal diff--fake it up */ + char hunk_type; + register int i; + LINENUM min, max; + file_offset line_beginning = file_tell (pfp); + + p_prefix_context = p_suffix_context = 0; + chars_read = get_line (); + if (chars_read == (size_t) -1 || !chars_read || !ISDIGIT (*buf)) { + next_intuit_at(line_beginning,p_input_line); + return chars_read == (size_t) -1 ? -1 : 0; + } + p_first = (LINENUM)atol(buf); + for (s = buf; ISDIGIT (*s); s++) + continue; + if (*s == ',') { + p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; + while (ISDIGIT (*s)) + s++; + } + else + p_ptrn_lines = (*s != 'a'); + hunk_type = *s; + if (hunk_type == 'a') + p_first++; /* do append rather than insert */ + min = (LINENUM)atol(++s); + while (ISDIGIT (*s)) + s++; + if (*s == ',') + max = (LINENUM)atol(++s); + else + max = min; + if (hunk_type == 'd') + min++; + p_end = p_ptrn_lines + 1 + max - min + 1; + while (p_end >= hunkmax) + if (! grow_hunkmax ()) + { + p_end = -1; + return -1; + } + p_newfirst = min; + p_repl_lines = max - min + 1; + sprintf (buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); + p_len[0] = strlen (buf); + if (! (p_line[0] = savestr (buf))) { + p_end = -1; + return -1; + } + p_Char[0] = '*'; + for (i=1; i<=p_ptrn_lines; i++) { + chars_read = get_line (); + if (chars_read == (size_t) -1) + { + p_end = i - 1; + return -1; + } + if (!chars_read) + fatal ("unexpected end of file in patch at line %ld", + p_input_line); + if (buf[0] != '<' || (buf[1] != ' ' && buf[1] != '\t')) + fatal ("`<' expected at line %ld of patch", p_input_line); + chars_read -= 2 + (i == p_ptrn_lines && incomplete_line ()); + p_len[i] = chars_read; + if (! (p_line[i] = savebuf (buf + 2, chars_read))) { + p_end = i-1; + return -1; + } + p_Char[i] = '-'; + } + if (hunk_type == 'c') { + chars_read = get_line (); + if (chars_read == (size_t) -1) + { + p_end = i - 1; + return -1; + } + if (! chars_read) + fatal ("unexpected end of file in patch at line %ld", + p_input_line); + if (*buf != '-') + fatal ("`---' expected at line %ld of patch", p_input_line); + } + sprintf (buf, "--- %ld,%ld\n", min, max); + p_len[i] = strlen (buf); + if (! (p_line[i] = savestr (buf))) { + p_end = i-1; + return -1; + } + p_Char[i] = '='; + for (i++; i<=p_end; i++) { + chars_read = get_line (); + if (chars_read == (size_t) -1) + { + p_end = i - 1; + return -1; + } + if (!chars_read) + fatal ("unexpected end of file in patch at line %ld", + p_input_line); + if (buf[0] != '>' || (buf[1] != ' ' && buf[1] != '\t')) + fatal ("`>' expected at line %ld of patch", p_input_line); + chars_read -= 2 + (i == p_end && incomplete_line ()); + p_len[i] = chars_read; + if (! (p_line[i] = savebuf (buf + 2, chars_read))) { + p_end = i-1; + return -1; + } + p_Char[i] = '+'; + } + } + if (rev) /* backwards patch? */ + if (!pch_swap()) + say ("Not enough memory to swap next hunk!\n"); + if (debug & 2) { + LINENUM i; + char special; + + for (i=0; i <= p_end; i++) { + if (i == p_ptrn_lines) + special = '^'; + else + special = ' '; + fprintf (stderr, "%3ld %c %c ", i, p_Char[i], special); + pch_write_line (i, stderr); + fflush (stderr); + } + } + if (p_end+1 < hunkmax) /* paranoia reigns supreme... */ + p_Char[p_end+1] = '^'; /* add a stopper for apply_hunk */ + return 1; +} + +static size_t +get_line () +{ + return pget_line (p_indent); +} + +/* Input a line from the patch file, worrying about indentation. + Strip up to INDENT characters' worth of leading indentation. + Ignore any partial lines at end of input, but warn about them. + Succeed if a line was read; it is terminated by "\n\0" for convenience. + Return the number of characters read, including '\n' but not '\0'. + Return -1 if we ran out of memory. */ + +static size_t +pget_line (indent) + int indent; +{ + register FILE *fp = pfp; + register int c; + register int i = 0; + register char *b; + register size_t s; + + for (;;) + { + c = getc (fp); + if (c == EOF) + { + if (ferror (fp)) + read_fatal (); + return 0; + } + if (indent <= i) + break; + if (c == ' ' || c == 'X') + i++; + else if (c == '\t') + i = (i + 8) & ~7; + else + break; + } + + i = 0; + b = buf; + s = bufsize; + + for (;;) + { + if (i == s - 1) + { + s *= 2; + b = realloc (b, s); + if (!b) + { + if (!using_plan_a) + memory_fatal (); + return (size_t) -1; + } + buf = b; + bufsize = s; + } + b[i++] = c; + if (c == '\n') + break; + c = getc (fp); + if (c == EOF) + { + if (ferror (fp)) + read_fatal (); + say ("patch unexpectedly ends in middle of line\n"); + return 0; + } + } + + b[i] = '\0'; + p_input_line++; + return i; +} + +static bool +incomplete_line () +{ + register FILE *fp = pfp; + register int c; + register file_offset line_beginning = file_tell (fp); + + if (getc (fp) == '\\') + { + while ((c = getc (fp)) != '\n' && c != EOF) + continue; + return TRUE; + } + else + { + /* We don't trust ungetc. */ + Fseek (pfp, line_beginning, SEEK_SET); + return FALSE; + } +} + +/* Reverse the old and new portions of the current hunk. */ + +bool +pch_swap() +{ + char **tp_line; /* the text of the hunk */ + size_t *tp_len; /* length of each line */ + char *tp_char; /* +, -, and ! */ + register LINENUM i; + register LINENUM n; + bool blankline = FALSE; + register char *s; + + i = p_first; + p_first = p_newfirst; + p_newfirst = i; + + /* make a scratch copy */ + + tp_line = p_line; + tp_len = p_len; + tp_char = p_Char; + p_line = 0; /* force set_hunkmax to allocate again */ + p_len = 0; + p_Char = 0; + set_hunkmax(); + if (!p_line || !p_len || !p_Char) { + if (p_line) + free (p_line); + p_line = tp_line; + if (p_len) + free (p_len); + p_len = tp_len; + if (p_Char) + free (p_Char); + p_Char = tp_char; + return FALSE; /* not enough memory to swap hunk! */ + } + + /* now turn the new into the old */ + + i = p_ptrn_lines + 1; + if (tp_char[i] == '\n') { /* account for possible blank line */ + blankline = TRUE; + i++; + } + if (p_efake >= 0) { /* fix non-freeable ptr range */ + if (p_efake <= i) + n = p_end - i + 1; + else + n = -i; + p_efake += n; + p_bfake += n; + } + for (n=0; i <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_Char[n] = tp_char[i]; + if (p_Char[n] == '+') + p_Char[n] = '-'; + p_len[n] = tp_len[i]; + } + if (blankline) { + i = p_ptrn_lines + 1; + p_line[n] = tp_line[i]; + p_Char[n] = tp_char[i]; + p_len[n] = tp_len[i]; + n++; + } + assert(p_Char[0] == '='); + p_Char[0] = '*'; + for (s=p_line[0]; *s; s++) + if (*s == '-') + *s = '*'; + + /* now turn the old into the new */ + + assert(tp_char[0] == '*'); + tp_char[0] = '='; + for (s=tp_line[0]; *s; s++) + if (*s == '*') + *s = '-'; + for (i=0; n <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_Char[n] = tp_char[i]; + if (p_Char[n] == '-') + p_Char[n] = '+'; + p_len[n] = tp_len[i]; + } + assert(i == p_ptrn_lines + 1); + i = p_ptrn_lines; + p_ptrn_lines = p_repl_lines; + p_repl_lines = i; + if (tp_line) + free (tp_line); + if (tp_len) + free (tp_len); + if (tp_char) + free (tp_char); + return TRUE; +} + +/* Return whether file WHICH (0 = old, 1 = new) appears to nonexistent. + Return 1 for empty, 2 for nonexistent. */ + +bool +pch_says_nonexistent (which) + int which; +{ + return p_says_nonexistent[which]; +} + +/* Return timestamp of patch header for file WHICH (0 = old, 1 = new), + or -1 if there was no timestamp or an error in the timestamp. */ + +time_t +pch_timestamp (which) + int which; +{ + return p_timestamp[which]; +} + +/* Return the specified line position in the old file of the old context. */ + +LINENUM +pch_first() +{ + return p_first; +} + +/* Return the number of lines of old context. */ + +LINENUM +pch_ptrn_lines() +{ + return p_ptrn_lines; +} + +/* Return the probable line position in the new file of the first line. */ + +LINENUM +pch_newfirst() +{ + return p_newfirst; +} + +/* Return the number of lines in the replacement text including context. */ + +LINENUM +pch_repl_lines() +{ + return p_repl_lines; +} + +/* Return the number of lines in the whole hunk. */ + +LINENUM +pch_end() +{ + return p_end; +} + +/* Return the number of context lines before the first changed line. */ + +LINENUM +pch_prefix_context () +{ + return p_prefix_context; +} + +/* Return the number of context lines after the last changed line. */ + +LINENUM +pch_suffix_context () +{ + return p_suffix_context; +} + +/* Return the length of a particular patch line. */ + +size_t +pch_line_len(line) +LINENUM line; +{ + return p_len[line]; +} + +/* Return the control character (+, -, *, !, etc) for a patch line. */ + +char +pch_char(line) +LINENUM line; +{ + return p_Char[line]; +} + +/* Return a pointer to a particular patch line. */ + +char * +pfetch(line) +LINENUM line; +{ + return p_line[line]; +} + +/* Output a patch line. */ + +bool +pch_write_line (line, file) + LINENUM line; + FILE *file; +{ + bool after_newline = p_line[line][p_len[line] - 1] == '\n'; + if (! fwrite (p_line[line], sizeof (*p_line[line]), p_len[line], file)) + write_fatal (); + return after_newline; +} + +/* Return where in the patch file this hunk began, for error messages. */ + +LINENUM +pch_hunk_beg() +{ + return p_hunk_beg; +} + +/* Apply an ed script by feeding ed itself. */ + +void +do_ed_script (ofp) + FILE *ofp; +{ + static char const ed_program[] = ed_PROGRAM; + + register char *t; + register file_offset beginning_of_this_line; + register bool this_line_is_command = FALSE; + register FILE *pipefp = 0; + register size_t chars_read; + + if (!skip_rest_of_patch) { + assert (! inerrno); + copy_file (inname, TMPOUTNAME, instat.st_mode); + sprintf (buf, "%s %s%s", ed_program, verbosity == VERBOSE ? "" : "- ", + TMPOUTNAME); + fflush (stdout); + pipefp = popen(buf, binary_transput ? "wb" : "w"); + if (!pipefp) + pfatal ("can't open pipe to `%s'", buf); + } + for (;;) { + beginning_of_this_line = file_tell (pfp); + chars_read = get_line (); + if (! chars_read) { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + for (t = buf; ISDIGIT (*t) || *t == ','; t++) + continue; + this_line_is_command = (ISDIGIT (*buf) && + (*t == 'd' || *t == 'c' || *t == 'a' || *t == 'i' || *t == 's') ); + if (this_line_is_command) { + if (pipefp) + if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) + write_fatal (); + if (*t != 'd' && *t != 's') { + while ((chars_read = get_line ()) != 0) { + if (pipefp) + if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) + write_fatal (); + if (chars_read == 2 && strEQ (buf, ".\n")) + break; + } + } + } + else { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + } + if (!pipefp) + return; + if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0 + || fflush (pipefp) != 0) + write_fatal (); + if (pclose (pipefp) != 0) + fatal ("%s FAILED", ed_program); + + if (ofp) + { + FILE *ifp = fopen (TMPOUTNAME, binary_transput ? "rb" : "r"); + int c; + if (!ifp) + pfatal ("can't open `%s'", TMPOUTNAME); + while ((c = getc (ifp)) != EOF) + if (putc (c, ofp) == EOF) + write_fatal (); + if (ferror (ifp) || fclose (ifp) != 0) + read_fatal (); + } +} diff --git a/contrib/patch/pch.h b/contrib/patch/pch.h new file mode 100644 index 0000000..2a4c3c0 --- /dev/null +++ b/contrib/patch/pch.h @@ -0,0 +1,25 @@ +/* reading patches */ + +/* $Id: pch.h,v 1.8 1997/06/13 06:28:37 eggert Exp $ */ + +LINENUM pch_end PARAMS ((void)); +LINENUM pch_first PARAMS ((void)); +LINENUM pch_hunk_beg PARAMS ((void)); +LINENUM pch_newfirst PARAMS ((void)); +LINENUM pch_prefix_context PARAMS ((void)); +LINENUM pch_ptrn_lines PARAMS ((void)); +LINENUM pch_repl_lines PARAMS ((void)); +LINENUM pch_suffix_context PARAMS ((void)); +bool pch_swap PARAMS ((void)); +bool pch_write_line PARAMS ((LINENUM, FILE *)); +bool there_is_another_patch PARAMS ((void)); +char *pfetch PARAMS ((LINENUM)); +char pch_char PARAMS ((LINENUM)); +int another_hunk PARAMS ((enum diff, int)); +int pch_says_nonexistent PARAMS ((int)); +size_t pch_line_len PARAMS ((LINENUM)); +time_t pch_timestamp PARAMS ((int)); +void do_ed_script PARAMS ((FILE *)); +void open_patch_file PARAMS ((char const *)); +void re_patch PARAMS ((void)); +void set_hunkmax PARAMS ((void)); diff --git a/contrib/patch/quotearg.c b/contrib/patch/quotearg.c new file mode 100644 index 0000000..e4926e4 --- /dev/null +++ b/contrib/patch/quotearg.c @@ -0,0 +1,125 @@ +/* Shell command argument quoting. + Copyright (C) 1994, 1995, 1997 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; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Paul Eggert */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +/* Place into QUOTED a quoted version of ARG suitable for `system'. + Return the length of the resulting string (which is not null-terminated). + If QUOTED is null, return the length without any side effects. */ + +size_t +quote_system_arg (quoted, arg) + char *quoted; + char const *arg; +{ + char const *a; + size_t len = 0; + + /* Scan ARG, copying it to QUOTED if QUOTED is not null, + looking for shell metacharacters. */ + + for (a = arg; ; a++) + { + char c = *a; + switch (c) + { + case 0: + /* ARG has no shell metacharacters. */ + return len; + + case '=': + if (*arg == '-') + break; + /* Fall through. */ + case '\t': case '\n': case ' ': + case '!': case '"': case '#': case '$': case '%': case '&': case '\'': + case '(': case ')': case '*': case ';': + case '<': case '>': case '?': case '[': case '\\': + case '^': case '`': case '|': case '~': + { + /* ARG has a shell metacharacter. + Start over, quoting it this time. */ + + len = 0; + c = *arg++; + + /* If ARG is an option, quote just its argument. + This is not necessary, but it looks nicer. */ + if (c == '-' && arg < a) + { + c = *arg++; + + if (quoted) + { + quoted[len] = '-'; + quoted[len + 1] = c; + } + len += 2; + + if (c == '-') + while (arg < a) + { + c = *arg++; + if (quoted) + quoted[len] = c; + len++; + if (c == '=') + break; + } + c = *arg++; + } + + if (quoted) + quoted[len] = '\''; + len++; + + for (; c; c = *arg++) + { + if (c == '\'') + { + if (quoted) + { + quoted[len] = '\''; + quoted[len + 1] = '\\'; + quoted[len + 2] = '\''; + } + len += 3; + } + if (quoted) + quoted[len] = c; + len++; + } + + if (quoted) + quoted[len] = '\''; + return len + 1; + } + } + + if (quoted) + quoted[len] = c; + len++; + } +} diff --git a/contrib/patch/quotearg.h b/contrib/patch/quotearg.h new file mode 100644 index 0000000..f32f919 --- /dev/null +++ b/contrib/patch/quotearg.h @@ -0,0 +1,9 @@ +/* quote.h -- declarations for quoting system arguments */ + +#if defined __STDC__ || __GNUC__ +# define __QUOTEARG_P(args) args +#else +# define __QUOTEARG_P(args) () +#endif + +size_t quote_system_arg __QUOTEARG_P ((char *, char const *)); diff --git a/contrib/patch/rename.c b/contrib/patch/rename.c new file mode 100644 index 0000000..d04a8bc --- /dev/null +++ b/contrib/patch/rename.c @@ -0,0 +1,112 @@ +/* BSD compatible rename and directory rename function for System V. + Copyright (C) 1988, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#ifndef errno +extern int errno; +#endif + +#if STAT_MACROS_BROKEN +# undef S_ISDIR +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* 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, to_stats; + int pid, status; + + if (stat (from, &from_stats)) + return -1; + + /* Be careful not to unlink `from' if it happens to be equal to `to' or + (on filesystems that silently truncate filenames after 14 characters) + if `from' and `to' share the significant characters. */ + if (stat (to, &to_stats)) + { + if (errno != ENOENT) + return -1; + } + else + { + if ((from_stats.st_dev == to_stats.st_dev) + && (from_stats.st_ino == to_stats.st_ino)) + /* `from' and `to' designate the same file on that filesystem. */ + return 0; + + if (unlink (to) && errno != ENOENT) + return -1; + } + +#ifdef MVDIR + +/* If MVDIR is defined, it should be the full filename of a setuid root + program able to link and unlink directories. If MVDIR is not defined, + then the capability of renaming directories may be missing. */ + + if (S_ISDIR (from_stats.st_mode)) + { + /* Need a setuid root process to link and unlink directories. */ + pid = fork (); + switch (pid) + { + case -1: /* Error. */ + error (1, errno, "cannot fork"); + + case 0: /* Child. */ + execl (MVDIR, "mvdir", from, to, (char *) 0); + error (255, errno, "cannot run `%s'", MVDIR); + + default: /* Parent. */ + while (wait (&status) != pid) + /* Do nothing. */ ; + + errno = 0; /* mvdir printed the system error message. */ + if (status) + return -1; + } + } + else + +#endif /* MVDIR */ + + { + if (link (from, to)) + return -1; + if (unlink (from) && errno != ENOENT) + { + unlink (to); + return -1; + } + } + return 0; +} diff --git a/contrib/patch/util.c b/contrib/patch/util.c new file mode 100644 index 0000000..935b325 --- /dev/null +++ b/contrib/patch/util.c @@ -0,0 +1,1070 @@ +/* utility functions for `patch' */ + +/* $Id: util.c,v 1.22 1997/06/13 06:28:37 eggert Exp $ */ + +/* +Copyright 1986 Larry Wall +Copyright 1992, 1993, 1997 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; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define XTERN extern +#include +#include +#include +#include +#undef XTERN +#define XTERN +#include + +#include +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +#define SIGCHLD SIGCLD +#endif +#if ! HAVE_RAISE +# define raise(sig) kill (getpid (), sig) +#endif + +#ifdef __STDC__ +# include +# define vararg_start va_start +#else +# define vararg_start(ap,p) va_start (ap) +# if HAVE_VARARGS_H +# include +# else + typedef char *va_list; +# define va_dcl int va_alist; +# define va_start(ap) ((ap) = (va_list) &va_alist) +# define va_arg(ap, t) (((t *) ((ap) += sizeof (t))) [-1]) +# define va_end(ap) +# endif +#endif + +static void makedirs PARAMS ((char *)); + +/* Move a file FROM to TO, renaming it if possible and copying it if necessary. + If we must create TO, use MODE to create it. + If FROM is null, remove TO (ignoring FROMSTAT). + Back up TO if BACKUP is nonzero. */ + +#ifdef __STDC__ +/* If mode_t doesn't promote to itself, we can't use old-style definition. */ +void +move_file (char const *from, char *to, mode_t mode, int backup) +#else +void +move_file (from, to, mode, backup) + char const *from; + char *to; + mode_t mode; + int backup; +#endif +{ + struct stat to_st; + int to_errno = ! backup ? -1 : stat (to, &to_st) == 0 ? 0 : errno; + + if (backup) + { + int try_makedirs_errno = 0; + char *bakname; + + if (origprae || origbase) + { + char const *p = origprae ? origprae : ""; + char const *b = origbase ? origbase : ""; + char const *o = base_name (to); + size_t plen = strlen (p); + size_t tlen = o - to; + size_t blen = strlen (b); + size_t osize = strlen (o) + 1; + bakname = xmalloc (plen + tlen + blen + osize); + memcpy (bakname, p, plen); + memcpy (bakname + plen, to, tlen); + memcpy (bakname + plen + tlen, b, blen); + memcpy (bakname + plen + tlen + blen, o, osize); + for (p += FILESYSTEM_PREFIX_LEN (p); *p; p++) + if (ISSLASH (*p)) + { + try_makedirs_errno = ENOENT; + break; + } + } + else + { + bakname = find_backup_file_name (to); + if (!bakname) + memory_fatal (); + } + + if (to_errno) + { + int fd; + if (debug & 4) + say ("creating empty unreadable file `%s'\n", bakname); + try_makedirs_errno = ENOENT; + unlink (bakname); + while ((fd = creat (bakname, 0)) < 0) + { + if (errno != try_makedirs_errno) + pfatal ("can't create file `%s'", bakname); + makedirs (bakname); + try_makedirs_errno = 0; + } + if (close (fd) != 0) + pfatal ("can't close `%s'", bakname); + } + else + { + if (debug & 4) + say ("renaming `%s' to `%s'\n", to, bakname); + while (rename (to, bakname) != 0) + { + if (errno != try_makedirs_errno) + pfatal ("can't rename `%s' to `%s'", to, bakname); + makedirs (bakname); + try_makedirs_errno = 0; + } + } + + free (bakname); + } + + if (from) + { + if (debug & 4) + say ("renaming `%s' to `%s'\n", from, to); + + if (rename (from, to) != 0) + { + if (errno == ENOENT + && (to_errno == -1 || to_errno == ENOENT)) + { + makedirs (to); + if (rename (from, to) == 0) + return; + } + +#ifdef EXDEV + if (errno == EXDEV) + { + if (! backup && unlink (to) != 0 && errno != ENOENT) + pfatal ("can't remove `%s'", to); + copy_file (from, to, mode); + return; + } +#endif + pfatal ("can't rename `%s' to `%s'", from, to); + } + } + else if (! backup) + { + if (debug & 4) + say ("removing `%s'\n", to); + if (unlink (to) != 0) + pfatal ("can't remove `%s'", to); + } +} + +/* Create FILE with OPEN_FLAGS, and with MODE adjusted so that + we can read and write the file and that the file is not executable. + Return the file descriptor. */ +#ifdef __STDC__ +/* If mode_t doesn't promote to itself, we can't use old-style definition. */ +int +create_file (char const *file, int open_flags, mode_t mode) +#else +int +create_file (file, open_flags, mode) + char const *file; + int open_flags; + mode_t mode; +#endif +{ + int fd; + mode |= S_IRUSR | S_IWUSR; + mode &= ~ (S_IXUSR | S_IXGRP | S_IXOTH); + if (! (O_CREAT && O_TRUNC)) + close (creat (file, mode)); + fd = open (file, O_CREAT | O_TRUNC | open_flags, mode); + if (fd < 0) + pfatal ("can't create `%s'", file); + return fd; +} + +/* Copy a file. */ + +#ifdef __STDC__ +/* If mode_t doesn't promote to itself, we can't use old-style definition. */ +void +copy_file (char const *from, char const *to, mode_t mode) +#else +void +copy_file (from, to, mode) + char const *from; + char const *to; + mode_t mode; +#endif +{ + int tofd; + int fromfd; + size_t i; + + if ((fromfd = open (from, O_RDONLY | O_BINARY)) < 0) + pfatal ("can't reopen `%s'", from); + tofd = create_file (to, O_WRONLY | O_BINARY, mode); + while ((i = read (fromfd, buf, bufsize)) != 0) + { + if (i == -1) + read_fatal (); + if (write (tofd, buf, i) != i) + write_fatal (); + } + if (close (fromfd) != 0) + read_fatal (); + if (close (tofd) != 0) + write_fatal (); +} + +static char const DEV_NULL[] = NULL_DEVICE; + +static char const SCCSPREFIX[] = "s."; +static char const GET[] = "get "; +static char const GET_LOCKED[] = "get -e "; +static char const SCCSDIFF1[] = "get -p "; +static char const SCCSDIFF2[] = "|diff - %s"; + +static char const RCSSUFFIX[] = ",v"; +static char const CHECKOUT[] = "co %s"; +static char const CHECKOUT_LOCKED[] = "co -l %s"; +static char const RCSDIFF1[] = "rcsdiff %s"; + +/* Return "RCS" if FILENAME is controlled by RCS, + "SCCS" if it is controlled by SCCS, and 0 otherwise. + READONLY is nonzero if we desire only readonly access to FILENAME. + FILESTAT describes FILENAME's status or is 0 if FILENAME does not exist. + If successful and if GETBUF is nonzero, set *GETBUF to a command + that gets the file; similarly for DIFFBUF and a command to diff the file. + *GETBUF and *DIFFBUF must be freed by the caller. */ +char const * +version_controller (filename, readonly, filestat, getbuf, diffbuf) + char const *filename; + int readonly; + struct stat const *filestat; + char **getbuf; + char **diffbuf; +{ + struct stat cstat; + char const *filebase = base_name (filename); + char const *dotslash = *filename == '-' ? "./" : ""; + size_t dir_len = filebase - filename; + size_t filenamelen = strlen (filename); + size_t maxfixlen = sizeof "SCCS/" - 1 + sizeof SCCSPREFIX - 1; + size_t maxtrysize = filenamelen + maxfixlen + 1; + size_t quotelen = quote_system_arg (0, filename); + size_t maxgetsize = sizeof GET_LOCKED + quotelen + maxfixlen; + size_t maxdiffsize = + (sizeof SCCSDIFF1 + sizeof SCCSDIFF2 + sizeof DEV_NULL - 1 + + 2 * quotelen + maxfixlen); + char *trybuf = xmalloc (maxtrysize); + char const *r = 0; + + strcpy (trybuf, filename); + +#define try1(f,a1) (sprintf (trybuf + dir_len, f, a1), stat (trybuf, &cstat) == 0) +#define try2(f,a1,a2) (sprintf (trybuf + dir_len, f, a1,a2), stat (trybuf, &cstat) == 0) + + /* Check that RCS file is not working file. + Some hosts don't report file name length errors. */ + + if ((try2 ("RCS/%s%s", filebase, RCSSUFFIX) + || try1 ("RCS/%s", filebase) + || try2 ("%s%s", filebase, RCSSUFFIX)) + && ! (filestat + && filestat->st_dev == cstat.st_dev + && filestat->st_ino == cstat.st_ino)) + { + if (getbuf) + { + char *p = *getbuf = xmalloc (maxgetsize); + sprintf (p, readonly ? CHECKOUT : CHECKOUT_LOCKED, dotslash); + p += strlen (p); + p += quote_system_arg (p, filename); + *p = '\0'; + } + + if (diffbuf) + { + char *p = *diffbuf = xmalloc (maxdiffsize); + sprintf (p, RCSDIFF1, dotslash); + p += strlen (p); + p += quote_system_arg (p, filename); + *p++ = '>'; + strcpy (p, DEV_NULL); + } + + r = "RCS"; + } + else if (try2 ("SCCS/%s%s", SCCSPREFIX, filebase) + || try2 ("%s%s", SCCSPREFIX, filebase)) + { + if (getbuf) + { + char *p = *getbuf = xmalloc (maxgetsize); + sprintf (p, readonly ? GET : GET_LOCKED); + p += strlen (p); + p += quote_system_arg (p, trybuf); + *p = '\0'; + } + + if (diffbuf) + { + char *p = *diffbuf = xmalloc (maxdiffsize); + strcpy (p, SCCSDIFF1); + p += sizeof SCCSDIFF1 - 1; + p += quote_system_arg (p, trybuf); + sprintf (p, SCCSDIFF2, dotslash); + p += strlen (p); + p += quote_system_arg (p, filename); + *p++ = '>'; + strcpy (p, DEV_NULL); + } + + r = "SCCS"; + } + + free (trybuf); + return r; +} + +/* Get FILENAME from version control system CS. The file already exists if + EXISTS is nonzero. Only readonly access is needed if READONLY is nonzero. + Use the command GETBUF to actually get the named file. + Store the resulting file status into *FILESTAT. + Return nonzero if successful. */ +int +version_get (filename, cs, exists, readonly, getbuf, filestat) + char const *filename; + char const *cs; + int exists; + int readonly; + char const *getbuf; + struct stat *filestat; +{ + if (patch_get < 0) + { + ask ("Get file `%s' from %s%s? [y] ", filename, + cs, readonly ? "" : " with lock"); + if (*buf == 'n') + return 0; + } + + if (dry_run) + { + if (! exists) + fatal ("can't do dry run on nonexistent version-controlled file `%s'; invoke `%s' and try again", + filename, getbuf); + } + else + { + if (verbosity == VERBOSE) + say ("Getting file `%s' from %s%s...\n", filename, + cs, readonly ? "" : " with lock"); + if (systemic (getbuf) != 0) + fatal ("can't get file `%s' from %s", filename, cs); + if (stat (filename, filestat) != 0) + pfatal ("%s", filename); + } + + return 1; +} + +/* Allocate a unique area for a string. */ + +char * +savebuf (s, size) + register char const *s; + register size_t size; +{ + register char *rv; + + assert (s && size); + rv = malloc (size); + + if (! rv) + { + if (! using_plan_a) + memory_fatal (); + } + else + memcpy (rv, s, size); + + return rv; +} + +char * +savestr(s) + char const *s; +{ + return savebuf (s, strlen (s) + 1); +} + +void +remove_prefix (p, prefixlen) + char *p; + size_t prefixlen; +{ + char const *s = p + prefixlen; + while ((*p++ = *s++)) + continue; +} + +#if !HAVE_VPRINTF +#define vfprintf my_vfprintf +static int vfprintf PARAMS ((FILE *, char const *, va_list)); +static int +vfprintf (stream, format, args) + FILE *stream; + char const *format; + va_list args; +{ +#if !HAVE_DOPRNT && HAVE__DOPRINTF +# define _doprnt _doprintf +#endif +#if HAVE_DOPRNT || HAVE__DOPRINTF + _doprnt (format, args, stream); + return ferror (stream) ? -1 : 0; +#else + int *a = (int *) args; + return fprintf (stream, format, + a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); +#endif +} +#endif /* !HAVE_VPRINTF */ + +/* Terminal output, pun intended. */ + +#ifdef __STDC__ +void +fatal (char const *format, ...) +#else +/*VARARGS1*/ void +fatal (format, va_alist) + char const *format; + va_dcl +#endif +{ + va_list args; + fprintf (stderr, "%s: **** ", program_name); + vararg_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + putc ('\n', stderr); + fflush (stderr); + fatal_exit (0); +} + +void +memory_fatal () +{ + fatal ("out of memory"); +} + +void +read_fatal () +{ + pfatal ("read error"); +} + +void +write_fatal () +{ + pfatal ("write error"); +} + +/* Say something from patch, something from the system, then silence . . . */ + +#ifdef __STDC__ +void +pfatal (char const *format, ...) +#else +/*VARARGS1*/ void +pfatal (format, va_alist) + char const *format; + va_dcl +#endif +{ + int errnum = errno; + va_list args; + fprintf (stderr, "%s: **** ", program_name); + vararg_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + fflush (stderr); /* perror bypasses stdio on some hosts. */ + errno = errnum; + perror (" "); + fflush (stderr); + fatal_exit (0); +} + +/* Tell the user something. */ + +#ifdef __STDC__ +void +say (char const *format, ...) +#else +/*VARARGS1*/ void +say (format, va_alist) + char const *format; + va_dcl +#endif +{ + va_list args; + vararg_start (args, format); + vfprintf (stdout, format, args); + va_end (args); + fflush (stdout); +} + +/* Get a response from the user, somehow or other. */ + +#ifdef __STDC__ +void +ask (char const *format, ...) +#else +/*VARARGS1*/ void +ask (format, va_alist) + char const *format; + va_dcl +#endif +{ + static int ttyfd = -2; + int r; + va_list args; + + vararg_start (args, format); + vfprintf (stdout, format, args); + va_end (args); + fflush (stdout); + + if (ttyfd == -2) + { + /* If standard output is not a tty, don't bother opening /dev/tty, + since it's unlikely that stdout will be seen by the tty user. + The isatty test also works around a bug in GNU Emacs 19.34 under Linux + which makes a call-process `patch' hang when it reads from /dev/tty. + POSIX.2 requires that we read /dev/tty, though. */ + ttyfd = (posixly_correct || isatty (STDOUT_FILENO) + ? open (TTY_DEVICE, O_RDONLY) + : -1); + } + + if (ttyfd < 0) + { + /* No terminal at all -- default it. */ + printf ("\n"); + buf[0] = '\n'; + buf[1] = '\0'; + } + else + { + size_t s = 0; + while ((r = read (ttyfd, buf + s, bufsize - 1 - s)) == bufsize - 1 - s + && buf[bufsize - 2] != '\n') + { + s = bufsize - 1; + bufsize *= 2; + buf = realloc (buf, bufsize); + if (!buf) + memory_fatal (); + } + if (r == 0) + printf ("EOF\n"); + else if (r < 0) + { + perror ("tty read"); + fflush (stderr); + close (ttyfd); + ttyfd = -1; + r = 0; + } + buf[s + r] = '\0'; + } +} + +/* Return nonzero if it OK to reverse a patch. */ + +#ifdef __STDC__ +int +ok_to_reverse (char const *format, ...) +#else +ok_to_reverse (format, va_alist) + char const *format; + va_dcl +#endif +{ + int r = 0; + + if (noreverse || ! (force && verbosity == SILENT)) + { + va_list args; + vararg_start (args, format); + vfprintf (stdout, format, args); + va_end (args); + } + + if (noreverse) + { + printf (" Skipping patch.\n"); + skip_rest_of_patch = TRUE; + r = 0; + } + else if (force) + { + if (verbosity != SILENT) + printf (" Applying it anyway.\n"); + r = 0; + } + else if (batch) + { + say (reverse ? " Ignoring -R.\n" : " Assuming -R.\n"); + r = 1; + } + else + { + ask (reverse ? " Ignore -R? [y] " : " Assume -R? [y] "); + r = *buf != 'n'; + if (! r) + { + ask ("Apply anyway? [n] "); + if (*buf != 'y') + { + if (verbosity != SILENT) + say ("Skipping patch.\n"); + skip_rest_of_patch = TRUE; + } + } + } + + return r; +} + +/* How to handle certain events when not in a critical region. */ + +#define NUM_SIGS (sizeof (sigs) / sizeof (*sigs)) +static int const sigs[] = { +#ifdef SIGHUP + SIGHUP, +#endif +#ifdef SIGPIPE + SIGPIPE, +#endif +#ifdef SIGTERM + SIGTERM, +#endif +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif + SIGINT +}; + +#if !HAVE_SIGPROCMASK +#define sigset_t int +#define sigemptyset(s) (*(s) = 0) +#ifndef sigmask +#define sigmask(sig) (1 << ((sig) - 1)) +#endif +#define sigaddset(s, sig) (*(s) |= sigmask (sig)) +#define sigismember(s, sig) ((*(s) & sigmask (sig)) != 0) +#ifndef SIG_BLOCK +#define SIG_BLOCK 0 +#endif +#ifndef SIG_UNBLOCK +#define SIG_UNBLOCK (SIG_BLOCK + 1) +#endif +#ifndef SIG_SETMASK +#define SIG_SETMASK (SIG_BLOCK + 2) +#endif +#define sigprocmask(how, n, o) \ + ((how) == SIG_BLOCK \ + ? ((o) ? *(o) = sigblock (*(n)) : sigblock (*(n))) \ + : (how) == SIG_UNBLOCK \ + ? sigsetmask (((o) ? *(o) = sigblock (0) : sigblock (0)) & ~*(n)) \ + : (o ? *(o) = sigsetmask (*(n)) : sigsetmask (*(n)))) +#if !HAVE_SIGSETMASK +#define sigblock(mask) 0 +#define sigsetmask(mask) 0 +#endif +#endif + +static sigset_t initial_signal_mask; +static sigset_t signals_to_block; + +#if ! HAVE_SIGACTION +static RETSIGTYPE fatal_exit_handler PARAMS ((int)) __attribute__ ((noreturn)); +static RETSIGTYPE +fatal_exit_handler (sig) + int sig; +{ + signal (sig, SIG_IGN); + fatal_exit (sig); +} +#endif + +void +set_signals(reset) +int reset; +{ + int i; +#if HAVE_SIGACTION + struct sigaction initial_act, fatal_act; + fatal_act.sa_handler = fatal_exit; + sigemptyset (&fatal_act.sa_mask); + fatal_act.sa_flags = 0; +#define setup_handler(sig) sigaction (sig, &fatal_act, (struct sigaction *) 0) +#else +#define setup_handler(sig) signal (sig, fatal_exit_handler) +#endif + + if (!reset) + { +#ifdef SIGCHLD + /* System V fork+wait does not work if SIGCHLD is ignored. */ + signal (SIGCHLD, SIG_DFL); +#endif + sigemptyset (&signals_to_block); + for (i = 0; i < NUM_SIGS; i++) + { + int ignoring_signal; +#if HAVE_SIGACTION + if (sigaction (sigs[i], (struct sigaction *) 0, &initial_act) != 0) + continue; + ignoring_signal = initial_act.sa_handler == SIG_IGN; +#else + ignoring_signal = signal (sigs[i], SIG_IGN) == SIG_IGN; +#endif + if (! ignoring_signal) + { + sigaddset (&signals_to_block, sigs[i]); + setup_handler (sigs[i]); + } + } + } + else + { + /* Undo the effect of ignore_signals. */ +#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK + sigprocmask (SIG_SETMASK, &initial_signal_mask, (sigset_t *) 0); +#else + for (i = 0; i < NUM_SIGS; i++) + if (sigismember (&signals_to_block, sigs[i])) + setup_handler (sigs[i]); +#endif + } +} + +/* How to handle certain events when in a critical region. */ + +void +ignore_signals() +{ +#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK + sigprocmask (SIG_BLOCK, &signals_to_block, &initial_signal_mask); +#else + int i; + for (i = 0; i < NUM_SIGS; i++) + if (sigismember (&signals_to_block, sigs[i])) + signal (sigs[i], SIG_IGN); +#endif +} + +void +exit_with_signal (sig) + int sig; +{ + sigset_t s; + signal (sig, SIG_DFL); + sigemptyset (&s); + sigaddset (&s, sig); + sigprocmask (SIG_UNBLOCK, &s, (sigset_t *) 0); + raise (sig); + exit (2); +} + +int +systemic (command) + char const *command; +{ + if (debug & 8) + say ("+ %s\n", command); + fflush (stdout); + return system (command); +} + +#if !HAVE_MKDIR +/* These mkdir and rmdir substitutes are good enough for `patch'; + they are not general emulators. */ + +static int doprogram PARAMS ((char const *, char const *)); +static int mkdir PARAMS ((char const *, mode_t)); +static int rmdir PARAMS ((char const *)); + +static int +doprogram (program, arg) + char const *program; + char const *arg; +{ + int result; + static char const DISCARD_OUTPUT[] = " 2>/dev/null"; + size_t program_len = strlen (program); + char *cmd = xmalloc (program_len + 1 + quote_system_arg (0, arg) + + sizeof DISCARD_OUTPUT); + char *p = cmd; + strcpy (p, program); + p += program_len; + *p++ = ' '; + p += quote_system_arg (p, arg); + strcpy (p, DISCARD_OUTPUT); + result = systemic (cmd); + free (cmd); + return result; +} + +#ifdef __STDC__ +/* If mode_t doesn't promote to itself, we can't use old-style definition. */ +static int +mkdir (char const *path, mode_t mode) +#else +static int +mkdir (path, mode) + char const *path; + mode_t mode; /* ignored */ +#endif +{ + return doprogram ("mkdir", path); +} + +static int +rmdir (path) + char const *path; +{ + int result = doprogram ("rmdir", path); + errno = EEXIST; + return result; +} +#endif + +/* Replace '/' with '\0' in FILENAME if it marks a place that + needs testing for the existence of directory. Return the address + of the last location replaced, or 0 if none were replaced. */ +static char *replace_slashes PARAMS ((char *)); +static char * +replace_slashes (filename) + char *filename; +{ + char *f; + char *last_location_replaced = 0; + char const *component_start; + + for (f = filename + FILESYSTEM_PREFIX_LEN (filename); ISSLASH (*f); f++) + continue; + + component_start = f; + + for (; *f; f++) + if (ISSLASH (*f)) + { + char *slash = f; + + /* Treat multiple slashes as if they were one slash. */ + while (ISSLASH (f[1])) + f++; + + /* Ignore slashes at the end of the path. */ + if (! f[1]) + break; + + /* "." and ".." need not be tested. */ + if (! (slash - component_start <= 2 + && component_start[0] == '.' && slash[-1] == '.')) + { + *slash = '\0'; + last_location_replaced = slash; + } + + component_start = f + 1; + } + + return last_location_replaced; +} + +/* Make sure we'll have the directories to create a file. + Ignore the last element of `filename'. */ + +static void +makedirs (filename) + register char *filename; +{ + register char *f; + register char *flim = replace_slashes (filename); + + if (flim) + { + /* Create any missing directories, replacing NULs by '/'s. + Ignore errors. We may have to keep going even after an EEXIST, + since the path may contain ".."s; and when there is an EEXIST + failure the system may return some other error number. + Any problems will eventually be reported when we create the file. */ + for (f = filename; f <= flim; f++) + if (!*f) + { + mkdir (filename, + S_IRUSR|S_IWUSR|S_IXUSR + |S_IRGRP|S_IWGRP|S_IXGRP + |S_IROTH|S_IWOTH|S_IXOTH); + *f = '/'; + } + } +} + +/* Remove empty ancestor directories of FILENAME. + Ignore errors, since the path may contain ".."s, and when there + is an EEXIST failure the system may return some other error number. */ +void +removedirs (filename) + char *filename; +{ + size_t i; + + for (i = strlen (filename); i != 0; i--) + if (ISSLASH (filename[i]) + && ! (ISSLASH (filename[i - 1]) + || (filename[i - 1] == '.' + && (i == 1 + || ISSLASH (filename[i - 2]) + || (filename[i - 2] == '.' + && (i == 2 + || ISSLASH (filename[i - 3]))))))) + { + filename[i] = '\0'; + if (rmdir (filename) == 0 && verbosity == VERBOSE) + say ("Removed empty directory `%s'.\n", filename); + filename[i] = '/'; + } +} + +static time_t initial_time; + +void +init_time () +{ + time (&initial_time); +} + +/* Make filenames more reasonable. */ + +char * +fetchname (at, strip_leading, pstamp) +char *at; +int strip_leading; +time_t *pstamp; +{ + char *name; + register char *t; + int sleading = strip_leading; + time_t stamp = (time_t) -1; + + while (ISSPACE ((unsigned char) *at)) + at++; + if (debug & 128) + say ("fetchname %s %d\n", at, strip_leading); + + name = at; + /* Strip off up to `sleading' leading slashes and null terminate. */ + for (t = at; *t; t++) + { + if (ISSLASH (*t)) + { + while (ISSLASH (t[1])) + t++; + if (--sleading >= 0) + name = t+1; + } + else if (ISSPACE ((unsigned char) *t)) + { + if (set_time | set_utc) + stamp = str2time (t, initial_time, set_utc ? 0L : TM_LOCAL_ZONE); + else + { + /* The head says the file is nonexistent if the timestamp + is the epoch; but the listed time is local time, not UTC, + and POSIX.1 allows local time to be 24 hours away from UTC. + So match any time within 24 hours of the epoch. + Use a default time zone 24 hours behind UTC so that any + non-zoned time within 24 hours of the epoch is valid. */ + stamp = str2time (t, initial_time, -24L * 60 * 60); + if (0 <= stamp && stamp <= 2 * 24L * 60 * 60) + stamp = 0; + } + + *t = '\0'; + break; + } + } + + if (!*name) + return 0; + + /* Allow files to be created by diffing against /dev/null. */ + if (strcmp (at, "/dev/null") == 0) + { + if (pstamp) + *pstamp = 0; + return 0; + } + + if (pstamp) + *pstamp = stamp; + + return savestr (name); +} + +GENERIC_OBJECT * +xmalloc (size) + size_t size; +{ + register GENERIC_OBJECT *p = malloc (size); + if (!p) + memory_fatal (); + return p; +} + +void +Fseek (stream, offset, ptrname) + FILE *stream; + file_offset offset; + int ptrname; +{ + if (file_seek (stream, offset, ptrname) != 0) + pfatal ("fseek"); +} diff --git a/contrib/patch/util.h b/contrib/patch/util.h new file mode 100644 index 0000000..f7fda0a --- /dev/null +++ b/contrib/patch/util.h @@ -0,0 +1,32 @@ +/* utility functions for `patch' */ + +/* $Id: util.h,v 1.14 1997/06/13 06:28:37 eggert Exp $ */ + +int ok_to_reverse PARAMS ((char const *, ...)) __attribute__ ((format (printf, 1, 2))); +void ask PARAMS ((char const *, ...)) __attribute__ ((format (printf, 1, 2))); +void say PARAMS ((char const *, ...)) __attribute__ ((format (printf, 1, 2))); + +void fatal PARAMS ((char const *, ...)) + __attribute__ ((noreturn, format (printf, 1, 2))); +void pfatal PARAMS ((char const *, ...)) + __attribute__ ((noreturn, format (printf, 1, 2))); + +char *fetchname PARAMS ((char *, int, time_t *)); +char *savebuf PARAMS ((char const *, size_t)); +char *savestr PARAMS ((char const *)); +char const *version_controller PARAMS ((char const *, int, struct stat const *, char **, char **)); +int version_get PARAMS ((char const *, char const *, int, int, char const *, struct stat *)); +int create_file PARAMS ((char const *, int, mode_t)); +int systemic PARAMS ((char const *)); +void Fseek PARAMS ((FILE *, file_offset, int)); +void copy_file PARAMS ((char const *, char const *, mode_t)); +void exit_with_signal PARAMS ((int)) __attribute__ ((noreturn)); +void ignore_signals PARAMS ((void)); +void init_time PARAMS ((void)); +void memory_fatal PARAMS ((void)); +void move_file PARAMS ((char const *, char *, mode_t, int)); +void read_fatal PARAMS ((void)); +void remove_prefix PARAMS ((char *, size_t)); +void removedirs PARAMS ((char *)); +void set_signals PARAMS ((int)); +void write_fatal PARAMS ((void)); diff --git a/contrib/patch/version.c b/contrib/patch/version.c new file mode 100644 index 0000000..ea08437 --- /dev/null +++ b/contrib/patch/version.c @@ -0,0 +1,30 @@ +/* Print the version number. */ + +/* $Id: version.c,v 1.5 1997/05/21 18:29:20 eggert Exp $ */ + +#define XTERN extern +#include +#undef XTERN +#define XTERN +#include +#include + +static char const copyright_string[] = "\ +Copyright 1988 Larry Wall\n\ +Copyright 1997 Free Software Foundation, Inc."; + +static char const free_software_msgid[] = "\ +This program comes with NO WARRANTY, to the extent permitted by law.\n\ +You may redistribute copies of this program\n\ +under the terms of the GNU General Public License.\n\ +For more information about these matters, see the file named COPYING."; + +static char const authorship_msgid[] = "\ +written by Larry Wall with lots o' patches by Paul Eggert"; + +void +version() +{ + printf ("%s %s\n%s\n\n%s\n\n%s\n", program_name, PATCH_VERSION, + copyright_string, free_software_msgid, authorship_msgid); +} diff --git a/contrib/patch/version.h b/contrib/patch/version.h new file mode 100644 index 0000000..b061c94 --- /dev/null +++ b/contrib/patch/version.h @@ -0,0 +1,5 @@ +/* Print the version number. */ + +/* $Id: version.h,v 1.3 1997/04/07 01:07:00 eggert Exp $ */ + +void version PARAMS ((void)); -- cgit v1.1