From f439bb3fe3a40c917b19de74486a8064acca7caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 16 Jun 2004 21:22:10 +0000 Subject: [PATCH] Adding patch 2.5.4, without unused files git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8008 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/bin/patch/COPYING | 340 ++++++ src/apps/bin/patch/Jamfile | 22 + src/apps/bin/patch/addext.c | 105 ++ src/apps/bin/patch/argmatch.c | 306 +++++ src/apps/bin/patch/argmatch.h | 129 +++ src/apps/bin/patch/backupfile.c | 268 +++++ src/apps/bin/patch/backupfile.h | 60 + src/apps/bin/patch/basename.c | 55 + src/apps/bin/patch/basename.h | 9 + src/apps/bin/patch/common.h | 328 ++++++ src/apps/bin/patch/config.h | 170 +++ src/apps/bin/patch/error.c | 250 ++++ src/apps/bin/patch/error.h | 78 ++ src/apps/bin/patch/inp.c | 467 ++++++++ src/apps/bin/patch/inp.h | 10 + src/apps/bin/patch/maketime.c | 501 ++++++++ src/apps/bin/patch/maketime.h | 39 + src/apps/bin/patch/partime.c | 956 +++++++++++++++ src/apps/bin/patch/partime.h | 77 ++ src/apps/bin/patch/patch.c | 1366 ++++++++++++++++++++++ src/apps/bin/patch/patch.man | 1149 +++++++++++++++++++ src/apps/bin/patch/patch.rdef | 11 + src/apps/bin/patch/patchlevel.h | 1 + src/apps/bin/patch/pch.c | 1913 +++++++++++++++++++++++++++++++ src/apps/bin/patch/pch.h | 25 + src/apps/bin/patch/quotearg.c | 403 +++++++ src/apps/bin/patch/quotearg.h | 109 ++ src/apps/bin/patch/quotesys.c | 125 ++ src/apps/bin/patch/quotesys.h | 9 + src/apps/bin/patch/util.c | 996 ++++++++++++++++ src/apps/bin/patch/util.h | 39 + src/apps/bin/patch/version.c | 30 + src/apps/bin/patch/version.h | 5 + src/apps/bin/patch/xalloc.h | 52 + src/apps/bin/patch/xmalloc.c | 113 ++ 35 files changed, 10516 insertions(+) create mode 100644 src/apps/bin/patch/COPYING create mode 100644 src/apps/bin/patch/Jamfile create mode 100644 src/apps/bin/patch/addext.c create mode 100644 src/apps/bin/patch/argmatch.c create mode 100644 src/apps/bin/patch/argmatch.h create mode 100644 src/apps/bin/patch/backupfile.c create mode 100644 src/apps/bin/patch/backupfile.h create mode 100644 src/apps/bin/patch/basename.c create mode 100644 src/apps/bin/patch/basename.h create mode 100644 src/apps/bin/patch/common.h create mode 100644 src/apps/bin/patch/config.h create mode 100644 src/apps/bin/patch/error.c create mode 100644 src/apps/bin/patch/error.h create mode 100644 src/apps/bin/patch/inp.c create mode 100644 src/apps/bin/patch/inp.h create mode 100644 src/apps/bin/patch/maketime.c create mode 100644 src/apps/bin/patch/maketime.h create mode 100644 src/apps/bin/patch/partime.c create mode 100644 src/apps/bin/patch/partime.h create mode 100644 src/apps/bin/patch/patch.c create mode 100644 src/apps/bin/patch/patch.man create mode 100644 src/apps/bin/patch/patch.rdef create mode 100644 src/apps/bin/patch/patchlevel.h create mode 100644 src/apps/bin/patch/pch.c create mode 100644 src/apps/bin/patch/pch.h create mode 100644 src/apps/bin/patch/quotearg.c create mode 100644 src/apps/bin/patch/quotearg.h create mode 100644 src/apps/bin/patch/quotesys.c create mode 100644 src/apps/bin/patch/quotesys.h create mode 100644 src/apps/bin/patch/util.c create mode 100644 src/apps/bin/patch/util.h create mode 100644 src/apps/bin/patch/version.c create mode 100644 src/apps/bin/patch/version.h create mode 100644 src/apps/bin/patch/xalloc.h create mode 100644 src/apps/bin/patch/xmalloc.c diff --git a/src/apps/bin/patch/COPYING b/src/apps/bin/patch/COPYING new file mode 100644 index 0000000000..60549be514 --- /dev/null +++ b/src/apps/bin/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/src/apps/bin/patch/Jamfile b/src/apps/bin/patch/Jamfile new file mode 100644 index 0000000000..e2793ef9a1 --- /dev/null +++ b/src/apps/bin/patch/Jamfile @@ -0,0 +1,22 @@ +SubDir OBOS_TOP src apps bin patch ; + +SubDirCcFlags -DHAVE_CONFIG_H -Ded_PROGRAM='\"ed\"' ; + +BinCommand patch : + addext.c + argmatch.c + backupfile.c + basename.c + error.c + inp.c + maketime.c + partime.c + patch.c + pch.c + quotearg.c + quotesys.c + util.c + version.c + xmalloc.c + : + : patch.rdef ; diff --git a/src/apps/bin/patch/addext.c b/src/apps/bin/patch/addext.c new file mode 100644 index 0000000000..a075ea3b1e --- /dev/null +++ b/src/apps/bin/patch/addext.c @@ -0,0 +1,105 @@ +/* addext.c -- add an extension to a file name + Copyright (C) 1990, 1997, 1998, 1999 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 + +char *base_name PARAMS ((char const *)); + +/* Append to FILENAME the extension EXT, unless the result would be too long, + in which case just append the character E. */ + +void +addext (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/src/apps/bin/patch/argmatch.c b/src/apps/bin/patch/argmatch.c new file mode 100644 index 0000000000..964270650c --- /dev/null +++ b/src/apps/bin/patch/argmatch.c @@ -0,0 +1,306 @@ +/* argmatch.c -- find a match for a string in an array + Copyright (C) 1990, 1998, 1999 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. */ + +/* Written by David MacKenzie + Modified by Akim Demaille */ + +#include "argmatch.h" + +#include +#ifdef STDC_HEADERS +# include +#endif + +#if HAVE_LOCALE_H +# include +#endif + +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# define _(Text) Text +#endif + +#include "error.h" +#include "quotearg.h" + +/* When reporting an invalid argument, show nonprinting characters + by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use + literal_quoting_style. */ +#ifndef ARGMATCH_QUOTING_STYLE +# define ARGMATCH_QUOTING_STYLE locale_quoting_style +#endif + +/* The following test is to work around the gross typo in + systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE + is defined to 0, not 1. */ +#if !EXIT_FAILURE +# undef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +/* Non failing version of argmatch call this function after failing. */ +#ifndef ARGMATCH_DIE +# define ARGMATCH_DIE exit (EXIT_FAILURE) +#endif + +#ifdef ARGMATCH_DIE_DECL +ARGMATCH_DIE_DECL; +#endif + +static void +__argmatch_die (void) +{ + ARGMATCH_DIE; +} + +/* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h. + Default to __argmatch_die, but allow caller to change this at run-time. */ +argmatch_exit_fn argmatch_die = __argmatch_die; + + +/* If ARG is an unambiguous match for an element of the + null-terminated array ARGLIST, return the index in ARGLIST + 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). + If SENSITIVE, comparison is case sensitive. + + If VALLIST is none null, use it to resolve ambiguities limited to + synonyms, i.e., for + "yes", "yop" -> 0 + "no", "nope" -> 1 + "y" is a valid argument, for `0', and "n" for `1'. */ + +static int +__argmatch_internal (const char *arg, const char *const *arglist, + const char *vallist, size_t valsize, + int case_sensitive) +{ + int i; /* Temporary index in ARGLIST. */ + 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; arglist[i]; i++) + { + if (case_sensitive + ? !strncmp (arglist[i], arg, arglen) + : !strncasecmp (arglist[i], arg, arglen)) + { + if (strlen (arglist[i]) == arglen) + /* Exact match found. */ + return i; + else if (matchind == -1) + /* First nonexact match found. */ + matchind = i; + else + { + /* Second nonexact match found. */ + if (vallist == NULL + || memcmp (vallist + valsize * matchind, + vallist + valsize * i, valsize)) + { + /* There is a real ambiguity, or we could not + disambiguate. */ + ambiguous = 1; + } + } + } + } + if (ambiguous) + return -2; + else + return matchind; +} + +/* argmatch - case sensitive version */ +int +argmatch (const char *arg, const char *const *arglist, + const char *vallist, size_t valsize) +{ + return __argmatch_internal (arg, arglist, vallist, valsize, 1); +} + +/* argcasematch - case insensitive version */ +int +argcasematch (const char *arg, const char *const *arglist, + const char *vallist, size_t valsize) +{ + return __argmatch_internal (arg, arglist, vallist, valsize, 0); +} + +/* Error reporting for argmatch. + CONTEXT 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 +argmatch_invalid (const char *context, const char *value, int problem) +{ + char const *format = (problem == -1 + ? _("invalid argument %s for `%s'") + : _("ambiguous argument %s for `%s'")); + + error (0, 0, format, quotearg_style (ARGMATCH_QUOTING_STYLE, value), context); +} + +/* List the valid arguments for argmatch. + ARGLIST is the same as in argmatch. + VALLIST is a pointer to an array of values. + VALSIZE is the size of the elements of VALLIST */ +void +argmatch_valid (const char *const *arglist, + const char *vallist, size_t valsize) +{ + int i; + const char *last_val = NULL; + + /* We try to put synonyms on the same line. The assumption is that + synonyms follow each other */ + fprintf (stderr, _("Valid arguments are:")); + for (i = 0; arglist[i]; i++) + if ((i == 0) + || memcmp (last_val, vallist + valsize * i, valsize)) + { + fprintf (stderr, "\n - `%s'", arglist[i]); + last_val = vallist + valsize * i; + } + else + { + fprintf (stderr, ", `%s'", arglist[i]); + } + putc ('\n', stderr); +} + +/* Never failing versions of the previous functions. + + CONTEXT is the context for which argmatch is called (e.g., + "--version-control", or "$VERSION_CONTROL" etc.). Upon failure, + calls the (supposed never to return) function EXIT_FN. */ + +int +__xargmatch_internal (const char *context, + const char *arg, const char *const *arglist, + const char *vallist, size_t valsize, + int case_sensitive, + argmatch_exit_fn exit_fn) +{ + int res = __argmatch_internal (arg, arglist, + vallist, valsize, + case_sensitive); + if (res >= 0) + /* Success. */ + return res; + + /* We failed. Explain why. */ + argmatch_invalid (context, arg, res); + argmatch_valid (arglist, vallist, valsize); + (*exit_fn) (); + + return -1; /* To please the compilers. */ +} + +/* Look for VALUE in VALLIST, an array of objects of size VALSIZE and + return the first corresponding argument in ARGLIST */ +const char * +argmatch_to_argument (const char *value, + const char *const *arglist, + const char *vallist, size_t valsize) +{ + int i; + + for (i = 0; arglist[i]; i++) + if (!memcmp (value, vallist + valsize * i, valsize)) + return arglist[i]; + return NULL; +} + +#ifdef TEST +/* + * Based on "getversion.c" by David MacKenzie + */ +char *program_name; +extern const char *getenv (); + +/* 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 +}; + +/* Two tables describing arguments (keys) and their corresponding + values */ +static const char *const backup_args[] = +{ + "no", "none", "off", + "simple", "never", + "existing", "nil", + "numbered", "t", + 0 +}; + +static const enum backup_type backup_vals[] = +{ + none, none, none, + simple, simple, + numbered_existing, numbered_existing, + numbered, numbered +}; + +int +main (int argc, const char *const *argv) +{ + const char *cp; + enum backup_type backup_type = none; + + program_name = (char *) argv[0]; + + if (argc > 2) + { + fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name); + exit (1); + } + + if ((cp = getenv ("VERSION_CONTROL"))) + backup_type = XARGCASEMATCH ("$VERSION_CONTROL", cp, + backup_args, backup_vals); + + if (argc == 2) + backup_type = XARGCASEMATCH (program_name, argv[1], + backup_args, backup_vals); + + printf ("The version control is `%s'\n", + ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals)); + + return 0; +} +#endif diff --git a/src/apps/bin/patch/argmatch.h b/src/apps/bin/patch/argmatch.h new file mode 100644 index 0000000000..d3f25cc333 --- /dev/null +++ b/src/apps/bin/patch/argmatch.h @@ -0,0 +1,129 @@ +/* argmatch.h -- definitions and prototypes for argmatch.c + Copyright (C) 1990, 1998, 1999 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. */ + +/* Written by David MacKenzie + Modified by Akim Demaille */ + +#ifndef ARGMATCH_H_ +# define ARGMATCH_H_ 1 + +# if HAVE_CONFIG_H +# include +# endif + +# include + +# ifndef PARAMS +# if PROTOTYPES || (defined (__STDC__) && __STDC__) +# define PARAMS(args) args +# else +# define PARAMS(args) () +# endif /* GCC. */ +# endif /* Not PARAMS. */ + +/* Assert there are as many real arguments as there are values + (argument list ends with a NULL guard). There is no execution + cost, since it will be statically evalauted to `assert (0)' or + `assert (1)'. Unfortunately there is no -Wassert-0. */ + +# undef ARRAY_CARDINALITY +# define ARRAY_CARDINALITY(Array) (sizeof ((Array)) / sizeof (*(Array))) + +# define ARGMATCH_ASSERT(Arglist, Vallist) \ + assert (ARRAY_CARDINALITY ((Arglist)) == ARRAY_CARDINALITY ((Vallist)) + 1) + +/* Return the index of the element of ARGLIST (NULL terminated) that + matches with ARG. If VALLIST is not NULL, then use it to resolve + false ambiguities (i.e., different matches of ARG but corresponding + to the same values in VALLIST). */ + +int argmatch + PARAMS ((const char *arg, const char *const *arglist, + const char *vallist, size_t valsize)); +int argcasematch + PARAMS ((const char *arg, const char *const *arglist, + const char *vallist, size_t valsize)); + +# define ARGMATCH(Arg, Arglist, Vallist) \ + argmatch ((Arg), (Arglist), (const char *) (Vallist), sizeof (*(Vallist))) + +# define ARGCASEMATCH(Arg, Arglist, Vallist) \ + argcasematch ((Arg), (Arglist), (const char *) (Vallist), sizeof (*(Vallist))) + +/* xargmatch calls this function when it fails. This function should not + return. By default, this is a function that calls ARGMATCH_DIE which + in turn defaults to `exit (EXIT_FAILURE)'. */ +typedef void (*argmatch_exit_fn) PARAMS ((void)); +extern argmatch_exit_fn argmatch_die; + +/* Report on stderr why argmatch failed. Report correct values. */ + +void argmatch_invalid + PARAMS ((const char *context, const char *value, int problem)); + +/* Left for compatibility with the old name invalid_arg */ + +# define invalid_arg(Context, Value, Problem) \ + argmatch_invalid ((Context), (Value), (Problem)) + + + +/* Report on stderr the list of possible arguments. */ + +void argmatch_valid + PARAMS ((const char *const *arglist, + const char *vallist, size_t valsize)); + +# define ARGMATCH_VALID(Arglist, Vallist) \ + argmatch_valid (Arglist, (const char *) Vallist, sizeof (*(Vallist))) + + + +/* Same as argmatch, but upon failure, reports a explanation on the + failure, and exits using the function EXIT_FN. */ + +int __xargmatch_internal + PARAMS ((const char *context, + const char *arg, const char *const *arglist, + const char *vallist, size_t valsize, + int case_sensitive, argmatch_exit_fn exit_fn)); + +/* Programmer friendly interface to __xargmatch_internal. */ + +# define XARGMATCH(Context, Arg, Arglist, Vallist) \ + (Vallist [__xargmatch_internal ((Context), (Arg), (Arglist), \ + (const char *) (Vallist), \ + sizeof (*(Vallist)), \ + 1, argmatch_die)]) + +# define XARGCASEMATCH(Context, Arg, Arglist, Vallist) \ + (Vallist [__xargmatch_internal ((Context), (Arg), (Arglist), \ + (const char *) (Vallist), \ + sizeof (*(Vallist)), \ + 0, argmatch_die)]) + +/* Convert a value into a corresponding argument. */ + +const char *argmatch_to_argument + PARAMS ((char const *value, const char *const *arglist, + const char *vallist, size_t valsize)); + +# define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \ + argmatch_to_argument ((char const *) &(Value), (Arglist), \ + (const char *) (Vallist), sizeof (*(Vallist))) + +#endif /* ARGMATCH_H_ */ diff --git a/src/apps/bin/patch/backupfile.c b/src/apps/bin/patch/backupfile.c new file mode 100644 index 0000000000..7f4da17fca --- /dev/null +++ b/src/apps/bin/patch/backupfile.c @@ -0,0 +1,268 @@ +/* backupfile.c -- make Emacs style backup file names + Copyright (C) 1990-1997, 1998, 1999 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 + +#ifndef HAVE_DECL_GETENV +char *getenv (); +#endif + +char *base_name PARAMS ((char const *)); + +#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 + +/* The extension added to file names to produce a simple (as opposed + to numbered) backup file name. */ +const char *simple_backup_suffix = "~"; + +static int max_backup_version PARAMS ((const char *, const char *)); +static int version_number PARAMS ((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. + Do not call this function if backup_type == none. */ + +char * +find_backup_file_name (const char *file, enum backup_type backup_type) +{ + 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) + { + 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 (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 (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[] = +{ + /* In a series of synonyms, present the most meaning full first, so + that argmatch_valid be more readable. */ + "none", "off", + "simple", "never", + "existing", "nil", + "numbered", "t", + 0 +}; + +static const enum backup_type backup_types[] = +{ + none, none, + simple, simple, + numbered_existing, numbered_existing, + numbered, numbered +}; + +/* Return the type of backup specified by VERSION. + If VERSION is NULL or the empty string, return numbered_existing. + If VERSION is invalid or ambiguous, fail with a diagnostic appropriate + for the specified CONTEXT. Unambiguous abbreviations are accepted. */ + +enum backup_type +get_version (const char *context, const char *version) +{ + if (version == 0 || *version == 0) + return numbered_existing; + else + return XARGMATCH (context, version, backup_args, backup_types); +} + + +/* Return the type of backup specified by VERSION. + If VERSION is NULL, use the value of the envvar VERSION_CONTROL. + If the specified string is invalid or ambiguous, fail with a diagnostic + appropriate for the specified CONTEXT. + Unambiguous abbreviations are accepted. */ + +enum backup_type +xget_version (const char *context, const char *version) +{ + if (version && *version) + return get_version (context, version); + else + return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL")); +} diff --git a/src/apps/bin/patch/backupfile.h b/src/apps/bin/patch/backupfile.h new file mode 100644 index 0000000000..b9b973c2dd --- /dev/null +++ b/src/apps/bin/patch/backupfile.h @@ -0,0 +1,60 @@ +/* backupfile.h -- declarations for making Emacs style backup file names + Copyright (C) 1990-1992, 1997-1999 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 BACKUPFILE_H_ +# define BACKUPFILE_H_ + +/* 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 +}; + +# define VALID_BACKUP_TYPE(Type) \ + ((Type) == none \ + || (Type) == simple \ + || (Type) == numbered_existing \ + || (Type) == numbered) + +extern char const *simple_backup_suffix; + +# ifndef PARAMS +# if defined PROTOTYPES || (defined __STDC__ && __STDC__) +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +# endif + +char *find_backup_file_name PARAMS ((char const *, enum backup_type)); +enum backup_type get_version PARAMS ((char const *context, char const *arg)); +enum backup_type xget_version PARAMS ((char const *context, char const *arg)); +void addext PARAMS ((char *, char const *, int)); + +#endif /* ! BACKUPFILE_H_ */ diff --git a/src/apps/bin/patch/basename.c b/src/apps/bin/patch/basename.c new file mode 100644 index 0000000000..e648b1a7e2 --- /dev/null +++ b/src/apps/bin/patch/basename.c @@ -0,0 +1,55 @@ +/* basename.c -- return the last element in a path + Copyright (C) 1990, 1998, 1999 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 + +#ifndef FILESYSTEM_PREFIX_LEN +# define FILESYSTEM_PREFIX_LEN(Filename) 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. + If NAME is all slashes, be sure to return `/'. */ + +char * +base_name (char const *name) +{ + char const *base = name += FILESYSTEM_PREFIX_LEN (name); + int all_slashes = 1; + char const *p; + + for (p = name; *p; p++) + { + if (ISSLASH (*p)) + base = p + 1; + else + all_slashes = 0; + } + + /* If NAME is all slashes, arrange to return `/'. */ + if (*base == '\0' && ISSLASH (*name) && all_slashes) + --base; + + return (char *) base; +} diff --git a/src/apps/bin/patch/basename.h b/src/apps/bin/patch/basename.h new file mode 100644 index 0000000000..90f765428d --- /dev/null +++ b/src/apps/bin/patch/basename.h @@ -0,0 +1,9 @@ +#ifndef PARAMS +# if defined PROTOTYPES || (defined __STDC__ && __STDC__) +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +#endif + +char *base_name PARAMS ((char const *name)); diff --git a/src/apps/bin/patch/common.h b/src/apps/bin/patch/common.h new file mode 100644 index 0000000000..64b88fce91 --- /dev/null +++ b/src/apps/bin/patch/common.h @@ -0,0 +1,328 @@ +/* common definitions for `patch' */ + +/* $Id: common.h,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +/* Copyright 1986, 1988 Larry Wall + Copyright 1990, 1991-1993, 1997-1998, 1999 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 + +#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 CHAR_BIT +#define CHAR_BIT 8 +#endif +/* The extra casts work around common compiler bugs, + e.g. Cray C 5.0.3.0 time_t. */ +#define TYPE_SIGNED(t) ((t) -1 < (t) 0) +#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \ + ? (t) (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)) \ + : (t) 0)) +#define TYPE_MAXIMUM(t) ((t) ((t) ~ (t) 0 - TYPE_MINIMUM (t))) +#ifndef CHAR_MAX +#define CHAR_MAX TYPE_MAXIMUM (char) +#endif +#ifndef INT_MAX +#define INT_MAX TYPE_MAXIMUM (int) +#endif +#ifndef LONG_MIN +#define LONG_MIN TYPE_MINIMUM (long) +#endif + +#if HAVE_INTTYPES_H +# include +#endif +#ifndef SIZE_MAX +/* On some nonstandard hosts, size_t is signed, + so SIZE_MAX != (size_t) -1. */ +#define SIZE_MAX TYPE_MAXIMUM (size_t) +#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 off_t 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 TMPINNAME; +XTERN char const * volatile TMPOUTNAME; +XTERN char const * volatile TMPPATNAME; + +XTERN int volatile TMPINNAME_needs_removal; +XTERN int volatile TMPOUTNAME_needs_removal; +XTERN int volatile TMPPATNAME_needs_removal; + +#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 + +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 +char *getenv (); +GENERIC_OBJECT *malloc (); +GENERIC_OBJECT *realloc (); +#endif + +#if HAVE_UNISTD_H +# include +#else +# ifndef lseek + off_t lseek (); +# endif +#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 HAVE_FSEEKO + 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_EXCL +#define O_EXCL 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/src/apps/bin/patch/config.h b/src/apps/bin/patch/config.h new file mode 100644 index 0000000000..c44585188b --- /dev/null +++ b/src/apps/bin/patch/config.h @@ -0,0 +1,170 @@ +/* config.h. Generated automatically by configure. */ +/* config.hin. Generated automatically from configure.in by autoheader. */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define if the closedir function returns void instead of int. */ +/* #undef CLOSEDIR_VOID */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if you don't have vprintf but do have _doprnt. */ +/* #undef HAVE_DOPRNT */ + +/* Define if you support file names longer than 14 characters. */ +#define HAVE_LONG_FILE_NAMES 1 + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF 1 + +/* Define if on MINIX. */ +/* #undef _MINIX */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if you have the _doprintf function. */ +/* #undef HAVE__DOPRINTF */ + +/* Define if you have the fseeko function. */ +#define HAVE_FSEEKO 1 + +/* Define if you have the isascii function. */ +#define HAVE_ISASCII 1 + +/* Define if you have the memchr function. */ +#define HAVE_MEMCHR 1 + +/* Define if you have the memcmp function. */ +#define HAVE_MEMCMP 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the mktemp function. */ +#define HAVE_MKTEMP 1 + +/* Define if you have the pathconf function. */ +#define HAVE_PATHCONF 1 + +/* Define if you have the raise function. */ +#define HAVE_RAISE 1 + +/* Define if you have the rename function. */ +#define HAVE_RENAME 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the setmode function. */ +/* #undef HAVE_SETMODE */ + +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the sigprocmask function. */ +#define HAVE_SIGPROCMASK 1 + +/* Define if you have the sigsetmask function. */ +/* #undef HAVE_SIGSETMASK */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_VARARGS_H 1 + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define to make fseeko etc. visible, on some hosts. */ +/* #undef _LARGEFILE_SOURCE */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define if compiler has function prototypes */ +#define PROTOTYPES 1 + +/* Define if exists, doesn't clash with , + and declares uintmax_t. */ +#define HAVE_INTTYPES_H 1 + +/* Define if struct utimbuf is declared -- usually in . + Some systems have utime.h but don't declare the struct anywhere. */ +#define HAVE_STRUCT_UTIMBUF 1 + +/* Define if the malloc check has been performed. */ +#define HAVE_DONE_WORKING_MALLOC_CHECK 1 + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define if the realloc check has been performed. */ +#define HAVE_DONE_WORKING_REALLOC_CHECK 1 + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define if there is a member named d_ino in the struct describing + directory headers. */ +#define D_INO_IN_DIRENT 1 + diff --git a/src/apps/bin/patch/error.c b/src/apps/bin/patch/error.c new file mode 100644 index 0000000000..4eff5d0f9d --- /dev/null +++ b/src/apps/bin/patch/error.c @@ -0,0 +1,250 @@ +/* Error handler for noninteractive utilities + Copyright (C) 1990,91,92,93,94,95,96,97,98 Free Software Foundation, Inc. + + This file is part of the GNU C Library. Its master source is NOT part of + the C library, however. The master source lives in /gd/gnu/lib. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* Written by David MacKenzie . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC +# if __STDC__ +# include +# define VA_START(args, lastarg) va_start(args, lastarg) +# else +# include +# define VA_START(args, lastarg) va_start(args) +# endif +#else +# define va_alist a1, a2, a3, a4, a5, a6, a7, a8 +# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8; +#endif + +#if STDC_HEADERS || _LIBC +# include +# include +#else +void exit (); +#endif + +#include "error.h" + +#ifndef _ +# define _(String) String +#endif + +/* If NULL, error will flush stdout, then print on stderr the program + name, a colon and a space. Otherwise, error will call this + function without parameters instead. */ +void (*error_print_progname) ( +#if __STDC__ - 0 + void +#endif + ); + +/* This variable is incremented each time `error' is called. */ +unsigned int error_message_count; + +#ifdef _LIBC +/* In the GNU C library, there is a predefined variable for this. */ + +# define program_name program_invocation_name +# include + +/* In GNU libc we want do not want to use the common name `error' directly. + Instead make it a weak alias. */ +# define error __error +# define error_at_line __error_at_line + +#else /* not _LIBC */ + +/* The calling program should define program_name and set it to the + name of the executing program. */ +extern char *program_name; + +# ifdef HAVE_STRERROR_R +# define __strerror_r strerror_r +# else +# if HAVE_STRERROR +# ifndef strerror /* On some systems, strerror is a macro */ +char *strerror (); +# endif +# else +static char * +private_strerror (errnum) + int errnum; +{ + extern char *sys_errlist[]; + extern int sys_nerr; + + if (errnum > 0 && errnum <= sys_nerr) + return _(sys_errlist[errnum]); + return _("Unknown system error"); +} +# define strerror private_strerror +# endif /* HAVE_STRERROR */ +# endif /* HAVE_STRERROR_R */ +#endif /* not _LIBC */ + +/* Print the program name and error message MESSAGE, which is a printf-style + format string with optional args. + If ERRNUM is nonzero, print its corresponding system error message. + Exit with status STATUS if it is nonzero. */ +/* VARARGS */ + +void +#if defined VA_START && __STDC__ +error (int status, int errnum, const char *message, ...) +#else +error (status, errnum, message, va_alist) + int status; + int errnum; + char *message; + va_dcl +#endif +{ +#ifdef VA_START + va_list args; +#endif + + if (error_print_progname) + (*error_print_progname) (); + else + { + fflush (stdout); + fprintf (stderr, "%s: ", program_name); + } + +#ifdef VA_START + VA_START (args, message); +# if HAVE_VPRINTF || _LIBC + vfprintf (stderr, message, args); +# else + _doprnt (message, args, stderr); +# endif + va_end (args); +#else + fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); +#endif + + ++error_message_count; + if (errnum) + { +#if defined HAVE_STRERROR_R || defined _LIBC + char errbuf[1024]; + /* Don't use __strerror_r's return value because on some systems + (at least DEC UNIX 4.0[A-D]) strerror_r returns `int'. */ + __strerror_r (errnum, errbuf, sizeof errbuf); + fprintf (stderr, ": %s", errbuf); +#else + fprintf (stderr, ": %s", strerror (errnum)); +#endif + } + putc ('\n', stderr); + fflush (stderr); + if (status) + exit (status); +} + +/* Sometimes we want to have at most one error per line. This + variable controls whether this mode is selected or not. */ +int error_one_per_line; + +void +#if defined VA_START && __STDC__ +error_at_line (int status, int errnum, const char *file_name, + unsigned int line_number, const char *message, ...) +#else +error_at_line (status, errnum, file_name, line_number, message, va_alist) + int status; + int errnum; + const char *file_name; + unsigned int line_number; + char *message; + va_dcl +#endif +{ +#ifdef VA_START + va_list args; +#endif + + if (error_one_per_line) + { + static const char *old_file_name; + static unsigned int old_line_number; + + if (old_line_number == line_number && + (file_name == old_file_name || !strcmp (old_file_name, file_name))) + /* Simply return and print nothing. */ + return; + + old_file_name = file_name; + old_line_number = line_number; + } + + if (error_print_progname) + (*error_print_progname) (); + else + { + fflush (stdout); + fprintf (stderr, "%s:", program_name); + } + + if (file_name != NULL) + fprintf (stderr, "%s:%d: ", file_name, line_number); + +#ifdef VA_START + VA_START (args, message); +# if HAVE_VPRINTF || _LIBC + vfprintf (stderr, message, args); +# else + _doprnt (message, args, stderr); +# endif + va_end (args); +#else + fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); +#endif + + ++error_message_count; + if (errnum) + { +#if defined HAVE_STRERROR_R || defined _LIBC + char errbuf[1024]; + fprintf (stderr, ": %s", __strerror_r (errnum, errbuf, sizeof errbuf)); +#else + fprintf (stderr, ": %s", strerror (errnum)); +#endif + } + putc ('\n', stderr); + fflush (stderr); + if (status) + exit (status); +} + +#ifdef _LIBC +/* Make the weak alias. */ +# undef error +# undef error_at_line +weak_alias (__error, error) +weak_alias (__error_at_line, error_at_line) +#endif diff --git a/src/apps/bin/patch/error.h b/src/apps/bin/patch/error.h new file mode 100644 index 0000000000..20f75824d6 --- /dev/null +++ b/src/apps/bin/patch/error.h @@ -0,0 +1,78 @@ +/* Declaration for error-reporting function + Copyright (C) 1995, 1996, 1997 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 _ERROR_H +#define _ERROR_H 1 + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __format__ format +# define __printf__ printf +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (__STDC__) && __STDC__ + +/* Print a message with `fprintf (stderr, FORMAT, ...)'; + if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). + If STATUS is nonzero, terminate the program with `exit (STATUS)'. */ + +extern void error (int status, int errnum, const char *format, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); + +extern void error_at_line (int status, int errnum, const char *fname, + unsigned int lineno, const char *format, ...) + __attribute__ ((__format__ (__printf__, 5, 6))); + +/* If NULL, error will flush stdout, then print on stderr the program + name, a colon and a space. Otherwise, error will call this + function without parameters instead. */ +extern void (*error_print_progname) (void); + +#else +void error (); +void error_at_line (); +extern void (*error_print_progname) (); +#endif + +/* This variable is incremented each time `error' is called. */ +extern unsigned int error_message_count; + +/* Sometimes we want to have at most one error per line. This + variable controls whether this mode is selected or not. */ +extern int error_one_per_line; + +#ifdef __cplusplus +} +#endif + +#endif /* error.h */ diff --git a/src/apps/bin/patch/inp.c b/src/apps/bin/patch/inp.c new file mode 100644 index 0000000000..cd3b2e75f3 --- /dev/null +++ b/src/apps/bin/patch/inp.c @@ -0,0 +1,467 @@ +/* inputting files to be patched */ + +/* $Id: inp.c,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +/* Copyright 1986, 1988 Larry Wall + Copyright 1991, 1992-1993, 1997-1998, 1999 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 +#include +#include +#undef XTERN +#define XTERN +#include + +/* Input-file-with-indexable-lines abstract type */ + +static char *i_buffer; /* plan A buffer */ +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)); +static void too_many_lines PARAMS ((char const *)) __attribute__((noreturn)); + +/* New patch--prepare to edit another file. */ + +void +re_input (void) +{ + if (using_plan_a) { + if (i_buffer) + { + free (i_buffer); + i_buffer = 0; + free (i_ptr); + } + } + else { + close (tifd); + tifd = -1; + if (tibuf[0]) + { + free (tibuf[0]); + tibuf[0] = 0; + } + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Construct the line index, somehow or other. */ + +void +scan_input (char *filename) +{ + using_plan_a = ! (debug & 16) && plan_a (filename); + if (!using_plan_a) + plan_b(filename); + + if (verbosity != SILENT) + { + filename = quotearg (filename); + + if (verbosity == VERBOSE) + say ("Patching file %s using Plan %s...\n", + filename, using_plan_a ? "A" : "B"); + else + say ("patching file %s\n", filename); + } +} + +/* Report whether a desired revision was found. */ + +static void +report_revision (int found_revision) +{ + revision = quotearg (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"); + } +} + + +static void +too_many_lines (char const *filename) +{ + fatal ("File %s has too many lines", quotearg (filename)); +} + + +void +get_input_file (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", + quotearg (filename), cs); + if (diffbuf) + { + /* 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", + quotearg (filename), cs); + + if (systemic (diffbuf) != 0) + { + say ("warning: Patching file %s, which does not match default %s version\n", + quotearg (filename), cs); + cs = 0; + } + } + } + + if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf, + &instat)) + inerrno = 0; + + free (getbuf); + if (diffbuf) + free (diffbuf); + + } else if (inerrno && !pch_says_nonexistent (reverse)) + { + errno = inerrno; + pfatal ("Can't find file %s", quotearg (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 ("File %s is not a regular file -- can't patch", + quotearg (filename)); +} + + +/* Try keeping everything in memory. */ + +static bool +plan_a (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; + + /* Fail if the file size doesn't fit in a size_t, + or if storage isn't available. */ + if (! (size == instat.st_size + && (buffer = malloc (size ? size : (size_t) 1)))) + return FALSE; + + /* 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", quotearg (filename)); + + while (size - buffered != 0) + { + n = read (ifd, buffer + buffered, size - buffered); + if (n == 0) + { + /* Some non-POSIX hosts exaggerate st_size in text mode; + or the file may have shrunk! */ + size = buffered; + break; + } + if (n == (size_t) -1) + { + /* Perhaps size is too large for this host. */ + close (ifd); + free (buffer); + return FALSE; + } + buffered += n; + } + + if (close (ifd) != 0) + read_fatal (); + } + + /* Scan the buffer and build array of pointers to lines. */ + lim = buffer + size; + iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */ + for (s = buffer; (s = (char *) memchr (s, '\n', lim - s)); s++) + if (++iline < 0) + too_many_lines (filename); + if (! (iline == (size_t) iline + && (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline + && (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr)))) + { + free (buffer); + return FALSE; + } + iline = 0; + 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_buffer = buffer; + i_ptr = ptr; + return TRUE; +} + +/* Keep (virtually) nothing in memory. */ + +static void +plan_b (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 = 1; + int exclusive; + + if (instat.st_size == 0) + filename = NULL_DEVICE; + if (! (ifp = fopen (filename, binary_transput ? "rb" : "r"))) + pfatal ("Can't open file %s", quotearg (filename)); + exclusive = TMPINNAME_needs_removal ? 0 : O_EXCL; + TMPINNAME_needs_removal = 1; + tifd = create_file (TMPINNAME, O_RDWR | O_BINARY | exclusive, (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 (++line < 0) + too_many_lines (filename); + 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. + WHICHBUF is ignored when the file is in memory. */ + +char const * +ifetch (LINENUM line, int whichbuf, 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/src/apps/bin/patch/inp.h b/src/apps/bin/patch/inp.h new file mode 100644 index 0000000000..bee582c21e --- /dev/null +++ b/src/apps/bin/patch/inp.h @@ -0,0 +1,10 @@ +/* inputting files to be patched */ + +/* $Id: inp.h,v 1.1 2004/06/16 21:22:10 korli 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/src/apps/bin/patch/maketime.c b/src/apps/bin/patch/maketime.c new file mode 100644 index 0000000000..40974c5e49 --- /dev/null +++ b/src/apps/bin/patch/maketime.c @@ -0,0 +1,501 @@ +/* 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 + + */ + +/* 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. */ + +#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 maket_id[] = + "$Id: maketime.c,v 1.1 2004/06/16 21:22:10 korli Exp $"; + +static int isleap P ((int)); +static int month_days P ((struct tm const *)); +static time_t maketime P ((struct partime const *, time_t)); + +/* Suppose A1 + B1 = SUM1, using 2's complement arithmetic ignoring overflow. + Suppose A, B and SUM have the same respective signs as A1, B1, and SUM1. + Then this yields nonzero if overflow occurred during the addition. + Overflow occurs if A and B have the same sign, but A and SUM differ in sign. + Use `^' to test whether signs differ, and `< 0' to isolate the sign. */ +#define overflow_sum_sign(a, b, sum) ((~((a) ^ (b)) & ((a) ^ (sum))) < 0) + +/* Quotient and remainder when dividing A by B, + truncating towards minus infinity, where B is positive. */ +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define MOD(a, b) ((a) % (b) + (b) * ((a) % (b) < 0)) + +/* Number of days in 400 consecutive Gregorian years. */ +#define Y400_DAYS (365 * 400L + 100 - 4 + 1) + +/* Number of years to add to tm_year to get Gregorian year. */ +#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 = DIV (ay, 100); + int bc = DIV (by, 100); + 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. + The absolute value of SECONDS cannot exceed 59 * INT_MAX, + and also cannot exceed one month's worth of seconds; + this is enough to handle any POSIX or real-life daylight-saving offset. + 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; +{ + int days = 0; + + /* 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 + && (t->tm_hour -= (59 - t->tm_min) / 60) < 0) + { + days = - ((23 - t->tm_hour) / 24); + if ((t->tm_mday += days) <= 0) + { + if (--t->tm_mon < 0) + { + --t->tm_year; + t->tm_mon = 11; + } + t->tm_mday += month_days (t); + } + } + } + else + { + if (60 <= (t->tm_min += sec / 60) + && (24 <= (t->tm_hour += t->tm_min / 60))) + { + days = t->tm_hour / 24; + if (month_days (t) < (t->tm_mday += days)) + { + if (11 < ++t->tm_mon) + { + ++t->tm_year; + t->tm_mon = 0; + } + t->tm_mday = 1; + } + } + } + if (TM_DEFINED (t->tm_wday)) + t->tm_wday = MOD (t->tm_wday + days, 7); + t->tm_hour = MOD (t->tm_hour, 24); + t->tm_min = MOD (t->tm_min, 60); + t->tm_sec = (int) MOD (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 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, year; + struct tm tm; + struct tm *tm0 = 0; + time_t r; + int use_ordinal_day; + + tm0 = 0; /* Keep gcc -Wall happy. */ + localzone = pt->zone == TM_LOCAL_ZONE; + + tm = pt->tm; + year = tm.tm_year; + wday = tm.tm_wday; + use_ordinal_day = (!TM_DEFINED (tm.tm_mday) + && TM_DEFINED (wday) && TM_DEFINED (pt->wday_ordinal)); + + if (use_ordinal_day || TM_DEFINED (pt->ymodulus) || !TM_DEFINED (year)) + { + /* Get tm corresponding to default time. */ + tm0 = time2tm (default_time, localzone); + if (!localzone) + adjzone (tm0, pt->zone); + } + + if (use_ordinal_day) + tm.tm_mday = (tm0->tm_mday + + ((wday - tm0->tm_wday + 7) % 7 + + 7 * (pt->wday_ordinal - (pt->wday_ordinal != 0)))); + + if (TM_DEFINED (pt->ymodulus)) + { + /* Yield a year closest to the default that has the given modulus. */ + int year0 = tm0->tm_year + TM_YEAR_ORIGIN; + int y0 = MOD (year0, pt->ymodulus); + int d = 2 * (year - y0); + year += (((year0 - y0) / pt->ymodulus + + (pt->ymodulus < d ? -1 : d < -pt->ymodulus)) + * pt->ymodulus); + } + else if (!TM_DEFINED (year)) + { + /* Set default year, month, day from current time. */ + 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; + } + } + + /* 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; + + tm.tm_year = year - TM_YEAR_ORIGIN; + if ((year < tm.tm_year) != (TM_YEAR_ORIGIN < 0)) + return -1; + + if (!localzone) + { + adjzone (&tm, -pt->zone); + wday = tm.tm_wday; + } + + /* Convert and fill in the rest of the tm. */ + r = tm2time (&tm, localzone); + if (r == -1) + return r; + + /* Check weekday. */ + if (TM_DEFINED (wday) && wday != tm.tm_wday) + return -1; + + /* Add relative time, except for seconds. + We handle seconds separately, at the end, + so that leap seconds are handled properly. */ + if (pt->tmr.tm_year | pt->tmr.tm_mon | pt->tmr.tm_mday + | pt->tmr.tm_hour | pt->tmr.tm_min) + { + int years = tm.tm_year + pt->tmr.tm_year; + int mons = tm.tm_mon + pt->tmr.tm_mon; + int mdays = tm.tm_mday + pt->tmr.tm_mday; + int hours = tm.tm_hour + pt->tmr.tm_hour; + int mins = tm.tm_min + pt->tmr.tm_min; + + int carried_hours = DIV (mins, 60); + int hours1 = hours + carried_hours; + int carried_days = DIV (hours1, 24); + int mdays1 = mdays + carried_days; + + int mon0 = MOD (mons, 12); + int carried_years0 = DIV (mons, 12); + int year0 = years + carried_years0; + int yday0 = (month_yday[mon0] + - (mon0 < 2 || !isleap (year0 + TM_YEAR_ORIGIN))); + + int yday1 = yday0 + mdays1; + int carried_years1 = DIV (yday1, Y400_DAYS) * 400; + int year1 = year0 + carried_years1; + int yday2 = MOD (yday1, Y400_DAYS); + int leap; + + if (overflow_sum_sign (tm.tm_year, pt->tmr.tm_year, years) + | overflow_sum_sign (tm.tm_mon, pt->tmr.tm_mon, mons) + | overflow_sum_sign (tm.tm_mday, pt->tmr.tm_mday, mdays) + | overflow_sum_sign (tm.tm_hour, pt->tmr.tm_hour, hours) + | overflow_sum_sign (tm.tm_min, pt->tmr.tm_min, mins) + | overflow_sum_sign (hours, carried_hours, hours1) + | overflow_sum_sign (mdays, carried_days, mdays1) + | overflow_sum_sign (years, carried_years0, year0) + | overflow_sum_sign (yday0, mdays1, yday1) + | overflow_sum_sign (year0, carried_years1, year1)) + return -1; + + for (;;) + { + int days_per_year = 365 + (leap = isleap (year1 + TM_YEAR_ORIGIN)); + if (yday2 < days_per_year) + break; + yday2 -= days_per_year; + year1++; + } + + tm.tm_year = year1; + + { + int mon; + for (mon = 11; + (tm.tm_mday = (yday2 - month_yday[mon] + (mon < 2 || !leap))) <= 0; + mon--) + continue; + tm.tm_mon = mon; + } + + tm.tm_hour = MOD (hours1, 24); + tm.tm_min = MOD (mins, 60); + + r = tm2time (&tm, localzone); + if (r == -1) + return r; + } + + /* Add the seconds' part of relative time. */ + { + time_t rs = r + pt->tmr.tm_sec; + if ((pt->tmr.tm_sec < 0) != (rs < r)) + return -1; + return rs; + } +} + +/* Parse a free-format date in *SOURCE, yielding a Unix format time. + Update *SOURCE to point to the first character after the date. + If *SOURCE is missing some information, take defaults from + DEFAULT_TIME and DEFAULT_ZONE. *SOURCE may even be the empty + string or an immediately invalid string, in which case the default + time and zone is used. + Return (time_t) -1 if the time is invalid or cannot be represented. */ +time_t +str2time (source, default_time, default_zone) + char const **source; + time_t default_time; + long default_zone; +{ + struct partime pt; + + *source = partime (*source, &pt); + if (pt.zone == TM_UNDEFINED_ZONE) + pt.zone = default_zone; + return maketime (&pt, default_time); +} + +#ifdef 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]) : TM_LOCAL_ZONE; + char buf[1000]; + while (fgets (buf, sizeof (buf), stdin)) + { + char const *p = buf; + time_t t = str2time (&p, default_time, default_zone); + printf ("`%.*s' -> %s", + (int) (p - buf - (p[0] == '\0' && p[-1] == '\n')), buf, + asctime ((argv[1] ? gmtime : localtime) (&t))); + } + return 0; +} +#endif diff --git a/src/apps/bin/patch/maketime.h b/src/apps/bin/patch/maketime.h new file mode 100644 index 0000000000..2f977c452c --- /dev/null +++ b/src/apps/bin/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/src/apps/bin/patch/partime.c b/src/apps/bin/patch/partime.c new file mode 100644 index 0000000000..da692233c0 --- /dev/null +++ b/src/apps/bin/patch/partime.c @@ -0,0 +1,956 @@ +/* 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 HAVE_STDDEF_H +# include +# endif +# if STDC_HEADERS +# include +# endif +# include +# ifdef __STDC__ +# define P(x) x +# else +# define P(x) () +# endif +#endif + +#ifndef offsetof +#define offsetof(aggregate, member) ((size_t) &((aggregate *) 0)->member) +#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 partime_id[] = + "$Id: partime.c,v 1.1 2004/06/16 21:22:10 korli 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 *, char const **, struct partime *)); +static char const *parse_ranged P ((char const *, int, int, int, int *)); +static char const *parse_varying P ((char const *, 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 RELATIVE_CONS(member, multiplier) \ + (offsetof (struct tm, member) + (multiplier) * sizeof (struct tm)) +#define RELATIVE_OFFSET(c) ((c) % sizeof (struct tm)) +#define RELATIVE_MULTIPLIER(c) ((c) / sizeof (struct tm)) +static struct name_val const relative_units[] = +{ + {"year", RELATIVE_CONS (tm_year, 1) }, + {"mont", RELATIVE_CONS (tm_mon , 1) }, + {"fort", RELATIVE_CONS (tm_mday, 14) }, + {"week", RELATIVE_CONS (tm_mday, 7) }, + {"day" , RELATIVE_CONS (tm_mday, 1) }, + {"hour", RELATIVE_CONS (tm_hour, 1) }, + {"min" , RELATIVE_CONS (tm_min , 1) }, + {"sec" , RELATIVE_CONS (tm_sec , 1) }, + {"", TM_UNDEFINED} +}; + +static struct name_val const ago[] = +{ + {"ago", 0}, + {"", TM_UNDEFINED} +}; + +static struct name_val const dst_names[] = +{ + {"dst", 1}, + {"", 0} +}; + +#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. + It's not worth keeping a complete list, since alphabetic time zone names + are deprecated and 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; + s++; + s += *s == '.'; + } + + 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->wday_ordinal = t->ymodulus = t->yweek + = TM_UNDEFINED; + t->tmr.tm_sec = t->tmr.tm_min = t->tmr.tm_hour = + t->tmr.tm_mday = t->tmr.tm_mon = t->tmr.tm_year = 0; + t->zone = TM_UNDEFINED_ZONE; +} + +/* Patterns to look for in a time 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 time_patterns[] = +{ + /* Traditional patterns come first, + to prevent an ISO 8601 format from misinterpreting their prefixes. */ + + /* RFC 822, extended */ + 'E', '_', 'N', '_', 'y', '$', 0, + 'x', 0, + + /* traditional */ + '4', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0, + 'R', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0, + 'E', '_', 'N', 0, + 'N', '_', 'E', '_', 'y', ';', 0, + 'N', '_', 'E', ';', 0, + 'N', 0, + 't', ':', 'm', ':', 's', '_', 'A', 0, + 't', ':', 'm', '_', 'A', 0, + 't', '_', 'A', 0, + + /* traditional get_date */ + 'i', '_', 'x', 0, + 'Y', '/', 'n', '/', 'E', ';', 0, + 'n', '/', 'E', '/', 'y', ';', 0, + 'n', '/', 'E', ';', 0, + 'u', 0, + + /* ISO 8601:1988 formats, generalized a bit. */ + 'y', '-', 'M', '-', 'D', '$', 0, + '4', 'M', 'D', '$', 0, + 'Y', '-', 'M', '$', 0, + 'R', 'M', 'D', '$', 0, + '-', 'R', '=', 'M', '$', 0, + '-', 'R', '$', 0, + '-', '-', 'M', '=', 'D', '$', 0, + 'M', '=', 'D', 'T', 0, + '-', '-', 'M', '$', 0, + '-', '-', '-', 'D', '$', 0, + 'D', 'T', 0, + 'Y', '-', 'd', '$', 0, + '4', 'd', '$', 0, + 'R', '=', 'd', '$', 0, + '-', 'd', '$', 0, + 'd', 'T', 0, + 'y', '-', 'W', '-', 'X', 0, + 'y', 'W', 'X', 0, + 'y', '=', 'W', 0, + '-', 'r', '-', 'W', '-', 'X', 0, + 'r', '-', 'W', '-', 'X', 'T', 0, + '-', 'r', 'W', 'X', 0, + 'r', 'W', 'X', 'T', 0, + '-', 'W', '=', 'X', 0, + 'W', '=', 'X', 'T', 0, + '-', 'W', 0, + '-', 'w', '-', 'X', 0, + 'w', '-', 'X', 'T', 0, + '-', '-', '-', 'X', '$', 0, + 'X', 'T', 0, + '4', '$', 0, + 'T', 0, + 'h', ':', 'm', ':', 's', '$', 0, + 'h', 'm', 's', '$', 0, + 'h', ':', 'L', '$', 0, + 'h', 'L', '$', 0, + 'H', '$', 0, + '-', 'm', ':', 's', '$', 0, + '-', 'm', 's', '$', 0, + '-', 'L', '$', 0, + '-', '-', 's', '$', 0, + 'Y', 0, + 'Z', 0, + + 0 +}; + +/* Parse an initial prefix of STR according to *PATTERNS, setting *T. + Return the first character after the prefix, or 0 if it couldn't be parsed. + *PATTERNS is a character array containing one pattern string after another; + it is terminated by an empty string. + If success, set *PATTERNS to the next pattern to try. + Set *PATTERNS to 0 if we know there are no more patterns to try; + if *PATTERNS is initially 0, give up immediately. */ +static char const * +parse_prefix (str, patterns, t) + char const *str; + char const **patterns; + struct partime *t; +{ + char const *pat = *patterns; + unsigned char c; + + if (! pat) + return 0; + + /* Remove initial noise. */ + while (! ISALNUM (c = *str) && c != '-' && c != '+') + { + if (! c) + { + undefine (t); + *patterns = 0; + return str; + } + + str++; + } + + /* Try a pattern until one succeeds. */ + while (*pat) + { + char const *s = str; + undefine (t); + + do + { + if (! (c = *pat++)) + { + *patterns = pat; + return s; + } + } + while ((s = parse_pattern_letter (s, c, t)) != 0); + + while (*pat++) + continue; + } + + 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 a possibly empty initial prefix of S. + Store the parsed number into *RES. + Return the first character after the prefix. */ +static char const * +parse_varying (s, res) + char const *s; + int *res; +{ + int n = 0; + for (;;) + { + unsigned d = *s - '0'; + if (9 < d) + break; + s++; + 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 const *s1; + char sign; + int hh, mm, ss; + int minutes_east_of_UTC; + int trailing_DST; + 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: + minutes_east_of_UTC = lookup (s, zone_names); + if (minutes_east_of_UTC == -1) + return 0; + + /* Don't bother to check rest of spelling, + but look for an embedded "DST". */ + trailing_DST = 0; + while (ISALPHA ((unsigned char) *s)) + { + if ((*s == 'D' || *s == 'd') && lookup (s, dst_names)) + trailing_DST = 1; + s++; + s += *s == '.'; + } + + /* Don't modify LT. */ + if (minutes_east_of_UTC == 1) + { + *zone = TM_LOCAL_ZONE; + return (char *) s; + } + + z = minutes_east_of_UTC * 60L; + s1 = s; + + /* Look for trailing "DST" or " DST". */ + while (ISSPACE ((unsigned char) *s)) + s++; + if (lookup (s, dst_names)) + { + while (ISALPHA ((unsigned char) *s)) + { + s++; + s += *s == '.'; + } + trailing_DST = 1; + } + + if (trailing_DST) + { + *zone = z + 60*60; + return (char *) s; + } + + s = s1; + + 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; +{ + char const *s0 = s; + + 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 ';': /* The next character must be a non-digit, and cannot be ':'. */ + if (ISDIGIT (*s) || *s == ':') + return 0; + 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; + } + s++; + s += *s == '.'; + switch (*s) + { + case 'M': + case 'm': + s++; + s += *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': /* traditional 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] */ + s = parse_ranged (s, 2, 0, 23, &t->tm.tm_hour); + 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 'i': /* ordinal day number, e.g. "3rd" */ + s = parse_varying (s, &t->wday_ordinal); + if (s == s0) + return 0; + while (ISALPHA ((unsigned char) *s)) + s++; + break; + + case 'L': /* 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 'm': /* minute [00-59] */ + s = parse_ranged (s, 2, 0, 59, &t->tm.tm_min); + break; + + case 'M': /* month [01-12] */ + s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon); + t->tm.tm_mon--; + break; + + case 'n': /* traditional month [1-9, 01-12] */ + s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12, + &t->tm.tm_mon); + t->tm.tm_mon--; + 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 '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 'u': /* relative unit */ + { + int i; + int n; + int negative = 0; + switch (*s) + { + case '-': negative = 1; + /* Fall through. */ + case '+': s++; + } + if (ISDIGIT (*s)) + s = parse_varying (s, &n); + else if (s == s0) + n = 1; + else + return 0; + if (negative) + n = -n; + while (!ISALNUM ((unsigned char) *s)) + s++; + i = lookup (s, relative_units); + if (!TM_DEFINED (i)) + return 0; + * (int *) ((char *) &t->tmr + RELATIVE_OFFSET (i)) + += n * RELATIVE_MULTIPLIER (i); + while (ISALPHA ((unsigned char) *s)) + s++; + while (! ISALNUM ((unsigned char) *s) && *s) + s++; + if (TM_DEFINED (lookup (s, ago))) + { + t->tmr.tm_sec = - t->tmr.tm_sec; + t->tmr.tm_min = - t->tmr.tm_min; + t->tmr.tm_hour = - t->tmr.tm_hour; + t->tmr.tm_mday = - t->tmr.tm_mday; + t->tmr.tm_mon = - t->tmr.tm_mon; + t->tmr.tm_year = - t->tmr.tm_year; + while (ISALPHA ((unsigned char) *s)) + s++; + } + 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] */ + s = parse_varying (s, &t->tm.tm_year); + if (s - s0 < 4) + return 0; + 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_wday) + || conflict (t->tm.tm_yday, 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_wday) + merge_ (t->tm.tm_yday, u->tm.tm_yday) + merge_ (t->ymodulus, u->ymodulus) + merge_ (t->yweek, u->yweek) +# undef merge_ + t->tmr.tm_sec += u->tmr.tm_sec; + t->tmr.tm_min += u->tmr.tm_min; + t->tmr.tm_hour += u->tmr.tm_hour; + t->tmr.tm_mday += u->tmr.tm_mday; + t->tmr.tm_mon += u->tmr.tm_mon; + t->tmr.tm_year += u->tmr.tm_year; + 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) + { + char const *patterns = time_patterns; + char const *s1; + + do + { + if (! (s1 = parse_prefix (s, &patterns, &p))) + return (char *) s; + } + while (merge_partime (t, &p) != 0); + + s = s1; + } + + return (char *) s; +} diff --git a/src/apps/bin/patch/partime.h b/src/apps/bin/patch/partime.h new file mode 100644 index 0000000000..1f4d31a8b1 --- /dev/null +++ b/src/apps/bin/patch/partime.h @@ -0,0 +1,77 @@ +/* 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_* members are used: + sec, min, hour, mday, mon, year, wday, yday. + If ! TM_DEFINED (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; + + /* Like tm, but values are relative to the value in tm, + and values are initialized to 0 rather than to TM_UNDEFINED. + Only the following tm_* members are used: + sec, min, hour, mday, mon, year. */ + struct tm tmr; + + /* If TM_DEFINED (wday_ordinal), + then day number (e.g. 3 in "3rd Sunday"). */ + int wday_ordinal; + + /* If TM_DEFINED (ymodulus), + then tm.tm_year is actually modulo ymodulus. */ + int ymodulus; + + /* Week of year, ISO 8601 style. + If ! TM_DEFINED (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/src/apps/bin/patch/patch.c b/src/apps/bin/patch/patch.c new file mode 100644 index 0000000000..07ced59d28 --- /dev/null +++ b/src/apps/bin/patch/patch.c @@ -0,0 +1,1366 @@ +/* patch - a program to apply diffs to original files */ + +/* $Id: patch.c,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +/* Copyright 1984, 1985-1987, 1988 Larry Wall + Copyright 1989, 1990-1993, 1997-1998, 1999 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 +#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 *, int)); +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 *, int, struct outstate *)); +static void init_reject PARAMS ((void)); +static void reinitialize_almost_everything PARAMS ((void)); +static void remove_if_needed PARAMS ((char const *, int volatile *)); +static void usage PARAMS ((FILE *, int)) __attribute__((noreturn)); + +static int make_backups; +static int backup_if_mismatch; +static char const *version_control; +static char const *version_control_context; +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 int volatile TMPREJNAME_needs_removal; + +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 (int argc, char **argv) +{ + char const *val; + bool somefailed = FALSE; + struct outstate outstate; + char numbuf[LINENUM_LENGTH_BOUND + 1]; + + init_time (); + + setbuf(stderr, serrbuf); + + xalloc_fail_func = memory_fatal; + bufsize = 8 * 1024; + buf = xmalloc (bufsize); + + strippath = -1; + + val = getenv ("QUOTING_STYLE"); + { + int i = val ? argmatch (val, quoting_style_args, 0, 0) : -1; + set_quoting_style ((struct quoting_options *) 0, + i < 0 ? shell_quoting_style : (enum quoting_style) i); + } + + 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); + + val = getenv ("SIMPLE_BACKUP_SUFFIX"); + if (val && *val) + simple_backup_suffix = val; + + if ((version_control = getenv ("PATCH_VERSION_CONTROL"))) + version_control_context = "$PATCH_VERSION_CONTROL"; + else if ((version_control = getenv ("VERSION_CONTROL"))) + version_control_context = "$VERSION_CONTROL"; + + /* 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(); + + if (make_backups | backup_if_mismatch) + backup_type = get_version (version_control_context, version_control); + + init_output (outfile, 0, &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) + { + int exclusive = TMPOUTNAME_needs_removal ? 0 : O_EXCL; + TMPOUTNAME_needs_removal = 1; + init_output (TMPOUTNAME, exclusive, &outstate); + } + + /* initialize reject file */ + init_reject (); + + /* 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 %s.\n", hunk, + format_linenum (numbuf, newwhere)); + } + else if (!where + || (where == 1 && pch_says_nonexistent (reverse) == 2 + && instat.st_size)) { + + if (where) + say ("Patch attempted to create file %s, which already exists.\n", + quotearg (inname)); + + abort_hunk(); + failed++; + if (verbosity != SILENT) + say ("Hunk #%d FAILED at %s.\n", hunk, + format_linenum (numbuf, newwhere)); + } + else if (! apply_hunk (&outstate, where)) { + abort_hunk (); + failed++; + if (verbosity != SILENT) + say ("Hunk #%d FAILED at %s.\n", hunk, + format_linenum (numbuf, newwhere)); + } else { + if (verbosity == VERBOSE + || (verbosity != SILENT && (fuzz || last_offset))) { + say ("Hunk #%d succeeded at %s", hunk, + format_linenum (numbuf, newwhere)); + if (fuzz) + say (" with fuzz %s", format_linenum (numbuf, fuzz)); + if (last_offset) + say (" (offset %s line%s)", + format_linenum (numbuf, last_offset), + "s" + (last_offset == 1)); + say (".\n"); + } + } + } + + if (!skip_rest_of_patch) + { + 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. */ + 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", quotearg (outname), + dry_run ? " and any empty ancestor directories" : ""); + if (! dry_run) + { + move_file ((char *) 0, (int *) 0, outname, (mode_t) 0, + (make_backups + || (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", + quotearg (outname)); + } + + if (! dry_run) + { + time_t t; + + move_file (TMPOUTNAME, &TMPOUTNAME_needs_removal, + outname, instat.st_mode, + (make_backups + || (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) != 2 + && (t = pch_timestamp (reverse)) != (time_t) -1 + && t != instat.st_mtime) + say ("Not setting time of file %s (time mismatch)\n", + quotearg (outname)); + else if (! force && (mismatch | failed)) + say ("Not setting time of file %s (contents mismatch)\n", + quotearg (outname)); + else if (utime (outname, &utimbuf) != 0) + pfatal ("Can't set timestamp on file %s", + quotearg (outname)); + } + + if (! inerrno && chmod (outname, instat.st_mode) != 0) + pfatal ("Can't set permissions on file %s", + quotearg (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 file %s", quotearg (rej)); + if (! dry_run) + { + move_file (TMPREJNAME, &TMPREJNAME_needs_removal, + 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", + quotearg (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 (void) +{ + 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, CHAR_MAX + 1}, + {"verbose", no_argument, NULL, CHAR_MAX + 2}, + {"binary", no_argument, NULL, CHAR_MAX + 3}, + {"help", no_argument, NULL, CHAR_MAX + 4}, + {"backup-if-mismatch", no_argument, NULL, CHAR_MAX + 5}, + {"no-backup-if-mismatch", no_argument, NULL, CHAR_MAX + 6}, + {"posix", no_argument, NULL, CHAR_MAX + 7}, + {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, + {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.", +"", +" --quoting-style=WORD output file names using quoting style WORD.", +" Valid WORDs are: literal, shell, shell-always, c, escape.", +" Default is taken from QUOTING_STYLE env variable, or 'shell' if unset.", +"", +"Backup and version control options:", +"", +" -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.", +"", +" -V STYLE --version-control=STYLE Use STYLE version control.", +" STYLE is either 'simple', 'numbered', or 'existing'.", +" -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 etc. 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.", +" --posix Conform to the POSIX standard.", +"", +" -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 (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 (void) +{ + 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': + make_backups = 1; + /* 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 `-b -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 `-b -z %s' instead\n", + optarg, optarg); + goto case_z; + } + 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 to directory %s", quotearg (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': + version_control = optarg; + version_control_context = "--version-control or -V option"; + 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 CHAR_MAX + 1: + dry_run = TRUE; + break; + case CHAR_MAX + 2: + verbosity = VERBOSE; + break; + case CHAR_MAX + 3: +#if HAVE_SETMODE + binary_transput = O_BINARY; +#endif + break; + case CHAR_MAX + 4: + usage (stdout, 0); + case CHAR_MAX + 5: + backup_if_mismatch = 1; + break; + case CHAR_MAX + 6: + backup_if_mismatch = 0; + break; + case CHAR_MAX + 7: + posixly_correct = 1; + break; + case CHAR_MAX + 8: + { + int i = argmatch (optarg, quoting_style_args, 0, 0); + if (i < 0) + { + invalid_arg ("quoting style", optarg, i); + usage (stderr, 2); + } + set_quoting_style ((struct quoting_options *) 0, + (enum quoting_style) i); + } + 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: %s: extra operand\n", + program_name, quotearg (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 (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, quotearg (string)); + + if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0)) + fatal ("%s %s is too large", argtype_msgid, quotearg (string)); + + value = next_value; + } + while (*++p); + + if (value < 0 && ! negative_allowed) + fatal ("%s %s is negative", argtype_msgid, quotearg (string)); + + return value; +} + +/* Attempt to find the right place to apply this hunk of patch. */ + +static LINENUM +locate_hunk (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; + if (last_frozen_line <= prefix_context + && offset <= max_pos_offset + && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz)) + { + last_offset = offset; + return first_guess + offset; + } + else + return 0; + } + + if (suffix_fuzz < 0) + { + /* Can only match end of file. */ + offset = first_guess - (input_lines - pat_lines + 1); + if (offset <= max_neg_offset + && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0)) + { + last_offset = - offset; + return first_guess - offset; + } + else + return 0; + } + + for (offset = 0; offset <= max_offset; offset++) { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1]; + if (offset <= max_pos_offset + && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) { + if (debug & 1) + say ("Offset changing from %s to %s\n", + format_linenum (numbuf0, last_offset), + format_linenum (numbuf1, 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 %s to %s\n", + format_linenum (numbuf0, last_offset), + format_linenum (numbuf1, -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 (void) +{ + 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++) { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1]; + switch (pch_char(i)) { + case '*': + if (oldlast < oldfirst) + fprintf(rejfp, "*** 0%s\n", stars); + else if (oldlast == oldfirst) + fprintf (rejfp, "*** %s%s\n", + format_linenum (numbuf0, oldfirst), stars); + else + fprintf (rejfp, "*** %s,%s%s\n", + format_linenum (numbuf0, oldfirst), + format_linenum (numbuf1, oldlast), stars); + break; + case '=': + if (newlast < newfirst) + fprintf(rejfp, "--- 0%s\n", minuses); + else if (newlast == newfirst) + fprintf (rejfp, "--- %s%s\n", + format_linenum (numbuf0, newfirst), minuses); + else + fprintf (rejfp, "--- %s,%s%s\n", + format_linenum (numbuf0, newfirst), + format_linenum (numbuf1, 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 (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)) { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1]; + if (debug & 1) + say ("oldchar = '%c', newchar = '%c'\n", + pch_char (old), pch_char (new)); + fatal ("Out-of-sync patch, lines %s,%s -- mangled text or line numbers, maybe?", + format_linenum (numbuf0, pch_hunk_beg() + old), + format_linenum (numbuf1, 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 (char const *name, int open_flags) +{ + int fd = create_file (name, O_WRONLY | binary_transput | open_flags, + instat.st_mode); + FILE *f = fdopen (fd, binary_transput ? "wb" : "w"); + if (! f) + pfatal ("Can't create file %s", quotearg (name)); + return f; +} + +/* Open the new file. */ + +static void +init_output (char const *name, int open_flags, struct outstate *outstate) +{ + outstate->ofp = name ? create_output_file (name, open_flags) : (FILE *) 0; + outstate->after_newline = 1; + outstate->zero_output = 1; +} + +/* Open a file to put hunks we can't locate. */ + +static void +init_reject (void) +{ + int exclusive = TMPREJNAME_needs_removal ? 0 : O_EXCL; + TMPREJNAME_needs_removal = 1; + rejfp = create_output_file (TMPREJNAME, exclusive); +} + +/* Copy input file to output, up to wherever hunk is to be applied. */ + +static bool +copy_till (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 (struct outstate *outstate) +{ + if (debug & 256) + { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1]; + say ("il=%s lfl=%s\n", + format_linenum (numbuf0, input_lines), + format_linenum (numbuf1, 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 (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 (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 (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 (int sig) +{ + cleanup (); + + if (sig) + exit_with_signal (sig); + + exit (2); +} + +static void +remove_if_needed (char const *name, int volatile *needs_removal) +{ + if (*needs_removal) + { + unlink (name); + *needs_removal = 0; + } +} + +static void +cleanup (void) +{ + remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal); + remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); + remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); + remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); +} diff --git a/src/apps/bin/patch/patch.man b/src/apps/bin/patch/patch.man new file mode 100644 index 0000000000..b5ded6188e --- /dev/null +++ b/src/apps/bin/patch/patch.man @@ -0,0 +1,1149 @@ +.\" patch man page +.de Id +.ds Dt \\$4 +.. +.Id $Id: patch.man,v 1.1 2004/06/16 21:22:10 korli 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 \-b +or +.B \*=backup +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, +or if a context diff contains lines ending in \s-1CRLF\s0 +or is encapsulated one or more times by prepending +"\fB\- \fP" to lines starting with "\fB\-\fP" as specified by Internet RFC 934, +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. +.LP +First, +.B patch +takes an ordered list of candidate file names as follows: +.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. +A name is ignored if it does not have enough slashes to satisfy the +.BI \-p num +or +.BI \*=strip= num +option. +The name +.B /dev/null +is also 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 if +.B patch +is conforming to \s-1POSIX\s0, +.B patch +takes the name in the +.B Index:\& +line. +.TP +.B " \(bu" +For the purpose of the following rules, +the candidate file names are considered to be in the order (old, new, index), +regardless of the order that they appear in the header. +.LP +Then +.B patch +selects a file name from the candidate list as follows: +.TP 3 +.B " \(bu" +If some of the named files exist, +.B patch +selects the first name if conforming to \s-1POSIX\s0, +and the best name otherwise. +.TP +.B " \(bu" +If +.B patch +is not ignoring \s-1RCS\s0, ClearCase, and \s-1SCCS\s0 (see the +.BI "\-g\ " num +or +.BI \*=get= num +option), and no named files exist +but an \s-1RCS\s0, ClearCase, or \s-1SCCS\s0 master is found, +.B patch +selects the first named file +with an \s-1RCS\s0, ClearCase, or \s-1SCCS\s0 master. +.TP +.B " \(bu" +If no named files exist, +no \s-1RCS\s0, ClearCase, or \s-1SCCS\s0 master was found, +some names are given, +.B patch +is not conforming to \s-1POSIX\s0, +and the patch appears to create a file, +.B patch +selects 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, and +.B patch +selects that name. +.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. +See the +.B \-V +or +.B \*=version\-control +option for details about how backup file names are determined. +.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. +This is the default unless +.B patch +is conforming to \s-1POSIX\s0. +.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 +.B patch +is conforming to \s-1POSIX\s0. +.TP +\fB\-B\fP \fIpref\fP or \fB\*=prefix=\fP\fIpref\fP +Prefix +.I pref +to a file name when generating its simple backup file name. +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-conforming 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 +.B patch +is conforming to \s-1POSIX\s0, +.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, +or when a file is under ClearCase control and does not exist. +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, ClearCase, 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 patch +is conforming to \s-1POSIX\s0, 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 +.B \*=posix +Conform more strictly to the \s-1POSIX\s0 standard, as follows. +.RS +.TP 3 +.B " \(bu" +Take the first existing file from the list (old, new, index) +when intuiting file names from diff headers. +.TP +.B " \(bu" +Do not remove files that are empty after patching. +.TP +.B " \(bu" +Do not ask whether to get files from \s-1RCS\s0, ClearCase, or \s-1SCCS\s0. +.TP +.B " \(bu" +Require that all options precede the files in the command line. +.TP +.B " \(bu" +Do not backup files when there is a mismatch. +.RE +.TP +.BI \*=quoting\-style= word +Use style +.I word +to quote output names. +The +.I word +should be one of the following: +.RS +.TP +.B literal +Output names as-is. +.TP +.B shell +Quote names for the shell if they contain shell metacharacters or would +cause ambiguous output. +.TP +.B shell-always +Quote names for the shell, even if they would normally not require quoting. +.TP +.B c +Quote names as for a C language string. +.TP +.B escape +Quote as with +.B c +except omit the surrounding double-quote characters. +.LP +You can specify the default value of the +.B \*=quoting\-style +option with the environment variable +.BR QUOTING_STYLE . +If that environment variable is not set, the default value is +.BR shell . +.RE +.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. +The method does not affect whether backup files are made; +it affects only the names of any backup files that are made. +.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. +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 +.I pref +to the basename of a file name when generating its simple backup file name. +For example, with +.B "\-Y\ .del/" +the simple 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. +For example, with +.B "\-z\ -" +the simple backup file name for +.B src/patch/util.c +is +.BR src/patch/util.c- . +The backup suffix 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 +.B PATCH_GET +This specifies whether +.B patch +gets missing or read-only files from \s-1RCS\s0, ClearCase, 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 by default: +see the +.B \*=posix +option. +.TP +.B QUOTING_STYLE +Default value of the +.B \*=quoting\-style +option. +.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) +.Sp +Marshall T. Rose and Einar A. Stefferud, +Proposed Standard for Message Encapsulation, +Internet RFC 934 (1985-01). +.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 +.B patch +is conforming to \s-1POSIX\s0 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 do not conform to \s-1POSIX\s0. +.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 , +backups were enabled by default. +This behavior is now enabled with the +.B \-b +or +.B \*=backup +option. +.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 conforming to \s-1POSIX\s0 with the +.B \*=posix +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 did not conform to \s-1POSIX\s0, and had a few gotchas. +Now +.B patch +uses a different, equally complicated (but better documented) method +that is optionally \s-1POSIX\s0-conforming; 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 +.B patch +that conforms to \s-1POSIX\s0. +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 +Please report bugs via email to +.BR . +.PP +.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 +1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998 +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/src/apps/bin/patch/patch.rdef b/src/apps/bin/patch/patch.rdef new file mode 100644 index 0000000000..969d56db9e --- /dev/null +++ b/src/apps/bin/patch/patch.rdef @@ -0,0 +1,11 @@ +resource app_version +{ + major = 2, + middle = 5, + minor = 4, + variety = 5, + internal = 0, + short_info = "2.5.4", + long_info = "2.5.4 ©1999 The Free Software Foundation" +}; + diff --git a/src/apps/bin/patch/patchlevel.h b/src/apps/bin/patch/patchlevel.h new file mode 100644 index 0000000000..5fa53213cc --- /dev/null +++ b/src/apps/bin/patch/patchlevel.h @@ -0,0 +1 @@ +#define PATCH_VERSION "2.5.4" diff --git a/src/apps/bin/patch/pch.c b/src/apps/bin/patch/pch.c new file mode 100644 index 0000000000..3e9f80cc37 --- /dev/null +++ b/src/apps/bin/patch/pch.c @@ -0,0 +1,1913 @@ +/* reading patches */ + +/* $Id: pch.c,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +/* Copyright 1986, 1987, 1988 Larry Wall + Copyright 1990, 1991-1993, 1997-1998, 1999 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 +#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: + 0 for existent and nonempty, + 1 for existent and probably (but not necessarily) empty, + 2 for nonexistent */ +static int p_rfc934_nesting; /* RFC 934 nesting level */ +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 int p_strip_trailing_cr; /* nonzero if stripping trailing \r */ +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 char *scan_linenum PARAMS ((char *, LINENUM *)); +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, int, 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)) __attribute__ ((noreturn)); +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 (void) +{ + p_first = 0; + p_newfirst = 0; + p_ptrn_lines = 0; + p_repl_lines = 0; + p_end = -1; + p_max = 0; + p_indent = 0; + p_strip_trailing_cr = 0; +} + +/* Open the patch file at the beginning of time. */ + +void +open_patch_file (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; + int exclusive = TMPPATNAME_needs_removal ? 0 : O_EXCL; + TMPPATNAME_needs_removal = 1; + pfp = fdopen (create_file (TMPPATNAME, + O_RDWR | O_BINARY | exclusive, + (mode_t) 0), + "w+b"); + if (!pfp) + pfatal ("Can't open stream for file %s", quotearg (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", quotearg (filename)); + if (fstat (fileno (pfp), &st) != 0) + pfatal ("fstat"); + } + p_filesize = st.st_size; + if (p_filesize != (file_offset) p_filesize) + fatal ("patch file is too long"); + next_intuit_at (file_pos, (LINENUM) 1); + set_hunkmax(); +} + +/* Make sure our dynamically realloced tables are malloced to begin with. */ + +void +set_hunkmax (void) +{ + 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 (void) +{ + 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 (void) +{ + 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"); + if (! p_base && p_filesize) + fatal ("Only garbage was found in the patch input."); + 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 (p_strip_trailing_cr) + say ("(Stripping trailing CRs from patch.)\n"); + if (! inname) + { + char numbuf[LINENUM_LENGTH_BOUND + 1]; + say ("can't find file to patch at input line %s\n", + format_linenum (numbuf, p_sline)); + say (strippath == -1 + ? "Perhaps you should have used the -p or --strip option?\n" + : "Perhaps you used the wrong -p or --strip option?\n"); + } + } + + 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 (void) +{ + register file_offset this_line = 0; + register file_offset first_command_line = -1; + LINENUM fcl_line = 0; /* Pacify `gcc -W'. */ + register bool this_is_a_command = 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_rfc934_nesting = 0; + 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 (;;) { + register char *s; + register char *t; + register file_offset previous_line = this_line; + register bool last_line_was_command = this_is_a_command; + register bool stars_last_line = stars_this_line; + register int indent = 0; + int strip_trailing_cr; + size_t chars_read; + + this_line = file_tell (pfp); + chars_read = pget_line (0, 0, 0); + if (chars_read == (size_t) -1) + memory_fatal (); + if (! chars_read) { + 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; + return NO_DIFF; + } + } + strip_trailing_cr = 2 <= chars_read && buf[chars_read - 2] == '\r'; + 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 */ + p_strip_trailing_cr = strip_trailing_cr; + } + if (!stars_last_line && strnEQ(s, "*** ", 4)) + name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); + 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; + } + } else + { + for (t = s; t[0] == '-' && t[1] == ' '; t += 2) + continue; + if (strnEQ(t, "--- ", 4)) + { + time_t timestamp = (time_t) -1; + name[NEW] = fetchname (t+4, strippath, ×tamp); + if (timestamp != (time_t) -1) + { + p_timestamp[NEW] = timestamp; + p_rfc934_nesting = (t - s) >> 1; + } + } + } + if ((diff_type == NO_DIFF || diff_type == ED_DIFF) && + first_command_line >= 0 && + strEQ(s, ".\n") ) { + p_indent = indent; + p_strip_trailing_cr = strip_trailing_cr; + 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 (s[0] == '0' && !ISDIGIT (s[1])) + p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; + while (*s != ' ' && *s != '\n') + s++; + while (*s == ' ') + s++; + if (s[0] == '+' && s[1] == '0' && !ISDIGIT (s[2])) + p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; + p_indent = indent; + p_strip_trailing_cr = strip_trailing_cr; + p_start = this_line; + p_sline = p_input_line; + retval = UNI_DIFF; + if (! ((name[OLD] || ! p_timestamp[OLD]) + && (name[NEW] || ! p_timestamp[NEW]))) + { + char numbuf[LINENUM_LENGTH_BOUND + 1]; + say ("missing header for unified diff at line %s of patch\n", + format_linenum (numbuf, 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 (s[0] == '0' && !ISDIGIT (s[1])) + 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_strip_trailing_cr = strip_trailing_cr; + 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]))) + { + char numbuf[LINENUM_LENGTH_BOUND + 1]; + say ("missing header for context diff at line %s of patch\n", + format_linenum (numbuf, 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; + p_strip_trailing_cr = strip_trailing_cr; + 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) + { + int is_empty; + + 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); + if (diffbuf) + free (diffbuf); + + if (! stat_errno[i]) + break; + } + } + + nope = i; + } + } + + is_empty = i == NONE || st[i].st_size == 0; + if ((! is_empty) < p_says_nonexistent[reverse ^ is_empty]) + { + 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"), + quotearg (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 (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 (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_MAX; + size_t len[3]; + size_t len_min = SIZE_MAX; + + 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_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_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 (void) +{ + char numbuf[LINENUM_LENGTH_BOUND + 1]; + fatal ("malformed patch at line %s: %s", + format_linenum (numbuf, p_input_line), buf); + /* about as informative as "Syntax error" in C */ +} + +/* Parse a line number from a string. + Return the address of the first char after the number. */ +static char * +scan_linenum (char *s0, LINENUM *linenum) +{ + char *s; + LINENUM n = 0; + int overflow = 0; + char numbuf[LINENUM_LENGTH_BOUND + 1]; + + for (s = s0; ISDIGIT (*s); s++) + { + LINENUM new_n = 10 * n + (*s - '0'); + overflow |= new_n / 10 != n; + n = new_n; + } + + if (s == s0) + fatal ("missing line number at line %s: %s", + format_linenum (numbuf, p_input_line), buf); + + if (overflow) + fatal ("line number %.*s is too large at line %s: %s", + (int) (s - s0), s0, format_linenum (numbuf, p_input_line), buf); + + *linenum = n; + return s; +} + +/* 1 if there is more of the current diff listing to process; + 0 if not; -1 if ran out of memory. */ + +int +another_hunk (enum diff difftype, int rev) +{ + register char *s; + register LINENUM context = 0; + register size_t chars_read; + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1]; + char numbuf2[LINENUM_LENGTH_BOUND + 1]; + char numbuf3[LINENUM_LENGTH_BOUND + 1]; + + 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 %s; giving up at line %s: %s", + format_linenum (numbuf0, pch_hunk_beg ()), + format_linenum (numbuf1, 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 %s", + format_linenum (numbuf0, p_input_line)); + } + if (p_end != 0) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal ("unexpected `***' at line %s: %s", + format_linenum (numbuf0, 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 (strnEQ(s,"0,0",3)) + remove_prefix (s, 2); + s = scan_linenum (s, &p_first); + if (*s == ',') { + while (*s && !ISDIGIT (*s)) + s++; + scan_linenum (s, &p_ptrn_lines); + p_ptrn_lines += 1 - p_first; + } + 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 %s; check line numbers at line %s", + (p_end <= p_ptrn_lines + ? "Premature" + : "Overdue"), + format_linenum (numbuf0, p_input_line), + format_linenum (numbuf1, p_hunk_beg)); + else if (! repl_could_be_missing) + fatal ("duplicate `---' at line %s; check line numbers at line %s", + format_linenum (numbuf0, p_input_line), + format_linenum (numbuf1, + 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; + s = scan_linenum (s, &p_newfirst); + if (*s == ',') + { + do + { + if (!*++s) + malformed (); + } + while (! ISDIGIT (*s)); + scan_linenum (s, &p_repl_lines); + p_repl_lines += 1 - p_newfirst; + } + 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 %s", + format_linenum (numbuf0, 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 %s", + format_linenum (numbuf0, 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 %s", + format_linenum (numbuf0, p_hunk_beg)); + fillsrc++; + } + if (debug & 64) + printf ("fillsrc %s, filldst %s, rb %s, e+1 %s\n", + format_linenum (numbuf0, fillsrc), + format_linenum (numbuf1, filldst), + format_linenum (numbuf2, repl_beginning), + format_linenum (numbuf3, 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 = scan_linenum (buf + 4, &p_first); + if (*s == ',') + s = scan_linenum (s + 1, &p_ptrn_lines); + else + p_ptrn_lines = 1; + if (*s == ' ') s++; + if (*s != '+') + malformed (); + s = scan_linenum (s + 1, &p_newfirst); + if (*s == ',') + s = scan_linenum (s + 1, &p_repl_lines); + 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, "*** %s,%s ****\n", + format_linenum (numbuf0, p_first), + format_linenum (numbuf1, 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, "--- %s,%s ----\n", + format_linenum (numbuf0, p_newfirst), + format_linenum (numbuf1, 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; + } + s = scan_linenum (buf, &p_first); + if (*s == ',') { + s = scan_linenum (s + 1, &p_ptrn_lines); + p_ptrn_lines += 1 - p_first; + } + else + p_ptrn_lines = (*s != 'a'); + hunk_type = *s; + if (hunk_type == 'a') + p_first++; /* do append rather than insert */ + s = scan_linenum (s + 1, &min); + if (*s == ',') + scan_linenum (s + 1, &max); + 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, "*** %s,%s\n", + format_linenum (numbuf0, p_first), + format_linenum (numbuf1, 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 %s", + format_linenum (numbuf0, p_input_line)); + if (buf[0] != '<' || (buf[1] != ' ' && buf[1] != '\t')) + fatal ("`<' expected at line %s of patch", + format_linenum (numbuf0, 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 %s", + format_linenum (numbuf0, p_input_line)); + if (*buf != '-') + fatal ("`---' expected at line %s of patch", + format_linenum (numbuf0, p_input_line)); + } + sprintf (buf, "--- %s,%s\n", + format_linenum (numbuf0, min), + format_linenum (numbuf1, 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 %s", + format_linenum (numbuf0, p_input_line)); + if (buf[0] != '>' || (buf[1] != ' ' && buf[1] != '\t')) + fatal ("`>' expected at line %s of patch", + format_linenum (numbuf0, 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, "%s %c %c ", format_linenum (numbuf0, 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 (void) +{ + return pget_line (p_indent, p_rfc934_nesting, p_strip_trailing_cr); +} + +/* Input a line from the patch file, worrying about indentation. + Strip up to INDENT characters' worth of leading indentation. + Then remove up to RFC934_NESTING instances of leading "- ". + If STRIP_TRAILING_CR is nonzero, remove any trailing carriage-return. + 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 (int indent, int rfc934_nesting, int strip_trailing_cr) +{ + 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; + + while (c == '-' && 0 <= --rfc934_nesting) + { + c = getc (fp); + if (c == EOF) + goto patch_ends_in_middle_of_line; + if (c != ' ') + { + i = 1; + b[0] = '-'; + break; + } + c = getc (fp); + if (c == EOF) + goto patch_ends_in_middle_of_line; + } + + 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) + goto patch_ends_in_middle_of_line; + } + + if (strip_trailing_cr && 2 <= i && b[i - 2] == '\r') + b[i-- - 2] = '\n'; + b[i] = '\0'; + p_input_line++; + return i; + + patch_ends_in_middle_of_line: + if (ferror (fp)) + read_fatal (); + say ("patch unexpectedly ends in middle of line\n"); + return 0; +} + +static bool +incomplete_line (void) +{ + 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 (void) +{ + 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 (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 (int which) +{ + return p_timestamp[which]; +} + +/* Return the specified line position in the old file of the old context. */ + +LINENUM +pch_first (void) +{ + return p_first; +} + +/* Return the number of lines of old context. */ + +LINENUM +pch_ptrn_lines (void) +{ + return p_ptrn_lines; +} + +/* Return the probable line position in the new file of the first line. */ + +LINENUM +pch_newfirst (void) +{ + return p_newfirst; +} + +/* Return the number of lines in the replacement text including context. */ + +LINENUM +pch_repl_lines (void) +{ + return p_repl_lines; +} + +/* Return the number of lines in the whole hunk. */ + +LINENUM +pch_end (void) +{ + return p_end; +} + +/* Return the number of context lines before the first changed line. */ + +LINENUM +pch_prefix_context (void) +{ + return p_prefix_context; +} + +/* Return the number of context lines after the last changed line. */ + +LINENUM +pch_suffix_context (void) +{ + return p_suffix_context; +} + +/* Return the length of a particular patch line. */ + +size_t +pch_line_len (LINENUM line) +{ + return p_len[line]; +} + +/* Return the control character (+, -, *, !, etc) for a patch line. */ + +char +pch_char (LINENUM line) +{ + return p_Char[line]; +} + +/* Return a pointer to a particular patch line. */ + +char * +pfetch (LINENUM line) +{ + return p_line[line]; +} + +/* Output a patch line. */ + +bool +pch_write_line (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 (void) +{ + return p_hunk_beg; +} + +/* Apply an ed script by feeding ed itself. */ + +void +do_ed_script (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) { + int exclusive = TMPOUTNAME_needs_removal ? 0 : O_EXCL; + assert (! inerrno); + TMPOUTNAME_needs_removal = 1; + copy_file (inname, TMPOUTNAME, exclusive, 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", quotearg (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/src/apps/bin/patch/pch.h b/src/apps/bin/patch/pch.h new file mode 100644 index 0000000000..0e8b87c5b2 --- /dev/null +++ b/src/apps/bin/patch/pch.h @@ -0,0 +1,25 @@ +/* reading patches */ + +/* $Id: pch.h,v 1.1 2004/06/16 21:22:10 korli 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/src/apps/bin/patch/quotearg.c b/src/apps/bin/patch/quotearg.c new file mode 100644 index 0000000000..5ac903ca1b --- /dev/null +++ b/src/apps/bin/patch/quotearg.c @@ -0,0 +1,403 @@ +/* quotearg.c - quote arguments for output + Copyright (C) 1998, 1999 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. */ + +/* Written by Paul Eggert */ + +/* FIXME: Multibyte characters are not supported yet. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include +#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII)) +# define ISASCII(c) 1 +#else +# define ISASCII(c) isascii (c) +#endif +#ifdef isgraph +# define ISGRAPH(c) (ISASCII (c) && isgraph (c)) +#else +# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) +#endif + +#if ENABLE_NLS +# include +# define _(text) gettext (text) +#else +# define _(text) text +#endif + +#if HAVE_LIMITS_H +# include +#endif +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif +#ifndef UCHAR_MAX +# define UCHAR_MAX ((unsigned char) -1) +#endif + +#if HAVE_STDLIB_H +# include +#endif + +#if HAVE_STRING_H +# include +#endif + +#define INT_BITS (sizeof (int) * CHAR_BIT) + +struct quoting_options +{ + /* Basic quoting style. */ + enum quoting_style style; + + /* Quote the characters indicated by this bit vector even if the + quoting style would not normally require them to be quoted. */ + int quote_these_too[((UCHAR_MAX + 1) / INT_BITS + + ((UCHAR_MAX + 1) % INT_BITS != 0))]; +}; + +/* Names of quoting styles. */ +char const *const quoting_style_args[] = +{ + "literal", + "shell", + "shell-always", + "c", + "escape", + "locale", + 0 +}; + +/* Correspondences to quoting style names. */ +enum quoting_style const quoting_style_vals[] = +{ + literal_quoting_style, + shell_quoting_style, + shell_always_quoting_style, + c_quoting_style, + escape_quoting_style, + locale_quoting_style +}; + +/* The default quoting options. */ +static struct quoting_options default_quoting_options; + +/* Allocate a new set of quoting options, with contents initially identical + to O if O is not null, or to the default if O is null. + It is the caller's responsibility to free the result. */ +struct quoting_options * +clone_quoting_options (struct quoting_options *o) +{ + struct quoting_options *p + = (struct quoting_options *) xmalloc (sizeof (struct quoting_options)); + *p = *(o ? o : &default_quoting_options); + return p; +} + +/* Get the value of O's quoting style. If O is null, use the default. */ +enum quoting_style +get_quoting_style (struct quoting_options *o) +{ + return (o ? o : &default_quoting_options)->style; +} + +/* In O (or in the default if O is null), + set the value of the quoting style to S. */ +void +set_quoting_style (struct quoting_options *o, enum quoting_style s) +{ + (o ? o : &default_quoting_options)->style = s; +} + +/* In O (or in the default if O is null), + set the value of the quoting options for character C to I. + Return the old value. Currently, the only values defined for I are + 0 (the default) and 1 (which means to quote the character even if + it would not otherwise be quoted). */ +int +set_char_quoting (struct quoting_options *o, char c, int i) +{ + unsigned char uc = c; + int *p = (o ? o : &default_quoting_options)->quote_these_too + uc / INT_BITS; + int shift = uc % INT_BITS; + int r = (*p >> shift) & 1; + *p ^= ((i & 1) ^ r) << shift; + return r; +} + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using O to control quoting. + If O is null, use the default. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is -1, use the string length of the argument for ARGSIZE. */ +size_t +quotearg_buffer (char *buffer, size_t buffersize, + char const *arg, size_t argsize, + struct quoting_options const *o) +{ + unsigned char c; + size_t i; + size_t len = 0; + char const *quote_string; + size_t quote_string_len; + struct quoting_options const *p = o ? o : &default_quoting_options; + enum quoting_style quoting_style = p->style; +#define STORE(c) \ + do \ + { \ + if (len < buffersize) \ + buffer[len] = (c); \ + len++; \ + } \ + while (0) + + switch (quoting_style) + { + case shell_quoting_style: + if (! (argsize == (size_t) -1 ? arg[0] == '\0' : argsize == 0)) + { + switch (arg[0]) + { + case '#': case '~': + break; + + default: + for (i = 0; ; i++) + { + if (argsize == (size_t) -1 ? arg[i] == '\0' : i == argsize) + goto done; + + c = arg[i]; + + switch (c) + { + case '\t': case '\n': case ' ': + case '!': /* special in csh */ + case '"': case '$': case '&': case '\'': + case '(': case ')': case '*': case ';': + case '<': case '>': case '?': case '[': case '\\': + case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */ + case '`': case '|': + goto needs_quoting; + } + + if (p->quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))) + goto needs_quoting; + + STORE (c); + } + needs_quoting:; + + len = 0; + break; + } + } + /* Fall through. */ + + case shell_always_quoting_style: + STORE ('\''); + quote_string = "'"; + quote_string_len = 1; + break; + + case c_quoting_style: + STORE ('"'); + quote_string = "\""; + quote_string_len = 1; + break; + + case locale_quoting_style: + for (quote_string = _("`"); *quote_string; quote_string++) + STORE (*quote_string); + quote_string = _("'"); + quote_string_len = strlen (quote_string); + break; + + default: + quote_string = 0; + quote_string_len = 0; + break; + } + + for (i = 0; ! (argsize == (size_t) -1 ? arg[i] == '\0' : i == argsize); i++) + { + c = arg[i]; + + switch (quoting_style) + { + case literal_quoting_style: + break; + + case shell_quoting_style: + case shell_always_quoting_style: + if (c == '\'') + { + STORE ('\''); + STORE ('\\'); + STORE ('\''); + } + break; + + case c_quoting_style: + case escape_quoting_style: + case locale_quoting_style: + switch (c) + { + case '?': /* Do not generate trigraphs. */ + case '\\': goto store_escape; + /* Not all C compilers know what \a means. */ + case 7 : c = 'a'; goto store_escape; + case '\b': c = 'b'; goto store_escape; + case '\f': c = 'f'; goto store_escape; + case '\n': c = 'n'; goto store_escape; + case '\r': c = 'r'; goto store_escape; + case '\t': c = 't'; goto store_escape; + case '\v': c = 'v'; goto store_escape; + + case ' ': break; + + default: + if (quote_string_len + && strncmp (arg + i, quote_string, quote_string_len) == 0) + goto store_escape; + if (!ISGRAPH (c)) + { + STORE ('\\'); + STORE ('0' + (c >> 6)); + STORE ('0' + ((c >> 3) & 7)); + c = '0' + (c & 7); + goto store_c; + } + break; + } + + if (! (p->quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS)))) + goto store_c; + + store_escape: + STORE ('\\'); + } + + store_c: + STORE (c); + } + + if (quote_string) + for (; *quote_string; quote_string++) + STORE (*quote_string); + + done: + if (len < buffersize) + buffer[len] = '\0'; + return len; +} + +/* Use storage slot N to return a quoted version of the string ARG. + OPTIONS specifies the quoting options. + The returned value points to static storage that can be + reused by the next call to this function with the same value of N. + N must be nonnegative. N is deliberately declared with type `int' + to allow for future extensions (using negative values). */ +static char * +quotearg_n_options (int n, char const *arg, + struct quoting_options const *options) +{ + static unsigned int nslots; + static struct slotvec + { + size_t size; + char *val; + } *slotvec; + + if (nslots <= n) + { + int n1 = n + 1; + size_t s = n1 * sizeof (struct slotvec); + if (! (0 < n1 && n1 == s / sizeof (struct slotvec))) + abort (); + slotvec = (struct slotvec *) xrealloc (slotvec, s); + memset (slotvec + nslots, 0, (n1 - nslots) * sizeof (struct slotvec)); + nslots = n; + } + + { + size_t size = slotvec[n].size; + char *val = slotvec[n].val; + size_t qsize = quotearg_buffer (val, size, arg, (size_t) -1, options); + + if (size <= qsize) + { + slotvec[n].size = size = qsize + 1; + slotvec[n].val = val = xrealloc (val, size); + quotearg_buffer (val, size, arg, (size_t) -1, options); + } + + return val; + } +} + +char * +quotearg_n (unsigned int n, char const *arg) +{ + return quotearg_n_options (n, arg, &default_quoting_options); +} + +char * +quotearg (char const *arg) +{ + return quotearg_n (0, arg); +} + +char * +quotearg_n_style (unsigned int n, enum quoting_style s, char const *arg) +{ + struct quoting_options o; + o.style = s; + memset (o.quote_these_too, 0, sizeof o.quote_these_too); + return quotearg_n_options (n, arg, &o); +} + +char * +quotearg_style (enum quoting_style s, char const *arg) +{ + return quotearg_n_style (0, s, arg); +} + +char * +quotearg_char (char const *arg, char ch) +{ + struct quoting_options options; + options = default_quoting_options; + set_char_quoting (&options, ch, 1); + return quotearg_n_options (0, arg, &options); +} + +char * +quotearg_colon (char const *arg) +{ + return quotearg_char (arg, ':'); +} diff --git a/src/apps/bin/patch/quotearg.h b/src/apps/bin/patch/quotearg.h new file mode 100644 index 0000000000..2a600e7a67 --- /dev/null +++ b/src/apps/bin/patch/quotearg.h @@ -0,0 +1,109 @@ +/* quotearg.h - quote arguments for output + Copyright (C) 1998, 1999 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. */ + +/* Written by Paul Eggert */ + +/* Basic quoting styles. */ +enum quoting_style + { + literal_quoting_style, /* --quoting-style=literal */ + shell_quoting_style, /* --quoting-style=shell */ + shell_always_quoting_style, /* --quoting-style=shell-always */ + c_quoting_style, /* --quoting-style=c */ + escape_quoting_style, /* --quoting-style=escape */ + locale_quoting_style /* --quoting-style=locale */ + }; + +/* For now, --quoting-style=literal is the default, but this may change. */ +#ifndef DEFAULT_QUOTING_STYLE +# define DEFAULT_QUOTING_STYLE literal_quoting_style +#endif + +/* Names of quoting styles and their corresponding values. */ +extern char const *const quoting_style_args[]; +extern enum quoting_style const quoting_style_vals[]; + +struct quoting_options; + +#ifndef PARAMS +# if defined PROTOTYPES || defined __STDC__ +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +#endif + +/* The functions listed below set and use a hidden variable + that contains the default quoting style options. */ + +/* Allocate a new set of quoting options, with contents initially identical + to O if O is not null, or to the default if O is null. + It is the caller's responsibility to free the result. */ +struct quoting_options *clone_quoting_options + PARAMS ((struct quoting_options *o)); + +/* Get the value of O's quoting style. If O is null, use the default. */ +enum quoting_style get_quoting_style PARAMS ((struct quoting_options *o)); + +/* In O (or in the default if O is null), + set the value of the quoting style to S. */ +void set_quoting_style PARAMS ((struct quoting_options *o, + enum quoting_style s)); + +/* In O (or in the default if O is null), + set the value of the quoting options for character C to I. + Return the old value. Currently, the only values defined for I are + 0 (the default) and 1 (which means to quote the character even if + it would not otherwise be quoted). */ +int set_char_quoting PARAMS ((struct quoting_options *o, char c, int i)); + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using O to control quoting. + If O is null, use the default. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is -1, use the string length of the argument for ARGSIZE. */ +size_t quotearg_buffer PARAMS ((char *buffer, size_t buffersize, + char const *arg, size_t argsize, + struct quoting_options const *o)); + +/* Use storage slot N to return a quoted version of the string ARG. + Use the default quoting options. + The returned value points to static storage that can be + reused by the next call to this function with the same value of N. + N must be nonnegative. */ +char *quotearg_n PARAMS ((unsigned int n, char const *arg)); + +/* Equivalent to quotearg_n (0, ARG). */ +char *quotearg PARAMS ((char const *arg)); + +/* Use style S and storage slot N to return a quoted version of the string ARG. + This is like quotearg_n (N, ARG), except that it uses S with no other + options to specify the quoting method. */ +char *quotearg_n_style PARAMS ((unsigned int n, enum quoting_style s, + char const *arg)); + +/* Equivalent to quotearg_n_style (0, S, ARG). */ +char *quotearg_style PARAMS ((enum quoting_style s, char const *arg)); + +/* Like quotearg (ARG), except also quote any instances of CH. */ +char *quotearg_char PARAMS ((char const *arg, char ch)); + +/* Equivalent to quotearg_char (ARG, ':'). */ +char *quotearg_colon PARAMS ((char const *arg)); diff --git a/src/apps/bin/patch/quotesys.c b/src/apps/bin/patch/quotesys.c new file mode 100644 index 0000000000..528f382272 --- /dev/null +++ b/src/apps/bin/patch/quotesys.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/src/apps/bin/patch/quotesys.h b/src/apps/bin/patch/quotesys.h new file mode 100644 index 0000000000..0d0d825709 --- /dev/null +++ b/src/apps/bin/patch/quotesys.h @@ -0,0 +1,9 @@ +/* quotesys.h -- declarations for quoting system arguments */ + +#if defined __STDC__ || __GNUC__ +# define __QUOTESYS_P(args) args +#else +# define __QUOTESYS_P(args) () +#endif + +size_t quote_system_arg __QUOTESYS_P ((char *, char const *)); diff --git a/src/apps/bin/patch/util.c b/src/apps/bin/patch/util.c new file mode 100644 index 0000000000..34e51c4147 --- /dev/null +++ b/src/apps/bin/patch/util.c @@ -0,0 +1,996 @@ +/* utility functions for `patch' */ + +/* $Id: util.c,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +/* Copyright 1986 Larry Wall + Copyright 1992, 1993, 1997-1998, 1999 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 +#include +#include +#undef XTERN +#define XTERN +#include +#include + +#include +#include + +#include +#if !defined SIGCHLD && defined SIGCLD +#define SIGCHLD SIGCLD +#endif +#if ! HAVE_RAISE && ! defined 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 (where *FROM_NEEDS_REMOVAL is nonzero if FROM + needs removal when cleaning up at the end of execution) + 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). + FROM_NEEDS_REMOVAL must be nonnull if FROM is nonnull. + Back up TO if BACKUP is nonzero. */ + +void +move_file (char const *from, int volatile *from_needs_removal, + char *to, mode_t mode, int backup) +{ + 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, backup_type); + if (!bakname) + memory_fatal (); + } + + if (to_errno) + { + int fd; + + if (debug & 4) + say ("Creating empty unreadable file %s\n", quotearg (bakname)); + + try_makedirs_errno = ENOENT; + unlink (bakname); + while ((fd = creat (bakname, 0)) < 0) + { + if (errno != try_makedirs_errno) + pfatal ("Can't create file %s", quotearg (bakname)); + makedirs (bakname); + try_makedirs_errno = 0; + } + if (close (fd) != 0) + pfatal ("Can't close file %s", quotearg (bakname)); + } + else + { + if (debug & 4) + say ("Renaming file %s to %s\n", + quotearg_n (0, to), quotearg_n (1, bakname)); + while (rename (to, bakname) != 0) + { + if (errno != try_makedirs_errno) + pfatal ("Can't rename file %s to %s", + quotearg_n (0, to), quotearg_n (1, bakname)); + makedirs (bakname); + try_makedirs_errno = 0; + } + } + + free (bakname); + } + + if (from) + { + if (debug & 4) + say ("Renaming file %s to %s\n", + quotearg_n (0, from), quotearg_n (1, to)); + + if (rename (from, to) == 0) + *from_needs_removal = 0; + else + { + int to_dir_known_to_exist = 0; + + if (errno == ENOENT + && (to_errno == -1 || to_errno == ENOENT)) + { + makedirs (to); + to_dir_known_to_exist = 1; + if (rename (from, to) == 0) + { + *from_needs_removal = 0; + return; + } + } + + if (errno == EXDEV) + { + if (! backup) + { + if (unlink (to) == 0) + to_dir_known_to_exist = 1; + else if (errno != ENOENT) + pfatal ("Can't remove file %s", quotearg (to)); + } + if (! to_dir_known_to_exist) + makedirs (to); + copy_file (from, to, 0, mode); + return; + } + + pfatal ("Can't rename file %s to %s", + quotearg_n (0, from), quotearg_n (1, to)); + } + } + else if (! backup) + { + if (debug & 4) + say ("Removing file %s\n", quotearg (to)); + if (unlink (to) != 0) + pfatal ("Can't remove file %s", quotearg (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. */ +int +create_file (char const *file, int open_flags, mode_t mode) +{ + 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 file %s", quotearg (file)); + return fd; +} + +/* Copy a file. */ + +void +copy_file (char const *from, char const *to, int to_flags, mode_t mode) +{ + int tofd; + int fromfd; + size_t i; + + if ((fromfd = open (from, O_RDONLY | O_BINARY)) < 0) + pfatal ("Can't reopen file %s", quotearg (from)); + tofd = create_file (to, O_WRONLY | O_BINARY | to_flags, mode); + while ((i = read (fromfd, buf, bufsize)) != 0) + { + if (i == (size_t) -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 RCSSUFFIX[] = ",v"; +static char const CHECKOUT[] = "co %s"; +static char const CHECKOUT_LOCKED[] = "co -l %s"; +static char const RCSDIFF1[] = "rcsdiff %s"; + +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 CLEARTOOL_CO[] = "cleartool co -unr -nc "; + +/* Return "RCS" if FILENAME is controlled by RCS, + "SCCS" if it is controlled by SCCS, + "ClearCase" if it is controlled by Clearcase, 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 + (but set *DIFFBUF to 0 if the diff operation is meaningless). + *GETBUF and *DIFFBUF must be freed by the caller. */ +char const * +version_controller (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 CLEARTOOL_CO + 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"; + } + else if (!readonly && filestat + && try1 ("%s@@", filebase) && S_ISDIR (cstat.st_mode)) + { + if (getbuf) + { + char *p = *getbuf = xmalloc (maxgetsize); + strcpy (p, CLEARTOOL_CO); + p += sizeof CLEARTOOL_CO - 1; + p += quote_system_arg (p, filename); + *p = '\0'; + } + + if (diffbuf) + *diffbuf = 0; + + r = "ClearCase"; + } + + 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 (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] ", + quotearg (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", + quotearg (filename), getbuf); + } + else + { + if (verbosity == VERBOSE) + say ("Getting file %s from %s%s...\n", quotearg (filename), + cs, readonly ? "" : " with lock"); + if (systemic (getbuf) != 0) + fatal ("Can't get file %s from %s", quotearg (filename), cs); + if (stat (filename, filestat) != 0) + pfatal ("%s", quotearg (filename)); + } + + return 1; +} + +/* Allocate a unique area for a string. */ + +char * +savebuf (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 (char const *s) +{ + return savebuf (s, strlen (s) + 1); +} + +void +remove_prefix (char *p, size_t prefixlen) +{ + char const *s = p + prefixlen; + while ((*p++ = *s++)) + continue; +} + +char * +format_linenum (char numbuf[LINENUM_LENGTH_BOUND + 1], LINENUM n) +{ + char *p = numbuf + LINENUM_LENGTH_BOUND; + *p = '\0'; + + if (n < 0) + { + do + *--p = '0' - (int) (n % 10); + while ((n /= 10) != 0); + + *--p = '-'; + } + else + { + do + *--p = '0' + (int) (n % 10); + while ((n /= 10) != 0); + } + + return p; +} + +#if !HAVE_VPRINTF +#define vfprintf my_vfprintf +static int +vfprintf (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. */ + +void +fatal (char const *format, ...) +{ + 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 (void) +{ + fatal ("out of memory"); +} + +void +read_fatal (void) +{ + pfatal ("read error"); +} + +void +write_fatal (void) +{ + pfatal ("write error"); +} + +/* Say something from patch, something from the system, then silence . . . */ + +void +pfatal (char const *format, ...) +{ + 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. */ + +void +say (char const *format, ...) +{ + 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. */ + +void +ask (char const *format, ...) +{ + 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. */ + +int +ok_to_reverse (char const *format, ...) +{ + 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? [n] " : " Assume -R? [n] "); + r = *buf == 'y'; + 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 (int sig) +{ + signal (sig, SIG_IGN); + fatal_exit (sig); +} +#endif + +void +set_signals (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 (void) +{ +#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 (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 (char const *command) +{ + if (debug & 8) + say ("+ %s\n", command); + fflush (stdout); + return system (command); +} + +/* 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 (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 (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 (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", quotearg (filename)); + filename[i] = '/'; + } +} + +static time_t initial_time; + +void +init_time (void) +{ + time (&initial_time); +} + +/* Make filenames more reasonable. */ + +char * +fetchname (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 up to `strip_leading' leading slashes and null terminate. + If `strip_leading' is negative, strip all leading slashes. */ + for (t = at; *t; t++) + { + if (ISSLASH (*t)) + { + while (ISSLASH (t[1])) + t++; + if (strip_leading < 0 || --sleading >= 0) + name = t+1; + } + else if (ISSPACE ((unsigned char) *t)) + { + char const *u = t; + + if (set_time | set_utc) + stamp = str2time (&u, 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 offset anywhere in the range + -25:00 < offset < +26:00. Match any time in that + range by assuming local time is -25:00 and then matching + any ``local'' time T in the range 0 < T < 25+26 hours. */ + stamp = str2time (&u, initial_time, -25L * 60 * 60); + if (0 < stamp && stamp < (25 + 26) * 60L * 60) + stamp = 0; + } + + if (*u && ! ISSPACE ((unsigned char) *u)) + stamp = (time_t) -1; + + *t = '\0'; + break; + } + } + + if (!*name) + return 0; + + /* If the name is "/dev/null", ignore the name and mark the file + as being nonexistent. The name "/dev/null" appears in patches + regardless of how NULL_DEVICE is spelled. */ + if (strcmp (at, "/dev/null") == 0) + { + if (pstamp) + *pstamp = 0; + return 0; + } + + /* Ignore the name if it doesn't have enough slashes to strip off. */ + if (0 < sleading) + return 0; + + if (pstamp) + *pstamp = stamp; + + return savestr (name); +} + +void +Fseek (FILE *stream, file_offset offset, int ptrname) +{ + if (file_seek (stream, offset, ptrname) != 0) + pfatal ("fseek"); +} diff --git a/src/apps/bin/patch/util.h b/src/apps/bin/patch/util.h new file mode 100644 index 0000000000..4217a665e9 --- /dev/null +++ b/src/apps/bin/patch/util.h @@ -0,0 +1,39 @@ +/* utility functions for `patch' */ + +/* $Id: util.h,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +/* An upper bound on the print length of a signed decimal line number. + Add one for the sign. */ +#define LINENUM_LENGTH_BOUND (sizeof (LINENUM) * CHAR_BIT / 3 + 1) + +XTERN enum backup_type backup_type; + +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 *)); +char *format_linenum PARAMS ((char[LINENUM_LENGTH_BOUND + 1], LINENUM)); +void Fseek PARAMS ((FILE *, file_offset, int)); +void copy_file PARAMS ((char const *, char const *, int, 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)) __attribute__ ((noreturn)); +void move_file PARAMS ((char const *, int volatile *, char *, mode_t, int)); +void read_fatal PARAMS ((void)) __attribute__ ((noreturn)); +void remove_prefix PARAMS ((char *, size_t)); +void removedirs PARAMS ((char *)); +void set_signals PARAMS ((int)); +void write_fatal PARAMS ((void)) __attribute__ ((noreturn)); diff --git a/src/apps/bin/patch/version.c b/src/apps/bin/patch/version.c new file mode 100644 index 0000000000..98891f8c6f --- /dev/null +++ b/src/apps/bin/patch/version.c @@ -0,0 +1,30 @@ +/* Print the version number. */ + +/* $Id: version.c,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +#define XTERN extern +#include +#undef XTERN +#define XTERN +#include +#include + +static char const copyright_string[] = "\ +Copyright 1984-1988 Larry Wall\n\ +Copyright 1989-1999 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 and Paul Eggert"; + +void +version (void) +{ + 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/src/apps/bin/patch/version.h b/src/apps/bin/patch/version.h new file mode 100644 index 0000000000..ad639a0758 --- /dev/null +++ b/src/apps/bin/patch/version.h @@ -0,0 +1,5 @@ +/* Print the version number. */ + +/* $Id: version.h,v 1.1 2004/06/16 21:22:10 korli Exp $ */ + +void version PARAMS ((void)); diff --git a/src/apps/bin/patch/xalloc.h b/src/apps/bin/patch/xalloc.h new file mode 100644 index 0000000000..2709ad866a --- /dev/null +++ b/src/apps/bin/patch/xalloc.h @@ -0,0 +1,52 @@ +/* xalloc.h -- malloc with out-of-memory checking + Copyright (C) 1990-1998, 1999 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. */ + +#ifndef XALLOC_H_ +# define XALLOC_H_ + +# ifndef PARAMS +# if defined PROTOTYPES || (defined __STDC__ && __STDC__) +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +# endif + +/* Exit value when the requested amount of memory is not available. + It is initialized to EXIT_FAILURE, but the caller may set it to + some other value. */ +extern int xalloc_exit_failure; + +/* If this pointer is non-zero, run the specified function upon each + allocation failure. It is initialized to zero. */ +extern void (*xalloc_fail_func) (); + +/* If XALLOC_FAIL_FUNC is undefined or a function that returns, this + message must be non-NULL. It is translated via gettext. + The default value is "Memory exhausted". */ +extern char *const xalloc_msg_memory_exhausted; + +void *xmalloc PARAMS ((size_t n)); +void *xcalloc PARAMS ((size_t n, size_t s)); +void *xrealloc PARAMS ((void *p, size_t n)); + +# define XMALLOC(Type, N_bytes) ((Type *) xmalloc (sizeof (Type) * (N_bytes))) +# define XCALLOC(Type, N_bytes) ((Type *) xcalloc (sizeof (Type), (N_bytes))) +# define XREALLOC(Ptr, Type, N_bytes) \ + ((Type *) xrealloc ((void *) (Ptr), sizeof (Type) * (N_bytes))) + +#endif /* !XALLOC_H_ */ diff --git a/src/apps/bin/patch/xmalloc.c b/src/apps/bin/patch/xmalloc.c new file mode 100644 index 0000000000..696cf3c9a2 --- /dev/null +++ b/src/apps/bin/patch/xmalloc.c @@ -0,0 +1,113 @@ +/* xmalloc.c -- malloc with out of memory checking + Copyright (C) 1990-1997, 98, 99 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 + +#if STDC_HEADERS +# include +#else +void *calloc (); +void *malloc (); +void *realloc (); +void free (); +#endif + +#if ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# define textdomain(Domain) +# define _(Text) Text +#endif +#define N_(Text) Text + +#include "error.h" +#include "xalloc.h" + +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +#ifndef HAVE_DONE_WORKING_MALLOC_CHECK +you must run the autoconf test for a properly working malloc -- see malloc.m4 +#endif + +#ifndef HAVE_DONE_WORKING_REALLOC_CHECK +you must run the autoconf test for a properly working realloc -- see realloc.m4 +#endif + +/* Exit value when the requested amount of memory is not available. + The caller may set it to some other value. */ +int xalloc_exit_failure = EXIT_FAILURE; + +/* If non NULL, call this function when memory is exhausted. */ +void (*xalloc_fail_func) () = 0; + +/* If XALLOC_FAIL_FUNC is NULL, or does return, display this message + before exiting when memory is exhausted. Goes through gettext. */ +char *const xalloc_msg_memory_exhausted = N_("Memory exhausted"); + +static void +xalloc_fail (void) +{ + if (xalloc_fail_func) + (*xalloc_fail_func) (); + error (xalloc_exit_failure, 0, "%s", _(xalloc_msg_memory_exhausted)); +} + +/* Allocate N bytes of memory dynamically, with error checking. */ + +void * +xmalloc (size_t n) +{ + void *p; + + p = malloc (n); + if (p == 0) + xalloc_fail (); + return p; +} + +/* Change the size of an allocated block of memory P to N bytes, + with error checking. + If P is NULL, run xmalloc. */ + +void * +xrealloc (void *p, size_t n) +{ + p = realloc (p, n); + if (p == 0) + xalloc_fail (); + return p; +} + +/* Allocate memory for N elements of S bytes, with error checking. */ + +void * +xcalloc (size_t n, size_t s) +{ + void *p; + + p = calloc (n, s); + if (p == 0) + xalloc_fail (); + return p; +}