summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/diff
diff options
context:
space:
mode:
authorconklin <conklin@FreeBSD.org>1993-07-09 20:36:06 +0000
committerconklin <conklin@FreeBSD.org>1993-07-09 20:36:06 +0000
commit2a4d079c1f211d720705087503bb34edb21400ba (patch)
tree638aeea32f1f840c438eb4093e3769cd7f1ce0d6 /gnu/usr.bin/diff
parent4a1861f4d760f487cc9f832f4478e6c657ca106f (diff)
downloadFreeBSD-src-2a4d079c1f211d720705087503bb34edb21400ba.zip
FreeBSD-src-2a4d079c1f211d720705087503bb34edb21400ba.tar.gz
Update diff, diff3, and sdiff's makefiles to use .PATH: instead of
copying the same files to multiple directories. This should make things easier when it comes time to update to a newer version of diffutils. With this checkin, this brings the netbsd and freebsd diff dists into sync.
Diffstat (limited to 'gnu/usr.bin/diff')
-rw-r--r--gnu/usr.bin/diff/COPYING339
-rw-r--r--gnu/usr.bin/diff/diff3.c1693
-rw-r--r--gnu/usr.bin/diff/sdiff.c1067
3 files changed, 3099 insertions, 0 deletions
diff --git a/gnu/usr.bin/diff/COPYING b/gnu/usr.bin/diff/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/diff/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/diff/diff3.c b/gnu/usr.bin/diff/diff3.c
new file mode 100644
index 0000000..b9952fc
--- /dev/null
+++ b/gnu/usr.bin/diff/diff3.c
@@ -0,0 +1,1693 @@
+/* Three way file comparison program (diff3) for Project GNU.
+ Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Randy Smith */
+
+#if __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include "getopt.h"
+#include "system.h"
+
+/*
+ * Internal data structures and macros for the diff3 program; includes
+ * data structures for both diff3 diffs and normal diffs.
+ */
+
+/* Different files within a three way diff. */
+#define FILE0 0
+#define FILE1 1
+#define FILE2 2
+
+/*
+ * A three way diff is built from two two-way diffs; the file which
+ * the two two-way diffs share is:
+ */
+#define FILEC FILE2
+
+/*
+ * Different files within a two way diff.
+ * FC is the common file, FO the other file.
+ */
+#define FO 0
+#define FC 1
+
+/* The ranges are indexed by */
+#define START 0
+#define END 1
+
+enum diff_type {
+ ERROR, /* Should not be used */
+ ADD, /* Two way diff add */
+ CHANGE, /* Two way diff change */
+ DELETE, /* Two way diff delete */
+ DIFF_ALL, /* All three are different */
+ DIFF_1ST, /* Only the first is different */
+ DIFF_2ND, /* Only the second */
+ DIFF_3RD /* Only the third */
+};
+
+/* Two way diff */
+struct diff_block {
+ int ranges[2][2]; /* Ranges are inclusive */
+ char **lines[2]; /* The actual lines (may contain nulls) */
+ int *lengths[2]; /* Line lengths (including newlines, if any) */
+ struct diff_block *next;
+};
+
+/* Three way diff */
+
+struct diff3_block {
+ enum diff_type correspond; /* Type of diff */
+ int ranges[3][2]; /* Ranges are inclusive */
+ char **lines[3]; /* The actual lines (may contain nulls) */
+ int *lengths[3]; /* Line lengths (including newlines, if any) */
+ struct diff3_block *next;
+};
+
+/*
+ * Access the ranges on a diff block.
+ */
+#define D_LOWLINE(diff, filenum) \
+ ((diff)->ranges[filenum][START])
+#define D_HIGHLINE(diff, filenum) \
+ ((diff)->ranges[filenum][END])
+#define D_NUMLINES(diff, filenum) \
+ (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
+
+/*
+ * Access the line numbers in a file in a diff by relative line
+ * numbers (i.e. line number within the diff itself). Note that these
+ * are lvalues and can be used for assignment.
+ */
+#define D_RELNUM(diff, filenum, linenum) \
+ ((diff)->lines[filenum][linenum])
+#define D_RELLEN(diff, filenum, linenum) \
+ ((diff)->lengths[filenum][linenum])
+
+/*
+ * And get at them directly, when that should be necessary.
+ */
+#define D_LINEARRAY(diff, filenum) \
+ ((diff)->lines[filenum])
+#define D_LENARRAY(diff, filenum) \
+ ((diff)->lengths[filenum])
+
+/*
+ * Next block.
+ */
+#define D_NEXT(diff) ((diff)->next)
+
+/*
+ * Access the type of a diff3 block.
+ */
+#define D3_TYPE(diff) ((diff)->correspond)
+
+/*
+ * Line mappings based on diffs. The first maps off the top of the
+ * diff, the second off of the bottom.
+ */
+#define D_HIGH_MAPLINE(diff, fromfile, tofile, lineno) \
+ ((lineno) \
+ - D_HIGHLINE ((diff), (fromfile)) \
+ + D_HIGHLINE ((diff), (tofile)))
+
+#define D_LOW_MAPLINE(diff, fromfile, tofile, lineno) \
+ ((lineno) \
+ - D_LOWLINE ((diff), (fromfile)) \
+ + D_LOWLINE ((diff), (tofile)))
+
+/*
+ * General memory allocation function.
+ */
+#define ALLOCATE(number, type) \
+ (type *) xmalloc ((number) * sizeof (type))
+
+/* Options variables for flags set on command line. */
+
+/* If nonzero, treat all files as text files, never as binary. */
+static int always_text;
+
+/* If nonzero, write out an ed script instead of the standard diff3 format. */
+static int edscript;
+
+/* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
+ preserve the lines which would normally be deleted from
+ file 1 with a special flagging mechanism. */
+static int flagging;
+
+/* Number of lines to keep in identical prefix and suffix. */
+static int horizon_lines = 10;
+
+/* If nonzero, do not output information for overlapping diffs. */
+static int simple_only;
+
+/* If nonzero, do not output information for non-overlapping diffs. */
+static int overlap_only;
+
+/* If nonzero, show information for DIFF_2ND diffs. */
+static int show_2nd;
+
+/* If nonzero, include `:wq' at the end of the script
+ to write out the file being edited. */
+static int finalwrite;
+
+/* If nonzero, output a merged file. */
+static int merge;
+
+static char *argv0;
+
+/*
+ * Forward function declarations.
+ */
+static int myread ();
+static void fatal ();
+static void perror_with_exit ();
+static struct diff_block *process_diff ();
+static struct diff3_block *make_3way_diff ();
+static void output_diff3 ();
+static int output_diff3_edscript ();
+static int output_diff3_merge ();
+static void usage ();
+
+static struct diff3_block *using_to_diff3_block ();
+static int copy_stringlist ();
+static struct diff3_block *create_diff3_block ();
+static int compare_line_list ();
+
+static char *read_diff ();
+static enum diff_type process_diff_control ();
+static char *scan_diff_line ();
+
+static struct diff3_block *reverse_diff3_blocklist ();
+
+VOID *xmalloc ();
+static VOID *xrealloc ();
+
+static char diff_program[] = DIFF_PROGRAM;
+
+static struct option longopts[] =
+{
+ {"text", 0, NULL, 'a'},
+ {"show-all", 0, NULL, 'A'},
+ {"ed", 0, NULL, 'e'},
+ {"show-overlap", 0, NULL, 'E'},
+ {"label", 1, NULL, 'L'},
+ {"merge", 0, NULL, 'm'},
+ {"overlap-only", 0, NULL, 'x'},
+ {"easy-only", 0, NULL, '3'},
+ {"version", 0, NULL, 'v'},
+ {0, 0, 0, 0}
+};
+
+/*
+ * Main program. Calls diff twice on two pairs of input files,
+ * combines the two diffs, and outputs them.
+ */
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *version_string;
+ int c, i;
+ int mapping[3];
+ int rev_mapping[3];
+ int incompat;
+ int conflicts_found;
+ struct diff_block *thread0, *thread1, *last_block;
+ struct diff3_block *diff3;
+ int tag_count = 0;
+ char *tag_strings[3];
+ extern char *optarg;
+ char *commonname;
+ char **file;
+ struct stat statb;
+
+ incompat = 0;
+
+ argv0 = argv[0];
+
+ while ((c = getopt_long (argc, argv, "aeimvx3AEXL:", longopts, (int *) 0))
+ != EOF)
+ {
+ switch (c)
+ {
+ case 'a':
+ always_text = 1;
+ break;
+ case 'A':
+ show_2nd = 1;
+ flagging = 1;
+ incompat++;
+ break;
+ case 'x':
+ overlap_only = 1;
+ incompat++;
+ break;
+ case '3':
+ simple_only = 1;
+ incompat++;
+ break;
+ case 'i':
+ finalwrite = 1;
+ break;
+ case 'm':
+ merge = 1;
+ break;
+ case 'X':
+ overlap_only = 1;
+ /* Falls through */
+ case 'E':
+ flagging = 1;
+ /* Falls through */
+ case 'e':
+ incompat++;
+ break;
+ case 'v':
+ fprintf (stderr, "GNU diff3 version %s\n", version_string);
+ break;
+ case 'L':
+ /* Handle up to three -L options. */
+ if (tag_count < 3)
+ {
+ tag_strings[tag_count++] = optarg;
+ break;
+ }
+ /* Falls through */
+ default:
+ usage ();
+ /* NOTREACHED */
+ }
+ }
+
+ edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */
+ show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */
+ flagging |= ~incompat & merge;
+
+ if (incompat > 1 /* Ensure at most one of -AeExX3. */
+ || finalwrite & merge /* -i -m would rewrite input file. */
+ || (tag_count && ! flagging) /* -L requires one of -AEX. */
+ || argc - optind != 3)
+ usage ();
+
+ file = &argv[optind];
+
+ for (i = tag_count; i < 3; i++)
+ tag_strings[i] = file[i];
+
+ /* Always compare file1 to file2, even if file2 is "-".
+ This is needed for -mAeExX3. Using the file0 as
+ the common file would produce wrong results, because if the
+ file0-file1 diffs didn't line up with the file0-file2 diffs
+ (which is entirely possible since we don't use diff's -n option),
+ diff3 might report phantom changes from file1 to file2. */
+
+ if (strcmp (file[2], "-") == 0)
+ {
+ /* Sigh. We've got standard input as the last arg. We can't
+ call diff twice on stdin. Use the middle arg as the common
+ file instead. */
+ if (strcmp (file[0], "-") == 0 || strcmp (file[1], "-") == 0)
+ fatal ("`-' specified for more than one input file");
+ mapping[0] = 0;
+ mapping[1] = 2;
+ mapping[2] = 1;
+ }
+ else
+ {
+ /* Normal, what you'd expect */
+ mapping[0] = 0;
+ mapping[1] = 1;
+ mapping[2] = 2;
+ }
+
+ for (i = 0; i < 3; i++)
+ rev_mapping[mapping[i]] = i;
+
+ for (i = 0; i < 3; i++)
+ if (strcmp (file[i], "-") != 0)
+ if (stat (file[i], &statb) < 0)
+ perror_with_exit (file[i]);
+ else if (S_ISDIR(statb.st_mode))
+ {
+ fprintf (stderr, "%s: %s: Is a directory\n", argv0, file[i]);
+ exit (2);
+ }
+
+
+ commonname = file[rev_mapping[FILEC]];
+ thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block);
+ if (thread1)
+ for (i = 0; i < 2; i++)
+ {
+ horizon_lines = max (horizon_lines, D_NUMLINES (thread1, i));
+ horizon_lines = max (horizon_lines, D_NUMLINES (last_block, i));
+ }
+ thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block);
+ diff3 = make_3way_diff (thread0, thread1);
+ if (edscript)
+ conflicts_found
+ = output_diff3_edscript (stdout, diff3, mapping, rev_mapping,
+ tag_strings[0], tag_strings[1], tag_strings[2]);
+ else if (merge)
+ {
+ if (! freopen (file[rev_mapping[FILE0]], "r", stdin))
+ perror_with_exit (file[rev_mapping[FILE0]]);
+ conflicts_found
+ = output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping,
+ tag_strings[0], tag_strings[1], tag_strings[2]);
+ if (ferror (stdin))
+ fatal ("read error");
+ }
+ else
+ {
+ output_diff3 (stdout, diff3, mapping, rev_mapping);
+ conflicts_found = 0;
+ }
+
+ if (ferror (stdout) || fclose (stdout) != 0)
+ fatal ("write error");
+ exit (conflicts_found);
+ return conflicts_found;
+}
+
+/*
+ * Explain, patiently and kindly, how to use this program. Then exit.
+ */
+static void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [options] my-file older-file your-file\n\
+Options:\n\
+ [-exAEX3v] [-i|-m] [-L label1 [-L label2 [-L label3]]] [--text] [--ed]\n\
+ [--merge] [--show-all] [--show-overlap] [--overlap-only] [--easy-only]\n\
+ [--label=label1 [--label=label2 [--label=label3]]] [--version]\n\
+ Only one of [exAEX3] is allowed\n", argv0);
+ exit (2);
+}
+
+/*
+ * Routines that combine the two diffs together into one. The
+ * algorithm used follows:
+ *
+ * File2 is shared in common between the two diffs.
+ * Diff02 is the diff between 0 and 2.
+ * Diff12 is the diff between 1 and 2.
+ *
+ * 1) Find the range for the first block in File2.
+ * a) Take the lowest of the two ranges (in File2) in the two
+ * current blocks (one from each diff) as being the low
+ * water mark. Assign the upper end of this block as
+ * being the high water mark and move the current block up
+ * one. Mark the block just moved over as to be used.
+ * b) Check the next block in the diff that the high water
+ * mark is *not* from.
+ *
+ * *If* the high water mark is above
+ * the low end of the range in that block,
+ *
+ * mark that block as to be used and move the current
+ * block up. Set the high water mark to the max of
+ * the high end of this block and the current. Repeat b.
+ *
+ * 2) Find the corresponding ranges in File0 (from the blocks
+ * in diff02; line per line outside of diffs) and in File1.
+ * Create a diff3_block, reserving space as indicated by the ranges.
+ *
+ * 3) Copy all of the pointers for file2 in. At least for now,
+ * do bcmp's between corresponding strings in the two diffs.
+ *
+ * 4) Copy all of the pointers for file0 and 1 in. Get what you
+ * need from file2 (when there isn't a diff block, it's
+ * identical to file2 within the range between diff blocks).
+ *
+ * 5) If the diff blocks you used came from only one of the two
+ * strings of diffs, then that file (i.e. the one other than
+ * the common file in that diff) is the odd person out. If you used
+ * diff blocks from both sets, check to see if files 0 and 1 match:
+ *
+ * Same number of lines? If so, do a set of bcmp's (if a
+ * bcmp matches; copy the pointer over; it'll be easier later
+ * if you have to do any compares). If they match, 0 & 1 are
+ * the same. If not, all three different.
+ *
+ * Then you do it again, until you run out of blocks.
+ *
+ */
+
+/*
+ * This routine makes a three way diff (chain of diff3_block's) from two
+ * two way diffs (chains of diff_block's). It is assumed that each of
+ * the two diffs passed are onto the same file (i.e. that each of the
+ * diffs were made "to" the same file). The three way diff pointer
+ * returned will have numbering FILE0--the other file in diff02,
+ * FILE1--the other file in diff12, and FILEC--the common file.
+ */
+static struct diff3_block *
+make_3way_diff (thread0, thread1)
+ struct diff_block *thread0, *thread1;
+{
+/*
+ * This routine works on the two diffs passed to it as threads.
+ * Thread number 0 is diff02, thread number 1 is diff12. The USING
+ * array is set to the base of the list of blocks to be used to
+ * construct each block of the three way diff; if no blocks from a
+ * particular thread are to be used, that element of the using array
+ * is set to 0. The elements LAST_USING array are set to the last
+ * elements on each of the using lists.
+ *
+ * The HIGH_WATER_MARK is set to the highest line number in the common file
+ * described in any of the diffs in either of the USING lists. The
+ * HIGH_WATER_THREAD names the thread. Similarly the BASE_WATER_MARK
+ * and BASE_WATER_THREAD describe the lowest line number in the common file
+ * described in any of the diffs in either of the USING lists. The
+ * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was
+ * taken.
+ *
+ * The HIGH_WATER_DIFF should always be equal to LAST_USING
+ * [HIGH_WATER_THREAD]. The OTHER_DIFF is the next diff to check for
+ * higher water, and should always be equal to
+ * CURRENT[HIGH_WATER_THREAD ^ 0x1]. The OTHER_THREAD is the thread
+ * in which the OTHER_DIFF is, and hence should always be equal to
+ * HIGH_WATER_THREAD ^ 0x1.
+ *
+ * The variable LAST_DIFF is kept set to the last diff block produced
+ * by this routine, for line correspondence purposes between that diff
+ * and the one currently being worked on. It is initialized to
+ * ZERO_DIFF before any blocks have been created.
+ */
+
+ struct diff_block
+ *using[2],
+ *last_using[2],
+ *current[2];
+
+ int
+ high_water_mark;
+
+ int
+ high_water_thread,
+ base_water_thread,
+ other_thread;
+
+ struct diff_block
+ *high_water_diff,
+ *other_diff;
+
+ struct diff3_block
+ *result,
+ *tmpblock,
+ **result_end,
+ *last_diff3;
+
+ static struct diff3_block zero_diff3 = {
+ ERROR,
+ { {0, 0}, {0, 0}, {0, 0} },
+ { (char **) 0, (char **) 0, (char **) 0 },
+ { (int *) 0, (int *) 0, (int *) 0 },
+ (struct diff3_block *) 0
+ };
+
+ /* Initialization */
+ result = 0;
+ result_end = &result;
+ current[0] = thread0; current[1] = thread1;
+ last_diff3 = &zero_diff3;
+
+ /* Sniff up the threads until we reach the end */
+
+ while (current[0] || current[1])
+ {
+ using[0] = using[1] = last_using[0] = last_using[1] =
+ (struct diff_block *) 0;
+
+ /* Setup low and high water threads, diffs, and marks. */
+ if (!current[0])
+ base_water_thread = 1;
+ else if (!current[1])
+ base_water_thread = 0;
+ else
+ base_water_thread =
+ (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
+
+ high_water_thread = base_water_thread;
+
+ high_water_diff = current[high_water_thread];
+
+#if 0
+ /* low and high waters start off same diff */
+ base_water_mark = D_LOWLINE (high_water_diff, FC);
+#endif
+
+ high_water_mark = D_HIGHLINE (high_water_diff, FC);
+
+ /* Make the diff you just got info from into the using class */
+ using[high_water_thread]
+ = last_using[high_water_thread]
+ = high_water_diff;
+ current[high_water_thread] = high_water_diff->next;
+ last_using[high_water_thread]->next
+ = (struct diff_block *) 0;
+
+ /* And mark the other diff */
+ other_thread = high_water_thread ^ 0x1;
+ other_diff = current[other_thread];
+
+ /* Shuffle up the ladder, checking the other diff to see if it
+ needs to be incorporated. */
+ while (other_diff
+ && D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
+ {
+
+ /* Incorporate this diff into the using list. Note that
+ this doesn't take it off the current list */
+ if (using[other_thread])
+ last_using[other_thread]->next = other_diff;
+ else
+ using[other_thread] = other_diff;
+ last_using[other_thread] = other_diff;
+
+ /* Take it off the current list. Note that this following
+ code assumes that other_diff enters it equal to
+ current[high_water_thread ^ 0x1] */
+ current[other_thread]
+ = current[other_thread]->next;
+ other_diff->next
+ = (struct diff_block *) 0;
+
+ /* Set the high_water stuff
+ If this comparison is equal, then this is the last pass
+ through this loop; since diff blocks within a given
+ thread cannot overlap, the high_water_mark will be
+ *below* the range_start of either of the next diffs. */
+
+ if (high_water_mark < D_HIGHLINE (other_diff, FC))
+ {
+ high_water_thread ^= 1;
+ high_water_diff = other_diff;
+ high_water_mark = D_HIGHLINE (other_diff, FC);
+ }
+
+ /* Set the other diff */
+ other_thread = high_water_thread ^ 0x1;
+ other_diff = current[other_thread];
+ }
+
+ /* The using lists contain a list of all of the blocks to be
+ included in this diff3_block. Create it. */
+
+ tmpblock = using_to_diff3_block (using, last_using,
+ base_water_thread, high_water_thread,
+ last_diff3);
+
+ if (!tmpblock)
+ fatal ("internal error: screwup in format of diff blocks");
+
+ /* Put it on the list. */
+ *result_end = tmpblock;
+ result_end = &tmpblock->next;
+
+ /* Set up corresponding lines correctly. */
+ last_diff3 = tmpblock;
+ }
+ return result;
+}
+
+/*
+ * using_to_diff3_block:
+ * This routine takes two lists of blocks (from two separate diff
+ * threads) and puts them together into one diff3 block.
+ * It then returns a pointer to this diff3 block or 0 for failure.
+ *
+ * All arguments besides using are for the convenience of the routine;
+ * they could be derived from the using array.
+ * LAST_USING is a pair of pointers to the last blocks in the using
+ * structure.
+ * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest
+ * and highest line numbers for File0.
+ * last_diff3 contains the last diff produced in the calling routine.
+ * This is used for lines mappings which would still be identical to
+ * the state that diff ended in.
+ *
+ * A distinction should be made in this routine between the two diffs
+ * that are part of a normal two diff block, and the three diffs that
+ * are part of a diff3_block.
+ */
+static struct diff3_block *
+using_to_diff3_block (using, last_using, low_thread, high_thread, last_diff3)
+ struct diff_block
+ *using[2],
+ *last_using[2];
+ int low_thread, high_thread;
+ struct diff3_block *last_diff3;
+{
+ int low[2], high[2];
+ struct diff3_block *result;
+ struct diff_block *ptr;
+ int d, i;
+
+ /* Find the range in the common file. */
+ int lowc = D_LOWLINE (using[low_thread], FC);
+ int highc = D_HIGHLINE (last_using[high_thread], FC);
+
+ /* Find the ranges in the other files.
+ If using[d] is null, that means that the file to which that diff
+ refers is equivalent to the common file over this range. */
+
+ for (d = 0; d < 2; d++)
+ if (using[d])
+ {
+ low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
+ high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
+ }
+ else
+ {
+ low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
+ high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
+ }
+
+ /* Create a block with the appropriate sizes */
+ result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
+
+ /* Copy information for the common file.
+ Return with a zero if any of the compares failed. */
+
+ for (d = 0; d < 2; d++)
+ for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
+ {
+ int result_offset = D_LOWLINE (ptr, FC) - lowc;
+
+ if (!copy_stringlist (D_LINEARRAY (ptr, FC),
+ D_LENARRAY (ptr, FC),
+ D_LINEARRAY (result, FILEC) + result_offset,
+ D_LENARRAY (result, FILEC) + result_offset,
+ D_NUMLINES (ptr, FC)))
+ return 0;
+ }
+
+ /* Copy information for file d. First deal with anything that might be
+ before the first diff. */
+
+ for (d = 0; d < 2; d++)
+ {
+ struct diff_block *u = using[d];
+ int lo = low[d], hi = high[d];
+
+ for (i = 0;
+ i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
+ i++)
+ {
+ D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
+ D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
+ }
+
+ for (ptr = u; ptr; ptr = D_NEXT (ptr))
+ {
+ int result_offset = D_LOWLINE (ptr, FO) - lo;
+ int linec;
+
+ if (!copy_stringlist (D_LINEARRAY (ptr, FO),
+ D_LENARRAY (ptr, FO),
+ D_LINEARRAY (result, FILE0 + d) + result_offset,
+ D_LENARRAY (result, FILE0 + d) + result_offset,
+ D_NUMLINES (ptr, FO)))
+ return 0;
+
+ /* Catch the lines between here and the next diff */
+ linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
+ for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
+ i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
+ i++)
+ {
+ D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
+ D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
+ linec++;
+ }
+ }
+ }
+
+ /* Set correspond */
+ if (!using[0])
+ D3_TYPE (result) = DIFF_2ND;
+ else if (!using[1])
+ D3_TYPE (result) = DIFF_1ST;
+ else
+ {
+ int nl0 = D_NUMLINES (result, FILE0);
+ int nl1 = D_NUMLINES (result, FILE1);
+
+ if (nl0 != nl1
+ || !compare_line_list (D_LINEARRAY (result, FILE0),
+ D_LENARRAY (result, FILE0),
+ D_LINEARRAY (result, FILE1),
+ D_LENARRAY (result, FILE1),
+ nl0))
+ D3_TYPE (result) = DIFF_ALL;
+ else
+ D3_TYPE (result) = DIFF_3RD;
+ }
+
+ return result;
+}
+
+/*
+ * This routine copies pointers from a list of strings to a different list
+ * of strings. If a spot in the second list is already filled, it
+ * makes sure that it is filled with the same string; if not it
+ * returns 0, the copy incomplete.
+ * Upon successful completion of the copy, it returns 1.
+ */
+static int
+copy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum)
+ char *fromptrs[], *toptrs[];
+ int *fromlengths, *tolengths;
+ int copynum;
+{
+ register char
+ **f = fromptrs,
+ **t = toptrs;
+ register int
+ *fl = fromlengths,
+ *tl = tolengths;
+
+ while (copynum--)
+ {
+ if (*t)
+ { if (*fl != *tl || bcmp (*f, *t, *fl)) return 0; }
+ else
+ { *t = *f ; *tl = *fl; }
+
+ t++; f++; tl++; fl++;
+ }
+ return 1;
+}
+
+/*
+ * Create a diff3_block, with ranges as specified in the arguments.
+ * Allocate the arrays for the various pointers (and zero them) based
+ * on the arguments passed. Return the block as a result.
+ */
+static struct diff3_block *
+create_diff3_block (low0, high0, low1, high1, low2, high2)
+ register int low0, high0, low1, high1, low2, high2;
+{
+ struct diff3_block *result = ALLOCATE (1, struct diff3_block);
+ int numlines;
+
+ D3_TYPE (result) = ERROR;
+ D_NEXT (result) = 0;
+
+ /* Assign ranges */
+ D_LOWLINE (result, FILE0) = low0;
+ D_HIGHLINE (result, FILE0) = high0;
+ D_LOWLINE (result, FILE1) = low1;
+ D_HIGHLINE (result, FILE1) = high1;
+ D_LOWLINE (result, FILE2) = low2;
+ D_HIGHLINE (result, FILE2) = high2;
+
+ /* Allocate and zero space */
+ numlines = D_NUMLINES (result, FILE0);
+ if (numlines)
+ {
+ D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *);
+ D_LENARRAY (result, FILE0) = ALLOCATE (numlines, int);
+ bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *)));
+ bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (int)));
+ }
+ else
+ {
+ D_LINEARRAY (result, FILE0) = (char **) 0;
+ D_LENARRAY (result, FILE0) = (int *) 0;
+ }
+
+ numlines = D_NUMLINES (result, FILE1);
+ if (numlines)
+ {
+ D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *);
+ D_LENARRAY (result, FILE1) = ALLOCATE (numlines, int);
+ bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *)));
+ bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (int)));
+ }
+ else
+ {
+ D_LINEARRAY (result, FILE1) = (char **) 0;
+ D_LENARRAY (result, FILE1) = (int *) 0;
+ }
+
+ numlines = D_NUMLINES (result, FILE2);
+ if (numlines)
+ {
+ D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *);
+ D_LENARRAY (result, FILE2) = ALLOCATE (numlines, int);
+ bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *)));
+ bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (int)));
+ }
+ else
+ {
+ D_LINEARRAY (result, FILE2) = (char **) 0;
+ D_LENARRAY (result, FILE2) = (int *) 0;
+ }
+
+ /* Return */
+ return result;
+}
+
+/*
+ * Compare two lists of lines of text.
+ * Return 1 if they are equivalent, 0 if not.
+ */
+static int
+compare_line_list (list1, lengths1, list2, lengths2, nl)
+ char *list1[], *list2[];
+ int *lengths1, *lengths2;
+ int nl;
+{
+ char
+ **l1 = list1,
+ **l2 = list2;
+ int
+ *lgths1 = lengths1,
+ *lgths2 = lengths2;
+
+ while (nl--)
+ if (!*l1 || !*l2 || *lgths1 != *lgths2++
+ || bcmp (*l1++, *l2++, *lgths1++))
+ return 0;
+ return 1;
+}
+
+/*
+ * Routines to input and parse two way diffs.
+ */
+
+extern char **environ;
+
+#define DIFF_CHUNK_SIZE 10000
+
+static struct diff_block *
+process_diff (filea, fileb, last_block)
+ char *filea, *fileb;
+ struct diff_block **last_block;
+{
+ char *diff_contents;
+ char *diff_limit;
+ char *scan_diff;
+ enum diff_type dt;
+ int i;
+ struct diff_block *block_list, **block_list_end, *bptr;
+
+ diff_limit = read_diff (filea, fileb, &diff_contents);
+ scan_diff = diff_contents;
+ block_list_end = &block_list;
+
+ while (scan_diff < diff_limit)
+ {
+ bptr = ALLOCATE (1, struct diff_block);
+ bptr->lines[0] = bptr->lines[1] = (char **) 0;
+ bptr->lengths[0] = bptr->lengths[1] = (int *) 0;
+
+ dt = process_diff_control (&scan_diff, bptr);
+ if (dt == ERROR || *scan_diff != '\n')
+ {
+ fprintf (stderr, "%s: diff error: ", argv0);
+ do
+ {
+ putc (*scan_diff, stderr);
+ }
+ while (*scan_diff++ != '\n');
+ exit (2);
+ }
+ scan_diff++;
+
+ /* Force appropriate ranges to be null, if necessary */
+ switch (dt)
+ {
+ case ADD:
+ bptr->ranges[0][0]++;
+ break;
+ case DELETE:
+ bptr->ranges[1][0]++;
+ break;
+ case CHANGE:
+ break;
+ default:
+ fatal ("internal error: invalid diff type in process_diff");
+ break;
+ }
+
+ /* Allocate space for the pointers for the lines from filea, and
+ parcel them out among these pointers */
+ if (dt != ADD)
+ {
+ int numlines = D_NUMLINES (bptr, 0);
+ bptr->lines[0] = ALLOCATE (numlines, char *);
+ bptr->lengths[0] = ALLOCATE (numlines, int);
+ for (i = 0; i < numlines; i++)
+ scan_diff = scan_diff_line (scan_diff,
+ &(bptr->lines[0][i]),
+ &(bptr->lengths[0][i]),
+ diff_limit,
+ '<');
+ }
+
+ /* Get past the separator for changes */
+ if (dt == CHANGE)
+ {
+ if (strncmp (scan_diff, "---\n", 4))
+ fatal ("invalid diff format; invalid change separator");
+ scan_diff += 4;
+ }
+
+ /* Allocate space for the pointers for the lines from fileb, and
+ parcel them out among these pointers */
+ if (dt != DELETE)
+ {
+ int numlines = D_NUMLINES (bptr, 1);
+ bptr->lines[1] = ALLOCATE (numlines, char *);
+ bptr->lengths[1] = ALLOCATE (numlines, int);
+ for (i = 0; i < numlines; i++)
+ scan_diff = scan_diff_line (scan_diff,
+ &(bptr->lines[1][i]),
+ &(bptr->lengths[1][i]),
+ diff_limit,
+ '>');
+ }
+
+ /* Place this block on the blocklist. */
+ *block_list_end = bptr;
+ block_list_end = &bptr->next;
+ }
+
+ *block_list_end = 0;
+ *last_block = bptr;
+ return block_list;
+}
+
+/*
+ * This routine will parse a normal format diff control string. It
+ * returns the type of the diff (ERROR if the format is bad). All of
+ * the other important information is filled into to the structure
+ * pointed to by db, and the string pointer (whose location is passed
+ * to this routine) is updated to point beyond the end of the string
+ * parsed. Note that only the ranges in the diff_block will be set by
+ * this routine.
+ *
+ * If some specific pair of numbers has been reduced to a single
+ * number, then both corresponding numbers in the diff block are set
+ * to that number. In general these numbers are interpetted as ranges
+ * inclusive, unless being used by the ADD or DELETE commands. It is
+ * assumed that these will be special cased in a superior routine.
+ */
+
+static enum diff_type
+process_diff_control (string, db)
+ char **string;
+ struct diff_block *db;
+{
+ char *s = *string;
+ int holdnum;
+ enum diff_type type;
+
+/* These macros are defined here because they can use variables
+ defined in this function. Don't try this at home kids, we're
+ trained professionals!
+
+ Also note that SKIPWHITE only recognizes tabs and spaces, and
+ that READNUM can only read positive, integral numbers */
+
+#define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; }
+#define READNUM(s, num) \
+ { if (!isdigit (*s)) return ERROR; holdnum = 0; \
+ do { holdnum = (*s++ - '0' + holdnum * 10); } \
+ while (isdigit (*s)); (num) = holdnum; }
+
+ /* Read first set of digits */
+ SKIPWHITE (s);
+ READNUM (s, db->ranges[0][START]);
+
+ /* Was that the only digit? */
+ SKIPWHITE (s);
+ if (*s == ',')
+ {
+ /* Get the next digit */
+ s++;
+ READNUM (s, db->ranges[0][END]);
+ }
+ else
+ db->ranges[0][END] = db->ranges[0][START];
+
+ /* Get the letter */
+ SKIPWHITE (s);
+ switch (*s)
+ {
+ case 'a':
+ type = ADD;
+ break;
+ case 'c':
+ type = CHANGE;
+ break;
+ case 'd':
+ type = DELETE;
+ break;
+ default:
+ return ERROR; /* Bad format */
+ }
+ s++; /* Past letter */
+
+ /* Read second set of digits */
+ SKIPWHITE (s);
+ READNUM (s, db->ranges[1][START]);
+
+ /* Was that the only digit? */
+ SKIPWHITE (s);
+ if (*s == ',')
+ {
+ /* Get the next digit */
+ s++;
+ READNUM (s, db->ranges[1][END]);
+ SKIPWHITE (s); /* To move to end */
+ }
+ else
+ db->ranges[1][END] = db->ranges[1][START];
+
+ *string = s;
+ return type;
+}
+
+static char *
+read_diff (filea, fileb, output_placement)
+ char *filea, *fileb;
+ char **output_placement;
+{
+ char *argv[7];
+ char horizon_arg[256];
+ char **ap;
+ int fds[2];
+ char *diff_result;
+ int current_chunk_size;
+ int bytes;
+ int total;
+ int pid, w;
+ int wstatus;
+
+ ap = argv;
+ *ap++ = diff_program;
+ if (always_text)
+ *ap++ = "-a";
+ sprintf (horizon_arg, "--horizon-lines=%d", horizon_lines);
+ *ap++ = horizon_arg;
+ *ap++ = "--";
+ *ap++ = filea;
+ *ap++ = fileb;
+ *ap = (char *) 0;
+
+ if (pipe (fds) < 0)
+ perror_with_exit ("pipe failed");
+
+ pid = vfork ();
+ if (pid == 0)
+ {
+ /* Child */
+ close (fds[0]);
+ if (fds[1] != fileno (stdout))
+ {
+ dup2 (fds[1], fileno (stdout));
+ close (fds[1]);
+ }
+ execve (diff_program, argv, environ);
+ /* Avoid stdio, because the parent process's buffers are inherited. */
+ write (fileno (stderr), diff_program, strlen (diff_program));
+ write (fileno (stderr), ": not found\n", 12);
+ _exit (2);
+ }
+
+ if (pid == -1)
+ perror_with_exit ("fork failed");
+
+ close (fds[1]); /* Prevent erroneous lack of EOF */
+ current_chunk_size = DIFF_CHUNK_SIZE;
+ diff_result = (char *) xmalloc (current_chunk_size);
+ total = 0;
+ do {
+ bytes = myread (fds[0],
+ diff_result + total,
+ current_chunk_size - total);
+ total += bytes;
+ if (total == current_chunk_size)
+ diff_result = (char *) xrealloc (diff_result, (current_chunk_size *= 2));
+ } while (bytes);
+
+ if (total != 0 && diff_result[total-1] != '\n')
+ fatal ("invalid diff format; incomplete last line");
+
+ *output_placement = diff_result;
+
+ do
+ if ((w = wait (&wstatus)) == -1)
+ perror_with_exit ("wait failed");
+ while (w != pid);
+
+ if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
+ fatal ("subsidiary diff failed");
+
+ return diff_result + total;
+}
+
+
+/*
+ * Scan a regular diff line (consisting of > or <, followed by a
+ * space, followed by text (including nulls) up to a newline.
+ *
+ * This next routine began life as a macro and many parameters in it
+ * are used as call-by-reference values.
+ */
+static char *
+scan_diff_line (scan_ptr, set_start, set_length, limit, firstchar)
+ char *scan_ptr, **set_start;
+ int *set_length;
+ char *limit;
+ char firstchar;
+{
+ char *line_ptr;
+
+ if (!(scan_ptr[0] == (firstchar)
+ && scan_ptr[1] == ' '))
+ fatal ("invalid diff format; incorrect leading line chars");
+
+ *set_start = line_ptr = scan_ptr + 2;
+ while (*line_ptr++ != '\n')
+ ;
+
+ /* Include newline if the original line ended in a newline,
+ or if an edit script is being generated.
+ Copy any missing newline message to stderr if an edit script is being
+ generated, because edit scripts cannot handle missing newlines.
+ Return the beginning of the next line. */
+ *set_length = line_ptr - *set_start;
+ if (line_ptr < limit && *line_ptr == '\\')
+ {
+ if (edscript)
+ fprintf (stderr, "%s:", argv0);
+ else
+ --*set_length;
+ line_ptr++;
+ do
+ {
+ if (edscript)
+ putc (*line_ptr, stderr);
+ }
+ while (*line_ptr++ != '\n');
+ }
+
+ return line_ptr;
+}
+
+/*
+ * This routine outputs a three way diff passed as a list of
+ * diff3_block's.
+ * The argument MAPPING is indexed by external file number (in the
+ * argument list) and contains the internal file number (from the
+ * diff passed). This is important because the user expects his
+ * outputs in terms of the argument list number, and the diff passed
+ * may have been done slightly differently (if the last argument
+ * was "-", for example).
+ * REV_MAPPING is the inverse of MAPPING.
+ */
+static void
+output_diff3 (outputfile, diff, mapping, rev_mapping)
+ FILE *outputfile;
+ struct diff3_block *diff;
+ int mapping[3], rev_mapping[3];
+{
+ int i;
+ int oddoneout;
+ char *cp;
+ struct diff3_block *ptr;
+ int line;
+ int length;
+ int dontprint;
+ static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
+
+ for (ptr = diff; ptr; ptr = D_NEXT (ptr))
+ {
+ char x[2];
+
+ switch (ptr->correspond)
+ {
+ case DIFF_ALL:
+ x[0] = '\0';
+ dontprint = 3; /* Print them all */
+ oddoneout = 3; /* Nobody's odder than anyone else */
+ break;
+ case DIFF_1ST:
+ case DIFF_2ND:
+ case DIFF_3RD:
+ oddoneout = rev_mapping[(int) ptr->correspond - (int) DIFF_1ST];
+
+ x[0] = oddoneout + '1';
+ x[1] = '\0';
+ dontprint = oddoneout==0;
+ break;
+ default:
+ fatal ("internal error: invalid diff type passed to output");
+ }
+ fprintf (outputfile, "====%s\n", x);
+
+ /* Go 0, 2, 1 if the first and third outputs are equivalent. */
+ for (i = 0; i < 3;
+ i = (oddoneout == 1 ? skew_increment[i] : i + 1))
+ {
+ int realfile = mapping[i];
+ int
+ lowt = D_LOWLINE (ptr, realfile),
+ hight = D_HIGHLINE (ptr, realfile);
+
+ fprintf (outputfile, "%d:", i + 1);
+ switch (lowt - hight)
+ {
+ case 1:
+ fprintf (outputfile, "%da\n", lowt - 1);
+ break;
+ case 0:
+ fprintf (outputfile, "%dc\n", lowt);
+ break;
+ default:
+ fprintf (outputfile, "%d,%dc\n", lowt, hight);
+ break;
+ }
+
+ if (i == dontprint) continue;
+
+ for (line = 0; line < hight - lowt + 1; line++)
+ {
+ fprintf (outputfile, " ");
+ cp = D_RELNUM (ptr, realfile, line);
+ length = D_RELLEN (ptr, realfile, line);
+ fwrite (cp, sizeof (char), length, outputfile);
+ }
+ if (line != 0 && cp[length - 1] != '\n')
+ fprintf (outputfile, "\n\\ No newline at end of file\n");
+ }
+ }
+}
+
+
+/*
+ * Output to OUTPUTFILE the lines of B taken from FILENUM.
+ * Double any initial '.'s; yield nonzero if any initial '.'s were doubled.
+ */
+static int
+dotlines (outputfile, b, filenum)
+ FILE *outputfile;
+ struct diff3_block *b;
+ int filenum;
+{
+ int i;
+ int leading_dot = 0;
+
+ for (i = 0;
+ i < D_NUMLINES (b, filenum);
+ i++)
+ {
+ char *line = D_RELNUM (b, filenum, i);
+ if (line[0] == '.')
+ {
+ leading_dot = 1;
+ fprintf (outputfile, ".");
+ }
+ fwrite (line, sizeof (char),
+ D_RELLEN (b, filenum, i), outputfile);
+ }
+
+ return leading_dot;
+}
+
+/*
+ * Output to OUTPUTFILE a '.' line. If LEADING_DOT is nonzero,
+ * also output a command that removes initial '.'s
+ * starting with line START and continuing for NUM lines.
+ */
+static void
+undotlines (outputfile, leading_dot, start, num)
+ FILE *outputfile;
+ int leading_dot, start, num;
+{
+ fprintf (outputfile, ".\n");
+ if (leading_dot)
+ if (num == 1)
+ fprintf (outputfile, "%ds/^\\.//\n", start);
+ else
+ fprintf (outputfile, "%d,%ds/^\\.//\n", start, start + num - 1);
+}
+
+/*
+ * This routine outputs a diff3 set of blocks as an ed script. This
+ * script applies the changes between file's 2 & 3 to file 1. It
+ * takes the precise format of the ed script to be output from global
+ * variables set during options processing. Note that it does
+ * destructive things to the set of diff3 blocks it is passed; it
+ * reverses their order (this gets around the problems involved with
+ * changing line numbers in an ed script).
+ *
+ * Note that this routine has the same problem of mapping as the last
+ * one did; the variable MAPPING maps from file number according to
+ * the argument list to file number according to the diff passed. All
+ * files listed below are in terms of the argument list.
+ * REV_MAPPING is the inverse of MAPPING.
+ *
+ * The arguments FILE0, FILE1 and FILE2 are the strings to print
+ * as the names of the three files. These may be the actual names,
+ * or may be the arguments specified with -L.
+ *
+ * Returns 1 if conflicts were found.
+ */
+
+static int
+output_diff3_edscript (outputfile, diff, mapping, rev_mapping,
+ file0, file1, file2)
+ FILE *outputfile;
+ struct diff3_block *diff;
+ int mapping[3], rev_mapping[3];
+ char *file0, *file1, *file2;
+{
+ int leading_dot;
+ int conflicts_found = 0, conflict;
+ struct diff3_block *b;
+
+ for (b = reverse_diff3_blocklist (diff); b; b = b->next)
+ {
+ /* Must do mapping correctly. */
+ enum diff_type type
+ = ((b->correspond == DIFF_ALL) ?
+ DIFF_ALL :
+ ((enum diff_type)
+ (((int) DIFF_1ST)
+ + rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
+
+ /* If we aren't supposed to do this output block, skip it. */
+ switch (type)
+ {
+ default: continue;
+ case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
+ case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
+ case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
+ }
+
+ if (conflict)
+ {
+ conflicts_found = 1;
+
+
+ /* Mark end of conflict. */
+
+ fprintf (outputfile, "%da\n", D_HIGHLINE (b, mapping[FILE0]));
+ leading_dot = 0;
+ if (type == DIFF_ALL)
+ {
+ if (show_2nd)
+ {
+ /* Append lines from FILE1. */
+ fprintf (outputfile, "||||||| %s\n", file1);
+ leading_dot = dotlines (outputfile, b, mapping[FILE1]);
+ }
+ /* Append lines from FILE2. */
+ fprintf (outputfile, "=======\n");
+ leading_dot |= dotlines (outputfile, b, mapping[FILE2]);
+ }
+ fprintf (outputfile, ">>>>>>> %s\n", file2);
+ undotlines (outputfile, leading_dot,
+ D_HIGHLINE (b, mapping[FILE0]) + 2,
+ (D_NUMLINES (b, mapping[FILE1])
+ + D_NUMLINES (b, mapping[FILE2]) + 1));
+
+
+ /* Mark start of conflict. */
+
+ fprintf (outputfile, "%da\n<<<<<<< %s\n",
+ D_LOWLINE (b, mapping[FILE0]) - 1,
+ type == DIFF_ALL ? file0 : file1);
+ leading_dot = 0;
+ if (type == DIFF_2ND)
+ {
+ /* Prepend lines from FILE1. */
+ leading_dot = dotlines (outputfile, b, mapping[FILE1]);
+ fprintf (outputfile, "=======\n");
+ }
+ undotlines (outputfile, leading_dot,
+ D_LOWLINE (b, mapping[FILE0]) + 1,
+ D_NUMLINES (b, mapping[FILE1]));
+ }
+ else if (D_NUMLINES (b, mapping[FILE2]) == 0)
+ /* Write out a delete */
+ {
+ if (D_NUMLINES (b, mapping[FILE0]) == 1)
+ fprintf (outputfile, "%dd\n",
+ D_LOWLINE (b, mapping[FILE0]));
+ else
+ fprintf (outputfile, "%d,%dd\n",
+ D_LOWLINE (b, mapping[FILE0]),
+ D_HIGHLINE (b, mapping[FILE0]));
+ }
+ else
+ /* Write out an add or change */
+ {
+ switch (D_NUMLINES (b, mapping[FILE0]))
+ {
+ case 0:
+ fprintf (outputfile, "%da\n",
+ D_HIGHLINE (b, mapping[FILE0]));
+ break;
+ case 1:
+ fprintf (outputfile, "%dc\n",
+ D_HIGHLINE (b, mapping[FILE0]));
+ break;
+ default:
+ fprintf (outputfile, "%d,%dc\n",
+ D_LOWLINE (b, mapping[FILE0]),
+ D_HIGHLINE (b, mapping[FILE0]));
+ break;
+ }
+
+ undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]),
+ D_LOWLINE (b, mapping[FILE0]),
+ D_NUMLINES (b, mapping[FILE2]));
+ }
+ }
+ if (finalwrite) fprintf (outputfile, "w\nq\n");
+ return conflicts_found;
+}
+
+/*
+ * Read from INFILE and output to OUTPUTFILE a set of diff3_ blocks DIFF
+ * as a merged file. This acts like 'ed file0 <[output_diff3_edscript]',
+ * except that it works even for binary data or incomplete lines.
+ *
+ * As before, MAPPING maps from arg list file number to diff file number,
+ * REV_MAPPING is its inverse,
+ * and FILE0, FILE1, and FILE2 are the names of the files.
+ *
+ * Returns 1 if conflicts were found.
+ */
+
+static int
+output_diff3_merge (infile, outputfile, diff, mapping, rev_mapping,
+ file0, file1, file2)
+ FILE *infile, *outputfile;
+ struct diff3_block *diff;
+ int mapping[3], rev_mapping[3];
+ char *file0, *file1, *file2;
+{
+ int c, i;
+ int conflicts_found = 0, conflict;
+ struct diff3_block *b;
+ int linesread = 0;
+
+ for (b = diff; b; b = b->next)
+ {
+ /* Must do mapping correctly. */
+ enum diff_type type
+ = ((b->correspond == DIFF_ALL) ?
+ DIFF_ALL :
+ ((enum diff_type)
+ (((int) DIFF_1ST)
+ + rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
+ char *format_2nd = "<<<<<<< %s\n";
+
+ /* If we aren't supposed to do this output block, skip it. */
+ switch (type)
+ {
+ default: continue;
+ case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
+ case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
+ case DIFF_ALL: if (simple_only) continue; conflict = flagging;
+ format_2nd = "||||||| %s\n";
+ break;
+ }
+
+ /* Copy I lines from file 0. */
+ i = D_LOWLINE (b, FILE0) - linesread - 1;
+ linesread += i;
+ while (0 <= --i)
+ do
+ {
+ c = getc (infile);
+ if (c == EOF)
+ if (ferror (infile))
+ perror_with_exit ("input file");
+ else if (feof (infile))
+ fatal ("input file shrank");
+ putc (c, outputfile);
+ }
+ while (c != '\n');
+
+ if (conflict)
+ {
+ conflicts_found = 1;
+
+ if (type == DIFF_ALL)
+ {
+ /* Put in lines from FILE0 with bracket. */
+ fprintf (outputfile, "<<<<<<< %s\n", file0);
+ for (i = 0;
+ i < D_NUMLINES (b, mapping[FILE0]);
+ i++)
+ fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
+ D_RELLEN (b, mapping[FILE0], i), outputfile);
+ }
+
+ if (show_2nd)
+ {
+ /* Put in lines from FILE1 with bracket. */
+ fprintf (outputfile, format_2nd, file1);
+ for (i = 0;
+ i < D_NUMLINES (b, mapping[FILE1]);
+ i++)
+ fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char),
+ D_RELLEN (b, mapping[FILE1], i), outputfile);
+ }
+
+ fprintf (outputfile, "=======\n");
+ }
+
+ /* Put in lines from FILE2. */
+ for (i = 0;
+ i < D_NUMLINES (b, mapping[FILE2]);
+ i++)
+ fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
+ D_RELLEN (b, mapping[FILE2], i), outputfile);
+
+ if (conflict)
+ fprintf (outputfile, ">>>>>>> %s\n", file2);
+
+ /* Skip I lines in file 0. */
+ i = D_NUMLINES (b, FILE0);
+ linesread += i;
+ while (0 <= --i)
+ while ((c = getc (infile)) != '\n')
+ if (c == EOF)
+ if (ferror (infile))
+ perror_with_exit ("input file");
+ else if (feof (infile))
+ {
+ if (i || b->next)
+ fatal ("input file shrank");
+ return conflicts_found;
+ }
+ }
+ /* Copy rest of common file. */
+ while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
+ putc (c, outputfile);
+ return conflicts_found;
+}
+
+/*
+ * Reverse the order of the list of diff3 blocks.
+ */
+static struct diff3_block *
+reverse_diff3_blocklist (diff)
+ struct diff3_block *diff;
+{
+ register struct diff3_block *tmp, *next, *prev;
+
+ for (tmp = diff, prev = (struct diff3_block *) 0;
+ tmp; tmp = next)
+ {
+ next = tmp->next;
+ tmp->next = prev;
+ prev = tmp;
+ }
+
+ return prev;
+}
+
+static int
+myread (fd, ptr, size)
+ int fd, size;
+ char *ptr;
+{
+ int result = read (fd, ptr, size);
+ if (result < 0)
+ perror_with_exit ("read failed");
+ return result;
+}
+
+VOID *
+xmalloc (size)
+ unsigned size;
+{
+ VOID *result = (VOID *) malloc (size ? size : 1);
+ if (!result)
+ fatal ("virtual memory exhausted");
+ return result;
+}
+
+static VOID *
+xrealloc (ptr, size)
+ VOID *ptr;
+ unsigned size;
+{
+ VOID *result = (VOID *) realloc (ptr, size ? size : 1);
+ if (!result)
+ fatal ("virtual memory exhausted");
+ return result;
+}
+
+static void
+fatal (string)
+ char *string;
+{
+ fprintf (stderr, "%s: %s\n", argv0, string);
+ exit (2);
+}
+
+static void
+perror_with_exit (string)
+ char *string;
+{
+ int e = errno;
+ fprintf (stderr, "%s: ", argv0);
+ errno = e;
+ perror (string);
+ exit (2);
+}
diff --git a/gnu/usr.bin/diff/sdiff.c b/gnu/usr.bin/diff/sdiff.c
new file mode 100644
index 0000000..7f1019e
--- /dev/null
+++ b/gnu/usr.bin/diff/sdiff.c
@@ -0,0 +1,1067 @@
+/* SDIFF -- interactive merge front end to diff
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GNU DIFF.
+
+GNU DIFF is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU DIFF is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU DIFF; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* GNU SDIFF was written by Thomas Lord. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "system.h"
+#include <signal.h>
+#include "getopt.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+/* Size of chunks read from files which must be parsed into lines. */
+#define SDIFF_BUFSIZE 65536
+
+/* Default name of the diff program */
+#ifndef DIFF_PROGRAM
+#define DIFF_PROGRAM "/usr/bin/diff"
+#endif
+
+/* Users' editor of nonchoice */
+#ifndef DEFAULT_EDITOR
+#define DEFAULT_EDITOR "ed"
+#endif
+
+extern char *version_string;
+static char const *prog;
+static char const *diffbin = DIFF_PROGRAM;
+static char const *edbin = DEFAULT_EDITOR;
+
+static char *tmpname;
+static int volatile tmpmade;
+static pid_t volatile diffpid;
+
+struct line_filter;
+static void diffarg (); /* (char *); */
+static void execdiff (); /* (int, char const *, char const *, char const *); */
+static int edit (); /* (struct line_filter *left, int lenl, struct
+ line_filter *right, int lenr, FILE *outfile); */
+static int interact (); /* (struct line_filter *diff,
+ struct line_filter *left,
+ struct line_filter *right, FILE *outfile); */
+static void trapsigs (); /* (void); */
+/* this lossage until the gnu libc conquers the universe */
+#define TMPNAMSIZE 1024
+#define PVT_tmpdir "/tmp"
+static char *private_tempnam (); /* (const char *, const char *, int, int *); */
+static int diraccess ();
+
+/* Options: */
+
+/* name of output file if -o spec'd */
+static char *out_file;
+
+/* do not print common lines if true, set by -s option */
+static int suppress_common_flag;
+
+static struct option longopts[] =
+{
+ {"ignore-blank-lines", 0, NULL, 'B'},
+ {"speed-large-files", 0, NULL, 'H'},
+ {"ignore-matching-lines", 1, NULL, 'I'},
+ {"ignore-all-space", 0, NULL, 'W'}, /* swap W and w for historical reasons */
+ {"text", 0, NULL, 'a'},
+ {"ignore-space-change", 0, NULL, 'b'},
+ {"minimal", 0, NULL, 'd'},
+ {"ignore-case", 0, NULL, 'i'},
+ {"left-column", 0, NULL, 'l'},
+ {"output", 1, NULL, 'o'},
+ {"suppress-common-lines", 0, NULL, 's'},
+ {"expand-tabs", 0, NULL, 't'},
+ {"width", 1, NULL, 'w'},
+ {"version", 0, NULL, 'v'},
+ {NULL, 0, NULL, 0}
+};
+
+/* prints usage message and quits */
+static void
+usage ()
+{
+ fprintf (stderr, "Usage: %s [options] from-file to-file\n", prog);
+ fprintf (stderr, "Options:\n\
+ [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
+ [--text] [--minimal] [--speed-large-files] [--expand-tabs]\n\
+ [--ignore-case] [--ignore-matching-lines=regexp]\n\
+ [--ignore-space-change] [--ignore-blank-lines] [--ignore-all-space]\n\
+ [--suppress-common-lines] [--left-column] [--output=outfile]\n\
+ [--version] [--width=columns]\n");
+ exit (2);
+}
+
+static void
+cleanup ()
+{
+ if (0 < diffpid)
+ kill (diffpid, SIGPIPE);
+ if (tmpmade)
+ unlink (tmpname);
+}
+
+static void
+exiterr ()
+{
+ cleanup ();
+ exit (2);
+}
+
+static void
+fatal (msg)
+ char *msg;
+{
+ fprintf (stderr, "%s: %s\n", prog, msg);
+ exiterr ();
+}
+
+static void
+perror_fatal (msg)
+ char *msg;
+{
+ int e = errno;
+ fprintf (stderr, "%s: ", prog);
+ errno = e;
+ perror (msg);
+ exiterr ();
+}
+
+
+/* malloc freely or DIE! */
+char *
+xmalloc (size)
+ size_t size;
+{
+ char *r = malloc (size);
+ if (!r)
+ fatal ("virtual memory exhausted");
+ return r;
+}
+
+static FILE *
+ck_fopen (fname, type)
+ char *fname, *type;
+{
+ FILE *r = fopen (fname, type);
+ if (!r)
+ perror_fatal (fname);
+ return r;
+}
+
+
+static FILE *
+ck_fdopen (fd, type)
+ int fd;
+ char *type;
+{
+ FILE *r = fdopen (fd, type);
+ if (!r)
+ perror_fatal ("fdopen");
+ return r;
+}
+
+static void
+ck_fclose (f)
+ FILE *f;
+{
+ if (fclose (f))
+ perror_fatal ("input/output error");
+}
+
+static size_t
+ck_fread (buf, size, f)
+ char *buf;
+ size_t size;
+ FILE *f;
+{
+ size_t r = fread (buf, sizeof (char), size, f);
+ if (r == 0 && ferror (f))
+ perror_fatal ("input error");
+ return r;
+}
+
+static void
+ck_fwrite (buf, size, f)
+ char *buf;
+ size_t size;
+ FILE *f;
+{
+ if (fwrite (buf, sizeof (char), size, f) != size)
+ perror_fatal ("output error");
+}
+
+static void
+ck_fflush (f)
+ FILE *f;
+{
+ if (fflush (f) != 0)
+ perror_fatal ("output error");
+}
+
+#if !HAVE_MEMCHR
+char *
+memchr (s, c, n)
+ char *s;
+ int c;
+ size_t n;
+{
+ unsigned char *p = (unsigned char *) s, *lim = p + n;
+ for (; p < lim; p++)
+ if (*p == c)
+ return (char *) p;
+ return 0;
+}
+#endif
+
+#ifndef HAVE_WAITPID
+/* Emulate waitpid well enough for sdiff, which has at most two children. */
+static pid_t
+waitpid (pid, stat_loc, options)
+ pid_t pid;
+ int *stat_loc;
+ int options;
+{
+ static int ostatus;
+ static pid_t opid;
+ int npid, status;
+
+ if (pid == opid)
+ {
+ opid = 0;
+ status = ostatus;
+ }
+ else
+ while ((npid = wait (&status)) != pid)
+ {
+ if (npid < 0)
+ return npid;
+ opid = npid;
+ ostatus = status;
+ }
+ *stat_loc = status;
+ return pid;
+}
+#endif
+
+static char const *
+expand_name (name, isdir, other_name)
+ char *name;
+ int isdir;
+ char const *other_name;
+{
+ if (strcmp (name, "-") == 0)
+ fatal ("cannot interactively merge standard input");
+ if (!isdir)
+ return name;
+ else
+ {
+ /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */
+ const char
+ *p = rindex (other_name, '/'),
+ *base = p ? p+1 : other_name;
+ size_t namelen = strlen (name), baselen = strlen (base);
+ char *r = xmalloc (namelen + baselen + 2);
+ bcopy (name, r, namelen);
+ r[namelen] = '/';
+ bcopy (base, r + namelen + 1, baselen + 1);
+ return r;
+ }
+}
+
+
+
+struct line_filter {
+ FILE *infile;
+ char *bufpos;
+ char *buffer;
+ char *buflim;
+};
+
+static void
+lf_init (lf, infile)
+ struct line_filter *lf;
+ FILE *infile;
+{
+ lf->infile = infile;
+ lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
+ lf->buflim[0] = '\n';
+}
+
+/* Fill an exhausted line_filter buffer from its INFILE */
+static size_t
+lf_refill (lf)
+ struct line_filter *lf;
+{
+ size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
+ lf->bufpos = lf->buffer;
+ lf->buflim = lf->buffer + s;
+ lf->buflim[0] = '\n';
+ return s;
+}
+
+/* Advance LINES on LF's infile, copying lines to OUTFILE */
+static void
+lf_copy (lf, lines, outfile)
+ struct line_filter *lf;
+ int lines;
+ FILE *outfile;
+{
+ char *start = lf->bufpos;
+
+ while (lines)
+ {
+ lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
+ if (! lf->bufpos)
+ {
+ ck_fwrite (start, lf->buflim - start, outfile);
+ if (! lf_refill (lf))
+ return;
+ start = lf->bufpos;
+ }
+ else
+ {
+ --lines;
+ ++lf->bufpos;
+ }
+ }
+
+ ck_fwrite (start, lf->bufpos - start, outfile);
+}
+
+/* Advance LINES on LF's infile without doing output */
+static void
+lf_skip (lf, lines)
+ struct line_filter *lf;
+ int lines;
+{
+ while (lines)
+ {
+ lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
+ if (! lf->bufpos)
+ {
+ if (! lf_refill (lf))
+ break;
+ }
+ else
+ {
+ --lines;
+ ++lf->bufpos;
+ }
+ }
+}
+
+/* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */
+static int
+lf_snarf (lf, buffer, bufsize)
+ struct line_filter *lf;
+ char *buffer;
+ size_t bufsize;
+{
+ char *start = lf->bufpos;
+
+ for (;;)
+ {
+ char *next = memchr (start, '\n', lf->buflim + 1 - start);
+ size_t s = next - start;
+ if (bufsize <= s)
+ return 0;
+ bcopy (start, buffer, s);
+ if (next < lf->buflim)
+ {
+ buffer[s] = 0;
+ lf->bufpos = next + 1;
+ return 1;
+ }
+ if (! lf_refill (lf))
+ return s ? 0 : EOF;
+ buffer += s;
+ bufsize -= s;
+ start = next;
+ }
+}
+
+
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int opt;
+ int version_requested = 0;
+ char *editor = getenv ("EDITOR");
+ char *differ = getenv ("DIFF");
+
+ prog = argv[0];
+ if (editor)
+ edbin = editor;
+ if (differ)
+ diffbin = differ;
+
+ diffarg ("diff");
+
+ /* parse command line args */
+ while ((opt=getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, (int *)0)) != EOF)
+ {
+ switch (opt)
+ {
+ case 'a':
+ diffarg ("-a");
+ break;
+
+ case 'b':
+ diffarg ("-b");
+ break;
+
+ case 'B':
+ diffarg ("-B");
+ break;
+
+ case 'd':
+ diffarg ("-d");
+ break;
+
+ case 'H':
+ diffarg ("-H");
+ break;
+
+ case 'i':
+ diffarg ("-i");
+ break;
+
+ case 'I':
+ diffarg ("-I");
+ diffarg (optarg);
+ break;
+
+ case 'l':
+ diffarg ("--left-column");
+ break;
+
+ case 'o':
+ out_file = optarg;
+ break;
+
+ case 's':
+ suppress_common_flag = 1;
+ break;
+
+ case 't':
+ diffarg ("-t");
+ break;
+
+ case 'v':
+ version_requested = 1;
+ fprintf (stderr, "GNU sdiff version %s\n", version_string);
+ ck_fflush (stderr);
+ break;
+
+ case 'w':
+ diffarg ("-W");
+ diffarg (optarg);
+ break;
+
+ case 'W':
+ diffarg ("-w");
+ break;
+
+ default:
+ usage ();
+ }
+ }
+
+ /* check: did user just want version message? if so exit. */
+ if (version_requested && argc - optind == 0)
+ exit (0);
+
+ if (argc - optind != 2)
+ usage ();
+
+ if (! out_file)
+ /* easy case: diff does everything for us */
+ execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1]);
+ else
+ {
+ FILE *left, *right, *out, *diffout;
+ int diff_fds[2];
+ int interact_ok;
+ pid_t pid;
+ struct line_filter lfilt;
+ struct line_filter rfilt;
+ struct line_filter diff_filt;
+ int leftdir = diraccess (argv[optind]);
+ int rightdir = diraccess (argv[optind + 1]);
+
+ if (leftdir && rightdir)
+ fatal ("both files to be compared are directories");
+
+ left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
+ ;
+ right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
+ out = ck_fopen (out_file, "w");
+
+ if (pipe (diff_fds))
+ perror_fatal ("pipe");
+
+ trapsigs ();
+
+ diffpid = pid = vfork ();
+
+ if (pid == 0)
+ {
+ signal (SIGINT, SIG_IGN); /* in case user interrupts editor */
+ signal (SIGPIPE, SIG_DFL);
+
+ close (diff_fds[0]);
+ if (diff_fds[1] != fileno (stdout))
+ {
+ dup2 (diff_fds[1], fileno (stdout));
+ close (diff_fds[1]);
+ }
+
+ execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1]);
+ }
+
+ if (pid < 0)
+ perror_fatal ("fork failed");
+
+ close (diff_fds[1]);
+ diffout = ck_fdopen (diff_fds[0], "r");
+
+ lf_init (&diff_filt, diffout);
+ lf_init (&lfilt, left);
+ lf_init (&rfilt, right);
+
+ interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
+
+ ck_fclose (diffout);
+ ck_fclose (left);
+ ck_fclose (right);
+ ck_fclose (out);
+
+ {
+ int wstatus;
+
+ if (waitpid (pid, &wstatus, 0) < 0)
+ perror_fatal ("wait failed");
+ diffpid = 0;
+
+ if (tmpmade)
+ {
+ unlink (tmpname);
+ tmpmade = 0;
+ }
+
+ if (! interact_ok)
+ exit (2);
+
+ if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
+ fatal ("Subsidiary diff failed");
+
+ exit (WEXITSTATUS (wstatus));
+ }
+ }
+ return 0; /* Fool -Wall . . . */
+}
+
+static char **diffargv;
+
+static void
+diffarg (a)
+ char *a;
+{
+ static unsigned diffargs, diffargsmax;
+
+ if (diffargs == diffargsmax)
+ {
+ if (! diffargsmax)
+ {
+ diffargv = (char **) xmalloc (sizeof (char));
+ diffargsmax = 8;
+ }
+ diffargsmax *= 2;
+ diffargv = (char **) realloc (diffargv, diffargsmax * sizeof (char *));
+ if (! diffargv)
+ fatal ("out of memory");
+ }
+ diffargv[diffargs++] = a;
+}
+
+static void
+execdiff (differences_only, option, file1, file2)
+ int differences_only;
+ char *option, *file1, *file2;
+{
+ if (differences_only)
+ diffarg ("--suppress-common-lines");
+ diffarg (option);
+ diffarg ("--");
+ diffarg (file1);
+ diffarg (file2);
+ diffarg (0);
+
+ execvp (diffbin, diffargv);
+ write (fileno (stderr), diffbin, strlen (diffbin));
+ write (fileno (stderr), ": not found\n", 12);
+ _exit (2);
+}
+
+
+
+
+/* Signal handling */
+
+static int volatile ignore_signals;
+
+static void
+catchsig (s)
+ int s;
+{
+ signal (s, catchsig);
+ if (! ignore_signals)
+ {
+ cleanup ();
+ _exit (2);
+ }
+}
+
+static void
+trapsigs ()
+{
+ static int const sigs[] = {
+# ifdef SIGHUP
+ SIGHUP,
+# endif
+# ifdef SIGQUIT
+ SIGQUIT,
+# endif
+# ifdef SIGTERM
+ SIGTERM,
+# endif
+# ifdef SIGXCPU
+ SIGXCPU,
+# endif
+# ifdef SIGXFSZ
+ SIGXFSZ,
+# endif
+ SIGINT,
+ SIGPIPE
+ };
+ int const *p;
+
+ for (p = sigs; p < sigs + sizeof (sigs) / sizeof (*sigs); p++)
+ if (signal (*p, SIG_IGN) != SIG_IGN && signal (*p, catchsig) != SIG_IGN)
+ fatal ("signal error");
+}
+
+
+
+static void
+give_help ()
+{
+ fprintf (stderr,"l:\tuse the left version\n");
+ fprintf (stderr,"r:\tuse the right version\n");
+ fprintf (stderr,"e l:\tedit then use the left version\n");
+ fprintf (stderr,"e r:\tedit then use the right version\n");
+ fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
+ fprintf (stderr,"e:\tedit a new version\n");
+ fprintf (stderr,"s:\tsilently include common lines\n");
+ fprintf (stderr,"v:\tverbosely include common lines\n");
+ fprintf (stderr,"q:\tquit\n");
+}
+
+static int
+skip_white ()
+{
+ int c;
+ while (isspace (c = getchar ()) && c != '\n')
+ ;
+ if (ferror (stdin))
+ perror_fatal ("input error");
+ return c;
+}
+
+static void
+flush_line ()
+{
+ int c;
+ while ((c = getchar ()) != '\n' && c != EOF)
+ ;
+ if (ferror (stdin))
+ perror_fatal ("input error");
+}
+
+
+/* interpret an edit command */
+static int
+edit (left, lenl, right, lenr, outfile)
+ struct line_filter *left;
+ int lenl;
+ struct line_filter *right;
+ int lenr;
+ FILE *outfile;
+{
+ for (;;)
+ {
+ int cmd0, cmd1;
+ int gotcmd = 0;
+
+ while (!gotcmd)
+ {
+ if (putchar ('%') != '%')
+ perror_fatal ("output error");
+ ck_fflush (stdout);
+
+ cmd0 = skip_white ();
+ switch (cmd0)
+ {
+ case 'l': case 'r': case 's': case 'v': case 'q':
+ if (skip_white () != '\n')
+ {
+ give_help ();
+ flush_line ();
+ continue;
+ }
+ gotcmd = 1;
+ break;
+
+ case 'e':
+ cmd1 = skip_white ();
+ switch (cmd1)
+ {
+ case 'l': case 'r': case 'b':
+ if (skip_white () != '\n')
+ {
+ give_help ();
+ flush_line ();
+ continue;
+ }
+ gotcmd = 1;
+ break;
+ case '\n':
+ gotcmd = 1;
+ break;
+ default:
+ give_help ();
+ flush_line ();
+ continue;
+ }
+ break;
+ case EOF:
+ if (feof (stdin))
+ {
+ gotcmd = 1;
+ cmd0 = 'q';
+ break;
+ }
+ /* falls through */
+ default:
+ give_help ();
+ flush_line ();
+ continue;
+ }
+ }
+
+ switch (cmd0)
+ {
+ case 'l':
+ lf_copy (left, lenl, outfile);
+ lf_skip (right, lenr);
+ return 1;
+ case 'r':
+ lf_copy (right, lenr, outfile);
+ lf_skip (left, lenl);
+ return 1;
+ case 's':
+ suppress_common_flag = 1;
+ break;
+ case 'v':
+ suppress_common_flag = 0;
+ break;
+ case 'q':
+ return 0;
+ case 'e':
+ if (! tmpname && ! (tmpname = private_tempnam (0, "sdiff", 1, 0)))
+ perror_fatal ("temporary file name");
+
+ tmpmade = 1;
+
+ {
+ FILE *tmp = ck_fopen (tmpname, "w+");
+
+ if (cmd1 == 'l' || cmd1 == 'b')
+ lf_copy (left, lenl, tmp);
+ else
+ lf_skip (left, lenl);
+
+ if (cmd1 == 'r' || cmd1 == 'b')
+ lf_copy (right, lenr, tmp);
+ else
+ lf_skip (right, lenr);
+
+ ck_fflush (tmp);
+
+ {
+ pid_t pid;
+ int wstatus;
+
+ ignore_signals = 1;
+
+ pid = vfork ();
+ if (pid == 0)
+ {
+ char const *argv[3];
+ int i = 0;
+
+ argv[i++] = edbin;
+ argv[i++] = tmpname;
+ argv[i++] = 0;
+
+ execvp (edbin, (char **) argv);
+ write (fileno (stderr), edbin, strlen (edbin));
+ write (fileno (stderr), ": not found\n", 12);
+ _exit (1);
+ }
+
+ if (pid < 0)
+ perror_fatal ("fork failed");
+
+ while (waitpid (pid, &wstatus, 0) < 0)
+ if (errno != EINTR)
+ perror_fatal ("wait failed");
+
+ ignore_signals = 0;
+
+ if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1))
+ fatal ("Subsidiary editor failed");
+ }
+
+ if (fseek (tmp, 0L, SEEK_SET) != 0)
+ perror_fatal ("fseek");
+ {
+ /* SDIFF_BUFSIZE is too big for a local var
+ in some compilers, so we allocate it dynamically. */
+ char *buf = (char *) xmalloc (SDIFF_BUFSIZE);
+ size_t size;
+
+ while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
+ ck_fwrite (buf, size, outfile);
+ ck_fclose (tmp);
+
+ free (buf);
+ }
+ return 1;
+ }
+ default:
+ give_help ();
+ break;
+ }
+ }
+}
+
+
+
+/* Alternately reveal bursts of diff output and handle user editing comands. */
+static int
+interact (diff, left, right, outfile)
+ struct line_filter *diff;
+ struct line_filter *left;
+ struct line_filter *right;
+ FILE *outfile;
+{
+ for (;;)
+ {
+ char diff_help[256];
+ int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
+
+ if (snarfed <= 0)
+ return snarfed;
+
+ switch (diff_help[0])
+ {
+ case ' ':
+ puts (diff_help + 1);
+ break;
+ case 'i':
+ {
+ int lenl = atoi (diff_help + 1), lenr, lenmax;
+ char *p = index (diff_help, ',');
+
+ if (!p)
+ fatal (diff_help);
+ lenr = atoi (p + 1);
+ lenmax = max (lenl, lenr);
+
+ if (suppress_common_flag)
+ lf_skip (diff, lenmax);
+ else
+ lf_copy (diff, lenmax, stdout);
+
+ lf_copy (left, lenl, outfile);
+ lf_skip (right, lenr);
+ break;
+ }
+ case 'c':
+ {
+ int lenl = atoi (diff_help + 1), lenr;
+ char *p = index (diff_help, ',');
+
+ if (!p)
+ fatal (diff_help);
+ lenr = atoi (p + 1);
+ lf_copy (diff, max (lenl, lenr), stdout);
+ if (! edit (left, lenl, right, lenr, outfile))
+ return 0;
+ break;
+ }
+ default:
+ fatal (diff_help);
+ break;
+ }
+ }
+}
+
+
+
+/* temporary lossage: this is torn from gnu libc */
+/* Return nonzero if DIR is an existent directory. */
+static int
+diraccess (dir)
+ const char *dir;
+{
+ struct stat buf;
+ return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
+}
+
+/* Return nonzero if FILE exists. */
+static int
+exists (file)
+ const char *file;
+{
+ struct stat buf;
+ return stat (file, &buf) == 0;
+}
+
+/* These are the characters used in temporary filenames. */
+static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/* Generate a temporary filename.
+ If DIR_SEARCH is nonzero, DIR and PFX are used as
+ described for tempnam. If not, a temporary filename
+ in P_tmpdir with no special prefix is generated. If LENPTR
+ is not NULL, *LENPTR is set the to length (including the
+ terminating '\0') of the resultant filename, which is returned.
+ This goes through a cyclic pattern of all possible filenames
+ consisting of five decimal digits of the current pid and three
+ of the characters in `letters'. Data for tempnam and tmpnam
+ is kept separate, but when tempnam is using P_tmpdir and no
+ prefix (i.e, it is identical to tmpnam), the same data is used.
+ Each potential filename is tested for an already-existing file of
+ the same name, and no name of an existing file will be returned.
+ When the cycle reaches its end (12345ZZZ), NULL is returned. */
+
+
+static char *
+private_tempnam (dir, pfx, dir_search, lenptr)
+ const char *dir;
+ const char *pfx;
+ int dir_search;
+ size_t *lenptr;
+{
+ static const char tmpdir[] = PVT_tmpdir;
+ static struct
+ {
+ char buf[3];
+ char *s;
+ size_t i;
+ } infos[2], *info;
+ static char buf[TMPNAMSIZE];
+ static pid_t oldpid = 0;
+ pid_t pid = getpid ();
+ register size_t len, plen;
+
+ if (dir_search)
+ {
+ register const char *d = getenv ("TMPDIR");
+ if (d != NULL && !diraccess (d))
+ d = NULL;
+ if (d == NULL && dir != NULL && diraccess (dir))
+ d = dir;
+ if (d == NULL && diraccess (tmpdir))
+ d = tmpdir;
+ if (d == NULL && diraccess ("/tmp"))
+ d = "/tmp";
+ if (d == NULL)
+ {
+ errno = ENOENT;
+ return NULL;
+ }
+ dir = d;
+ }
+ else
+ dir = tmpdir;
+
+ if (pfx != NULL && *pfx != '\0')
+ {
+ plen = strlen (pfx);
+ if (plen > 5)
+ plen = 5;
+ }
+ else
+ plen = 0;
+
+ if (dir != tmpdir && !strcmp (dir, tmpdir))
+ dir = tmpdir;
+ info = &infos[(plen == 0 && dir == tmpdir) ? 1 : 0];
+
+ if (pid != oldpid)
+ {
+ oldpid = pid;
+ info->buf[0] = info->buf[1] = info->buf[2] = '0';
+ info->s = &info->buf[0];
+ info->i = 0;
+ }
+
+ len = strlen (dir) + 1 + plen + 8;
+ for (;;)
+ {
+ *info->s = letters[info->i];
+ sprintf (buf, "%s/%.*s%.5d%.3s", dir, (int) plen, pfx,
+ pid % 100000, info->buf);
+ if (!exists (buf))
+ break;
+ ++info->i;
+ if (info->i > sizeof (letters) - 1)
+ {
+ info->i = 0;
+ if (info->s == &info->buf[2])
+ {
+ errno = EEXIST;
+ return NULL;
+ }
+ ++info->s;
+ }
+ }
+
+ if (lenptr != NULL)
+ *lenptr = len;
+ return buf;
+}
OpenPOWER on IntegriCloud