From ec9763539f0d7dec8cd59a3ff333c9b49b3a6fe0 Mon Sep 17 00:00:00 2001
From: Peter Wemm <peter@FreeBSD.org>
Date: Sat, 28 Oct 1995 18:51:33 +0000
Subject: [PATCH] Import GNU diffutils 2.7

Note, this is going to be messy.. 2.3 was vendor-branch imported, while
2.6 was done as a delta. Sigh.  I'm importing this on a vendor branch so
that it will be easier to deal with next time..

(cvs-1.6 wants rcs-5.7, and rcs-5.7 suggests diffutils-2.7)
---
 gnu/usr.bin/diff/COPYING   |  339 +++++++
 gnu/usr.bin/diff/NEWS      |  126 +++
 gnu/usr.bin/diff/analyze.c | 1084 ++++++++++++++++++++++
 gnu/usr.bin/diff/cmpbuf.c  |   40 +
 gnu/usr.bin/diff/cmpbuf.h  |   20 +
 gnu/usr.bin/diff/config.h  |  118 +++
 gnu/usr.bin/diff/context.c |  468 ++++++++++
 gnu/usr.bin/diff/diff.c    | 1106 ++++++++++++++++++++++
 gnu/usr.bin/diff/diff.h    |  340 +++++++
 gnu/usr.bin/diff/diff3.c   | 1778 ++++++++++++++++++++++++++++++++++++
 gnu/usr.bin/diff/dir.c     |  216 +++++
 gnu/usr.bin/diff/ed.c      |  200 ++++
 gnu/usr.bin/diff/fnmatch.c |  209 +++++
 gnu/usr.bin/diff/fnmatch.h |   69 ++
 gnu/usr.bin/diff/getopt.c  |  748 +++++++++++++++
 gnu/usr.bin/diff/getopt1.c |  180 ++++
 gnu/usr.bin/diff/ifdef.c   |  428 +++++++++
 gnu/usr.bin/diff/io.c      |  714 +++++++++++++++
 gnu/usr.bin/diff/normal.c  |   71 ++
 gnu/usr.bin/diff/sdiff.c   | 1180 ++++++++++++++++++++++++
 gnu/usr.bin/diff/side.c    |  284 ++++++
 gnu/usr.bin/diff/system.h  |  267 ++++++
 gnu/usr.bin/diff/util.c    |  754 +++++++++++++++
 gnu/usr.bin/diff/version.c |    5 +
 gnu/usr.bin/diff/xmalloc.c |   81 ++
 25 files changed, 10825 insertions(+)
 create mode 100644 gnu/usr.bin/diff/COPYING
 create mode 100644 gnu/usr.bin/diff/NEWS
 create mode 100644 gnu/usr.bin/diff/analyze.c
 create mode 100644 gnu/usr.bin/diff/cmpbuf.c
 create mode 100644 gnu/usr.bin/diff/cmpbuf.h
 create mode 100644 gnu/usr.bin/diff/config.h
 create mode 100644 gnu/usr.bin/diff/context.c
 create mode 100644 gnu/usr.bin/diff/diff.c
 create mode 100644 gnu/usr.bin/diff/diff.h
 create mode 100644 gnu/usr.bin/diff/diff3.c
 create mode 100644 gnu/usr.bin/diff/dir.c
 create mode 100644 gnu/usr.bin/diff/ed.c
 create mode 100644 gnu/usr.bin/diff/fnmatch.c
 create mode 100644 gnu/usr.bin/diff/fnmatch.h
 create mode 100644 gnu/usr.bin/diff/getopt.c
 create mode 100644 gnu/usr.bin/diff/getopt1.c
 create mode 100644 gnu/usr.bin/diff/ifdef.c
 create mode 100644 gnu/usr.bin/diff/io.c
 create mode 100644 gnu/usr.bin/diff/normal.c
 create mode 100644 gnu/usr.bin/diff/sdiff.c
 create mode 100644 gnu/usr.bin/diff/side.c
 create mode 100644 gnu/usr.bin/diff/system.h
 create mode 100644 gnu/usr.bin/diff/util.c
 create mode 100644 gnu/usr.bin/diff/version.c
 create mode 100644 gnu/usr.bin/diff/xmalloc.c

diff --git a/gnu/usr.bin/diff/COPYING b/gnu/usr.bin/diff/COPYING
new file mode 100644
index 000000000000..a43ea2126fb6
--- /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/NEWS b/gnu/usr.bin/diff/NEWS
new file mode 100644
index 000000000000..dcde1227d308
--- /dev/null
+++ b/gnu/usr.bin/diff/NEWS
@@ -0,0 +1,126 @@
+User-visible changes in version 2.7:
+
+* New diff option: --binary (useful only on non-Posix hosts)
+* diff -b and -w now ignore line incompleteness; -B no longer does this.
+* cmp -c now uses locale to decide which output characters to quote.
+* Help and version messages are reorganized.
+
+
+User-visible changes in version 2.6:
+
+* New cmp, diff, diff3, sdiff option: --help
+* A new heuristic for diff greatly reduces the time needed to compare
+  large input files that contain many differences.
+* Partly as a result, GNU diff's output is not exactly the same as before.
+  Usually it is a bit smaller, but sometimes it is a bit larger.
+
+
+User-visible changes in version 2.5:
+
+* New cmp option: -v --version
+
+
+User-visible changes in version 2.4:
+
+* New cmp option: --ignore-initial=BYTES
+* New diff3 option: -T --initial-tab
+* New diff option: --line-format=FORMAT
+* New diff group format specifications:
+  <PRINTF_SPEC>[eflmnEFLMN]
+      A printf spec followed by one of the following letters
+      causes the integer corresponding to that letter to be
+      printed according to the printf specification.
+      E.g. `%5df' prints the number of the first line in the
+      group in the old file using the "%5d" format.
+	e: line number just before the group in old file; equals f - 1
+	f: first line number in group in the old file
+	l: last line number in group in the old file
+	m: line number just after the group in old file; equals l + 1
+	n: number of lines in group in the old file; equals l - f + 1
+	E, F, L, M, N: likewise, for lines in the new file
+  %(A=B?T:E)
+      If A equals B then T else E.  A and B are each either a decimal
+      constant or a single letter interpreted as above.  T and E are
+      arbitrary format strings.  This format spec is equivalent to T if
+      A's value equals B's; otherwise it is equivalent to E.  For
+      example, `%(N=0?no:%dN) line%(N=1?:s)' is equivalent to `no lines'
+      if N (the number of lines in the group in the the new file) is 0,
+      to `1 line' if N is 1, and to `%dN lines' otherwise.
+  %c'C'
+      where C is a single character, stands for the character C.  C may not
+      be a backslash or an apostrophe.  E.g. %c':' stands for a colon.
+  %c'\O'
+      where O is a string of 1, 2, or 3 octal digits, stands for the
+      character with octal code O.  E.g. %c'\0' stands for a null character.
+* New diff line format specifications:
+  <PRINTF_SPEC>n
+      The line number, printed with <PRINTF_SPEC>.
+      E.g. `%5dn' prints the line number with a "%5d" format.
+  %c'C'
+  %c'\O'
+      The character C, or with octal code O, as above.
+* Supported <PRINTF_SPEC>s have the same meaning as with printf, but must
+  match the extended regular expression %-*[0-9]*(\.[0-9]*)?[doxX].
+* The format spec %0 introduced in version 2.1 has been removed, since it
+  is incompatible with printf specs like %02d.  To represent a null char,
+  use %c'\0' instead.
+* cmp and diff now conform to Posix.2 (ISO/IEC 9945-2:1993)
+  if the underlying system conforms to Posix:
+  - Some messages' wordings are changed in minor ways.
+  - ``White space'' is now whatever C's `isspace' says it is.
+  - When comparing directories, if `diff' finds a file that is not a regular
+    file or a directory, it reports the file's type instead of diffing it.
+    (As usual, it follows symbolic links first.)
+  - When signaled, sdiff exits with the signal's status, not with status 2.
+* Now portable to hosts where int, long, pointer, etc. are not all the same
+  size.
+* `cmp - -' now works like `diff - -'.
+
+
+User-visible changes in version 2.3:
+
+* New diff option: --horizon-lines=lines
+
+
+User-visible changes in version 2.1:
+
+* New diff options:
+  --{old,new,unchanged}-line-format='format'
+  --{old,new,unchanged,changed}-group-format='format'
+  -U
+* New diff3 option:
+  -A --show-all
+* diff3 -m now defaults to -A, not -E.
+* diff3 now takes up to three -L or --label options, not just two.
+  If just two options are given, they refer to the first two input files,
+  not the first and third input files.
+* sdiff and diff -y handle incomplete lines.
+
+
+User-visible changes in version 2.0:
+
+* Add sdiff and cmp programs.
+* Add Texinfo documentation.
+* Add configure script.
+* Improve diff performance.
+* New diff options:
+-x --exclude
+-X --exclude-from
+-P --unidirectional-new-file
+-W --width
+-y --side-by-side
+--left-column
+--sdiff-merge-assist
+--suppress-common-lines
+* diff options renamed:
+--label renamed from --file-label
+--forward-ed renamed from --reversed-ed
+--paginate renamed from --print
+--entire-new-file renamed from --entire-new-files
+--new-file renamed from --new-files
+--all-text removed
+* New diff3 options:
+-v --version
+* Add long-named equivalents for other diff3 options.
+* diff options -F (--show-function-line) and -I (--ignore-matching-lines)
+  can now be given more than once.
diff --git a/gnu/usr.bin/diff/analyze.c b/gnu/usr.bin/diff/analyze.c
new file mode 100644
index 000000000000..556d388c46a4
--- /dev/null
+++ b/gnu/usr.bin/diff/analyze.c
@@ -0,0 +1,1084 @@
+/* Analyze file differences for GNU DIFF.
+   Copyright (C) 1988, 1989, 1992, 1993 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.  */
+
+/* The basic algorithm is described in:
+   "An O(ND) Difference Algorithm and its Variations", Eugene Myers,
+   Algorithmica Vol. 1 No. 2, 1986, pp. 251-266;
+   see especially section 4.2, which describes the variation used below.
+   Unless the --minimal option is specified, this code uses the TOO_EXPENSIVE
+   heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N)
+   at the price of producing suboptimal output for large inputs with
+   many differences.
+
+   The basic algorithm was independently discovered as described in:
+   "Algorithms for Approximate String Matching", E. Ukkonen,
+   Information and Control Vol. 64, 1985, pp. 100-118.  */
+
+#include "diff.h"
+#include "cmpbuf.h"
+
+extern int no_discards;
+
+static int *xvec, *yvec;	/* Vectors being compared. */
+static int *fdiag;		/* Vector, indexed by diagonal, containing
+				   1 + the X coordinate of the point furthest
+				   along the given diagonal in the forward
+				   search of the edit matrix. */
+static int *bdiag;		/* Vector, indexed by diagonal, containing
+				   the X coordinate of the point furthest
+				   along the given diagonal in the backward
+				   search of the edit matrix. */
+static int too_expensive;	/* Edit scripts longer than this are too
+				   expensive to compute.  */
+
+#define SNAKE_LIMIT 20	/* Snakes bigger than this are considered `big'.  */
+
+struct partition
+{
+  int xmid, ymid;	/* Midpoints of this partition.  */
+  int lo_minimal;	/* Nonzero if low half will be analyzed minimally.  */
+  int hi_minimal;	/* Likewise for high half.  */
+};
+
+static int diag PARAMS((int, int, int, int, int, struct partition *));
+static struct change *add_change PARAMS((int, int, int, int, struct change *));
+static struct change *build_reverse_script PARAMS((struct file_data const[]));
+static struct change *build_script PARAMS((struct file_data const[]));
+static void briefly_report PARAMS((int, struct file_data const[]));
+static void compareseq PARAMS((int, int, int, int, int));
+static void discard_confusing_lines PARAMS((struct file_data[]));
+static void shift_boundaries PARAMS((struct file_data[]));
+
+/* Find the midpoint of the shortest edit script for a specified
+   portion of the two files.
+
+   Scan from the beginnings of the files, and simultaneously from the ends,
+   doing a breadth-first search through the space of edit-sequence.
+   When the two searches meet, we have found the midpoint of the shortest
+   edit sequence.
+
+   If MINIMAL is nonzero, find the minimal edit script regardless
+   of expense.  Otherwise, if the search is too expensive, use
+   heuristics to stop the search and report a suboptimal answer.
+
+   Set PART->(XMID,YMID) to the midpoint (XMID,YMID).  The diagonal number
+   XMID - YMID equals the number of inserted lines minus the number
+   of deleted lines (counting only lines before the midpoint).
+   Return the approximate edit cost; this is the total number of
+   lines inserted or deleted (counting only lines before the midpoint),
+   unless a heuristic is used to terminate the search prematurely.
+
+   Set PART->LEFT_MINIMAL to nonzero iff the minimal edit script for the
+   left half of the partition is known; similarly for PART->RIGHT_MINIMAL.
+
+   This function assumes that the first lines of the specified portions
+   of the two files do not match, and likewise that the last lines do not
+   match.  The caller must trim matching lines from the beginning and end
+   of the portions it is going to specify.
+
+   If we return the "wrong" partitions,
+   the worst this can do is cause suboptimal diff output.
+   It cannot cause incorrect diff output.  */
+
+static int
+diag (xoff, xlim, yoff, ylim, minimal, part)
+     int xoff, xlim, yoff, ylim, minimal;
+     struct partition *part;
+{
+  int *const fd = fdiag;	/* Give the compiler a chance. */
+  int *const bd = bdiag;	/* Additional help for the compiler. */
+  int const *const xv = xvec;	/* Still more help for the compiler. */
+  int const *const yv = yvec;	/* And more and more . . . */
+  int const dmin = xoff - ylim;	/* Minimum valid diagonal. */
+  int const dmax = xlim - yoff;	/* Maximum valid diagonal. */
+  int const fmid = xoff - yoff;	/* Center diagonal of top-down search. */
+  int const bmid = xlim - ylim;	/* Center diagonal of bottom-up search. */
+  int fmin = fmid, fmax = fmid;	/* Limits of top-down search. */
+  int bmin = bmid, bmax = bmid;	/* Limits of bottom-up search. */
+  int c;			/* Cost. */
+  int odd = (fmid - bmid) & 1;	/* True if southeast corner is on an odd
+				   diagonal with respect to the northwest. */
+
+  fd[fmid] = xoff;
+  bd[bmid] = xlim;
+
+  for (c = 1;; ++c)
+    {
+      int d;			/* Active diagonal. */
+      int big_snake = 0;
+
+      /* Extend the top-down search by an edit step in each diagonal. */
+      fmin > dmin ? fd[--fmin - 1] = -1 : ++fmin;
+      fmax < dmax ? fd[++fmax + 1] = -1 : --fmax;
+      for (d = fmax; d >= fmin; d -= 2)
+	{
+	  int x, y, oldx, tlo = fd[d - 1], thi = fd[d + 1];
+
+	  if (tlo >= thi)
+	    x = tlo + 1;
+	  else
+	    x = thi;
+	  oldx = x;
+	  y = x - d;
+	  while (x < xlim && y < ylim && xv[x] == yv[y])
+	    ++x, ++y;
+	  if (x - oldx > SNAKE_LIMIT)
+	    big_snake = 1;
+	  fd[d] = x;
+	  if (odd && bmin <= d && d <= bmax && bd[d] <= x)
+	    {
+	      part->xmid = x;
+	      part->ymid = y;
+	      part->lo_minimal = part->hi_minimal = 1;
+	      return 2 * c - 1;
+	    }
+	}
+
+      /* Similarly extend the bottom-up search.  */
+      bmin > dmin ? bd[--bmin - 1] = INT_MAX : ++bmin;
+      bmax < dmax ? bd[++bmax + 1] = INT_MAX : --bmax;
+      for (d = bmax; d >= bmin; d -= 2)
+	{
+	  int x, y, oldx, tlo = bd[d - 1], thi = bd[d + 1];
+
+	  if (tlo < thi)
+	    x = tlo;
+	  else
+	    x = thi - 1;
+	  oldx = x;
+	  y = x - d;
+	  while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1])
+	    --x, --y;
+	  if (oldx - x > SNAKE_LIMIT)
+	    big_snake = 1;
+	  bd[d] = x;
+	  if (!odd && fmin <= d && d <= fmax && x <= fd[d])
+	    {
+	      part->xmid = x;
+	      part->ymid = y;
+	      part->lo_minimal = part->hi_minimal = 1;
+	      return 2 * c;
+	    }
+	}
+
+      if (minimal)
+	continue;
+
+      /* Heuristic: check occasionally for a diagonal that has made
+	 lots of progress compared with the edit distance.
+	 If we have any such, find the one that has made the most
+	 progress and return it as if it had succeeded.
+
+	 With this heuristic, for files with a constant small density
+	 of changes, the algorithm is linear in the file size.  */
+
+      if (c > 200 && big_snake && heuristic)
+	{
+	  int best;
+
+	  best = 0;
+	  for (d = fmax; d >= fmin; d -= 2)
+	    {
+	      int dd = d - fmid;
+	      int x = fd[d];
+	      int y = x - d;
+	      int v = (x - xoff) * 2 - dd;
+	      if (v > 12 * (c + (dd < 0 ? -dd : dd)))
+		{
+		  if (v > best
+		      && xoff + SNAKE_LIMIT <= x && x < xlim
+		      && yoff + SNAKE_LIMIT <= y && y < ylim)
+		    {
+		      /* We have a good enough best diagonal;
+			 now insist that it end with a significant snake.  */
+		      int k;
+
+		      for (k = 1; xv[x - k] == yv[y - k]; k++)
+			if (k == SNAKE_LIMIT)
+			  {
+			    best = v;
+			    part->xmid = x;
+			    part->ymid = y;
+			    break;
+			  }
+		    }
+		}
+	    }
+	  if (best > 0)
+	    {
+	      part->lo_minimal = 1;
+	      part->hi_minimal = 0;
+	      return 2 * c - 1;
+	    }
+
+	  best = 0;
+	  for (d = bmax; d >= bmin; d -= 2)
+	    {
+	      int dd = d - bmid;
+	      int x = bd[d];
+	      int y = x - d;
+	      int v = (xlim - x) * 2 + dd;
+	      if (v > 12 * (c + (dd < 0 ? -dd : dd)))
+		{
+		  if (v > best
+		      && xoff < x && x <= xlim - SNAKE_LIMIT
+		      && yoff < y && y <= ylim - SNAKE_LIMIT)
+		    {
+		      /* We have a good enough best diagonal;
+			 now insist that it end with a significant snake.  */
+		      int k;
+
+		      for (k = 0; xv[x + k] == yv[y + k]; k++)
+			if (k == SNAKE_LIMIT - 1)
+			  {
+			    best = v;
+			    part->xmid = x;
+			    part->ymid = y;
+			    break;
+			  }
+		    }
+		}
+	    }
+	  if (best > 0)
+	    {
+	      part->lo_minimal = 0;
+	      part->hi_minimal = 1;
+	      return 2 * c - 1;
+	    }
+	}
+
+      /* Heuristic: if we've gone well beyond the call of duty,
+	 give up and report halfway between our best results so far.  */
+      if (c >= too_expensive)
+	{
+	  int fxybest, fxbest;
+	  int bxybest, bxbest;
+
+	  fxbest = bxbest = 0;  /* Pacify `gcc -Wall'.  */
+
+	  /* Find forward diagonal that maximizes X + Y.  */
+	  fxybest = -1;
+	  for (d = fmax; d >= fmin; d -= 2)
+	    {
+	      int x = min (fd[d], xlim);
+	      int y = x - d;
+	      if (ylim < y)
+		x = ylim + d, y = ylim;
+	      if (fxybest < x + y)
+		{
+		  fxybest = x + y;
+		  fxbest = x;
+		}
+	    }
+
+	  /* Find backward diagonal that minimizes X + Y.  */
+	  bxybest = INT_MAX;
+	  for (d = bmax; d >= bmin; d -= 2)
+	    {
+	      int x = max (xoff, bd[d]);
+	      int y = x - d;
+	      if (y < yoff)
+		x = yoff + d, y = yoff;
+	      if (x + y < bxybest)
+		{
+		  bxybest = x + y;
+		  bxbest = x;
+		}
+	    }
+
+	  /* Use the better of the two diagonals.  */
+	  if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff))
+	    {
+	      part->xmid = fxbest;
+	      part->ymid = fxybest - fxbest;
+	      part->lo_minimal = 1;
+	      part->hi_minimal = 0;
+	    }
+	  else
+	    {
+	      part->xmid = bxbest;
+	      part->ymid = bxybest - bxbest;
+	      part->lo_minimal = 0;
+	      part->hi_minimal = 1;
+	    }
+	  return 2 * c - 1;
+	}
+    }
+}
+
+/* Compare in detail contiguous subsequences of the two files
+   which are known, as a whole, to match each other.
+
+   The results are recorded in the vectors files[N].changed_flag, by
+   storing a 1 in the element for each line that is an insertion or deletion.
+
+   The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
+
+   Note that XLIM, YLIM are exclusive bounds.
+   All line numbers are origin-0 and discarded lines are not counted.
+ 
+   If MINIMAL is nonzero, find a minimal difference no matter how
+   expensive it is.  */
+
+static void
+compareseq (xoff, xlim, yoff, ylim, minimal)
+     int xoff, xlim, yoff, ylim, minimal;
+{
+  int * const xv = xvec; /* Help the compiler.  */
+  int * const yv = yvec;
+
+  /* Slide down the bottom initial diagonal. */
+  while (xoff < xlim && yoff < ylim && xv[xoff] == yv[yoff])
+    ++xoff, ++yoff;
+  /* Slide up the top initial diagonal. */
+  while (xlim > xoff && ylim > yoff && xv[xlim - 1] == yv[ylim - 1])
+    --xlim, --ylim;
+
+  /* Handle simple cases. */
+  if (xoff == xlim)
+    while (yoff < ylim)
+      files[1].changed_flag[files[1].realindexes[yoff++]] = 1;
+  else if (yoff == ylim)
+    while (xoff < xlim)
+      files[0].changed_flag[files[0].realindexes[xoff++]] = 1;
+  else
+    {
+      int c;
+      struct partition part;
+
+      /* Find a point of correspondence in the middle of the files.  */
+
+      c = diag (xoff, xlim, yoff, ylim, minimal, &part);
+
+      if (c == 1)
+	{
+	  /* This should be impossible, because it implies that
+	     one of the two subsequences is empty,
+	     and that case was handled above without calling `diag'.
+	     Let's verify that this is true.  */
+	  abort ();
+#if 0
+	  /* The two subsequences differ by a single insert or delete;
+	     record it and we are done.  */
+	  if (part.xmid - part.ymid < xoff - yoff)
+	    files[1].changed_flag[files[1].realindexes[part.ymid - 1]] = 1;
+	  else
+	    files[0].changed_flag[files[0].realindexes[part.xmid]] = 1;
+#endif
+	}
+      else
+	{
+	  /* Use the partitions to split this problem into subproblems.  */
+	  compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal);
+	  compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal);
+	}
+    }
+}
+
+/* Discard lines from one file that have no matches in the other file.
+
+   A line which is discarded will not be considered by the actual
+   comparison algorithm; it will be as if that line were not in the file.
+   The file's `realindexes' table maps virtual line numbers
+   (which don't count the discarded lines) into real line numbers;
+   this is how the actual comparison algorithm produces results
+   that are comprehensible when the discarded lines are counted.
+
+   When we discard a line, we also mark it as a deletion or insertion
+   so that it will be printed in the output.  */
+
+static void
+discard_confusing_lines (filevec)
+     struct file_data filevec[];
+{
+  unsigned int f, i;
+  char *discarded[2];
+  int *equiv_count[2];
+  int *p;
+
+  /* Allocate our results.  */
+  p = (int *) xmalloc ((filevec[0].buffered_lines + filevec[1].buffered_lines)
+		       * (2 * sizeof (int)));
+  for (f = 0; f < 2; f++)
+    {
+      filevec[f].undiscarded = p;  p += filevec[f].buffered_lines;
+      filevec[f].realindexes = p;  p += filevec[f].buffered_lines;
+    }
+
+  /* Set up equiv_count[F][I] as the number of lines in file F
+     that fall in equivalence class I.  */
+
+  p = (int *) xmalloc (filevec[0].equiv_max * (2 * sizeof (int)));
+  equiv_count[0] = p;
+  equiv_count[1] = p + filevec[0].equiv_max;
+  bzero (p, filevec[0].equiv_max * (2 * sizeof (int)));
+
+  for (i = 0; i < filevec[0].buffered_lines; ++i)
+    ++equiv_count[0][filevec[0].equivs[i]];
+  for (i = 0; i < filevec[1].buffered_lines; ++i)
+    ++equiv_count[1][filevec[1].equivs[i]];
+
+  /* Set up tables of which lines are going to be discarded.  */
+
+  discarded[0] = xmalloc (sizeof (char)
+			  * (filevec[0].buffered_lines
+			     + filevec[1].buffered_lines));
+  discarded[1] = discarded[0] + filevec[0].buffered_lines;
+  bzero (discarded[0], sizeof (char) * (filevec[0].buffered_lines
+					+ filevec[1].buffered_lines));
+
+  /* Mark to be discarded each line that matches no line of the other file.
+     If a line matches many lines, mark it as provisionally discardable.  */
+
+  for (f = 0; f < 2; f++)
+    {
+      unsigned int end = filevec[f].buffered_lines;
+      char *discards = discarded[f];
+      int *counts = equiv_count[1 - f];
+      int *equivs = filevec[f].equivs;
+      unsigned int many = 5;
+      unsigned int tem = end / 64;
+
+      /* Multiply MANY by approximate square root of number of lines.
+	 That is the threshold for provisionally discardable lines.  */
+      while ((tem = tem >> 2) > 0)
+	many *= 2;
+
+      for (i = 0; i < end; i++)
+	{
+	  int nmatch;
+	  if (equivs[i] == 0)
+	    continue;
+	  nmatch = counts[equivs[i]];
+	  if (nmatch == 0)
+	    discards[i] = 1;
+	  else if (nmatch > many)
+	    discards[i] = 2;
+	}
+    }
+
+  /* Don't really discard the provisional lines except when they occur
+     in a run of discardables, with nonprovisionals at the beginning
+     and end.  */
+
+  for (f = 0; f < 2; f++)
+    {
+      unsigned int end = filevec[f].buffered_lines;
+      register char *discards = discarded[f];
+
+      for (i = 0; i < end; i++)
+	{
+	  /* Cancel provisional discards not in middle of run of discards.  */
+	  if (discards[i] == 2)
+	    discards[i] = 0;
+	  else if (discards[i] != 0)
+	    {
+	      /* We have found a nonprovisional discard.  */
+	      register int j;
+	      unsigned int length;
+	      unsigned int provisional = 0;
+
+	      /* Find end of this run of discardable lines.
+		 Count how many are provisionally discardable.  */
+	      for (j = i; j < end; j++)
+		{
+		  if (discards[j] == 0)
+		    break;
+		  if (discards[j] == 2)
+		    ++provisional;
+		}
+
+	      /* Cancel provisional discards at end, and shrink the run.  */
+	      while (j > i && discards[j - 1] == 2)
+		discards[--j] = 0, --provisional;
+
+	      /* Now we have the length of a run of discardable lines
+		 whose first and last are not provisional.  */
+	      length = j - i;
+
+	      /* If 1/4 of the lines in the run are provisional,
+		 cancel discarding of all provisional lines in the run.  */
+	      if (provisional * 4 > length)
+		{
+		  while (j > i)
+		    if (discards[--j] == 2)
+		      discards[j] = 0;
+		}
+	      else
+		{
+		  register unsigned int consec;
+		  unsigned int minimum = 1;
+		  unsigned int tem = length / 4;
+
+		  /* MINIMUM is approximate square root of LENGTH/4.
+		     A subrun of two or more provisionals can stand
+		     when LENGTH is at least 16.
+		     A subrun of 4 or more can stand when LENGTH >= 64.  */
+		  while ((tem = tem >> 2) > 0)
+		    minimum *= 2;
+		  minimum++;
+
+		  /* Cancel any subrun of MINIMUM or more provisionals
+		     within the larger run.  */
+		  for (j = 0, consec = 0; j < length; j++)
+		    if (discards[i + j] != 2)
+		      consec = 0;
+		    else if (minimum == ++consec)
+		      /* Back up to start of subrun, to cancel it all.  */
+		      j -= consec;
+		    else if (minimum < consec)
+		      discards[i + j] = 0;
+
+		  /* Scan from beginning of run
+		     until we find 3 or more nonprovisionals in a row
+		     or until the first nonprovisional at least 8 lines in.
+		     Until that point, cancel any provisionals.  */
+		  for (j = 0, consec = 0; j < length; j++)
+		    {
+		      if (j >= 8 && discards[i + j] == 1)
+			break;
+		      if (discards[i + j] == 2)
+			consec = 0, discards[i + j] = 0;
+		      else if (discards[i + j] == 0)
+			consec = 0;
+		      else
+			consec++;
+		      if (consec == 3)
+			break;
+		    }
+
+		  /* I advances to the last line of the run.  */
+		  i += length - 1;
+
+		  /* Same thing, from end.  */
+		  for (j = 0, consec = 0; j < length; j++)
+		    {
+		      if (j >= 8 && discards[i - j] == 1)
+			break;
+		      if (discards[i - j] == 2)
+			consec = 0, discards[i - j] = 0;
+		      else if (discards[i - j] == 0)
+			consec = 0;
+		      else
+			consec++;
+		      if (consec == 3)
+			break;
+		    }
+		}
+	    }
+	}
+    }
+
+  /* Actually discard the lines. */
+  for (f = 0; f < 2; f++)
+    {
+      char *discards = discarded[f];
+      unsigned int end = filevec[f].buffered_lines;
+      unsigned int j = 0;
+      for (i = 0; i < end; ++i)
+	if (no_discards || discards[i] == 0)
+	  {
+	    filevec[f].undiscarded[j] = filevec[f].equivs[i];
+	    filevec[f].realindexes[j++] = i;
+	  }
+	else
+	  filevec[f].changed_flag[i] = 1;
+      filevec[f].nondiscarded_lines = j;
+    }
+
+  free (discarded[0]);
+  free (equiv_count[0]);
+}
+
+/* Adjust inserts/deletes of identical lines to join changes
+   as much as possible.
+
+   We do something when a run of changed lines include a
+   line at one end and have an excluded, identical line at the other.
+   We are free to choose which identical line is included.
+   `compareseq' usually chooses the one at the beginning,
+   but usually it is cleaner to consider the following identical line
+   to be the "change".  */
+
+int inhibit;
+
+static void
+shift_boundaries (filevec)
+     struct file_data filevec[];
+{
+  int f;
+
+  if (inhibit)
+    return;
+
+  for (f = 0; f < 2; f++)
+    {
+      char *changed = filevec[f].changed_flag;
+      char const *other_changed = filevec[1-f].changed_flag;
+      int const *equivs = filevec[f].equivs;
+      int i = 0;
+      int j = 0;
+      int i_end = filevec[f].buffered_lines;
+
+      while (1)
+	{
+	  int runlength, start, corresponding;
+
+	  /* Scan forwards to find beginning of another run of changes.
+	     Also keep track of the corresponding point in the other file.  */
+
+	  while (i < i_end && changed[i] == 0)
+	    {
+	      while (other_changed[j++])
+		continue;
+	      i++;
+	    }
+
+	  if (i == i_end)
+	    break;
+
+	  start = i;
+
+	  /* Find the end of this run of changes.  */
+
+	  while (changed[++i])
+	    continue;
+	  while (other_changed[j])
+	    j++;
+
+	  do
+	    {
+	      /* Record the length of this run of changes, so that
+		 we can later determine whether the run has grown.  */
+	      runlength = i - start;
+
+	      /* Move the changed region back, so long as the
+		 previous unchanged line matches the last changed one.
+		 This merges with previous changed regions.  */
+
+	      while (start && equivs[start - 1] == equivs[i - 1])
+		{
+		  changed[--start] = 1;
+		  changed[--i] = 0;
+		  while (changed[start - 1])
+		    start--;
+		  while (other_changed[--j])
+		    continue;
+		}
+
+	      /* Set CORRESPONDING to the end of the changed run, at the last
+		 point where it corresponds to a changed run in the other file.
+		 CORRESPONDING == I_END means no such point has been found.  */
+	      corresponding = other_changed[j - 1] ? i : i_end;
+
+	      /* Move the changed region forward, so long as the
+		 first changed line matches the following unchanged one.
+		 This merges with following changed regions.
+		 Do this second, so that if there are no merges,
+		 the changed region is moved forward as far as possible.  */
+
+	      while (i != i_end && equivs[start] == equivs[i])
+		{
+		  changed[start++] = 0;
+		  changed[i++] = 1;
+		  while (changed[i])
+		    i++;
+		  while (other_changed[++j])
+		    corresponding = i;
+		}
+	    }
+	  while (runlength != i - start);
+
+	  /* If possible, move the fully-merged run of changes
+	     back to a corresponding run in the other file.  */
+
+	  while (corresponding < i)
+	    {
+	      changed[--start] = 1;
+	      changed[--i] = 0;
+	      while (other_changed[--j])
+		continue;
+	    }
+	}
+    }
+}
+
+/* Cons an additional entry onto the front of an edit script OLD.
+   LINE0 and LINE1 are the first affected lines in the two files (origin 0).
+   DELETED is the number of lines deleted here from file 0.
+   INSERTED is the number of lines inserted here in file 1.
+
+   If DELETED is 0 then LINE0 is the number of the line before
+   which the insertion was done; vice versa for INSERTED and LINE1.  */
+
+static struct change *
+add_change (line0, line1, deleted, inserted, old)
+     int line0, line1, deleted, inserted;
+     struct change *old;
+{
+  struct change *new = (struct change *) xmalloc (sizeof (struct change));
+
+  new->line0 = line0;
+  new->line1 = line1;
+  new->inserted = inserted;
+  new->deleted = deleted;
+  new->link = old;
+  return new;
+}
+
+/* Scan the tables of which lines are inserted and deleted,
+   producing an edit script in reverse order.  */
+
+static struct change *
+build_reverse_script (filevec)
+     struct file_data const filevec[];
+{
+  struct change *script = 0;
+  char *changed0 = filevec[0].changed_flag;
+  char *changed1 = filevec[1].changed_flag;
+  int len0 = filevec[0].buffered_lines;
+  int len1 = filevec[1].buffered_lines;
+
+  /* Note that changedN[len0] does exist, and contains 0.  */
+
+  int i0 = 0, i1 = 0;
+
+  while (i0 < len0 || i1 < len1)
+    {
+      if (changed0[i0] || changed1[i1])
+	{
+	  int line0 = i0, line1 = i1;
+
+	  /* Find # lines changed here in each file.  */
+	  while (changed0[i0]) ++i0;
+	  while (changed1[i1]) ++i1;
+
+	  /* Record this change.  */
+	  script = add_change (line0, line1, i0 - line0, i1 - line1, script);
+	}
+
+      /* We have reached lines in the two files that match each other.  */
+      i0++, i1++;
+    }
+
+  return script;
+}
+
+/* Scan the tables of which lines are inserted and deleted,
+   producing an edit script in forward order.  */
+
+static struct change *
+build_script (filevec)
+     struct file_data const filevec[];
+{
+  struct change *script = 0;
+  char *changed0 = filevec[0].changed_flag;
+  char *changed1 = filevec[1].changed_flag;
+  int i0 = filevec[0].buffered_lines, i1 = filevec[1].buffered_lines;
+
+  /* Note that changedN[-1] does exist, and contains 0.  */
+
+  while (i0 >= 0 || i1 >= 0)
+    {
+      if (changed0[i0 - 1] || changed1[i1 - 1])
+	{
+	  int line0 = i0, line1 = i1;
+
+	  /* Find # lines changed here in each file.  */
+	  while (changed0[i0 - 1]) --i0;
+	  while (changed1[i1 - 1]) --i1;
+
+	  /* Record this change.  */
+	  script = add_change (i0, i1, line0 - i0, line1 - i1, script);
+	}
+
+      /* We have reached lines in the two files that match each other.  */
+      i0--, i1--;
+    }
+
+  return script;
+}
+
+/* If CHANGES, briefly report that two files differed.  */
+static void
+briefly_report (changes, filevec)
+     int changes;
+     struct file_data const filevec[];
+{
+  if (changes)
+    message (no_details_flag ? "Files %s and %s differ\n"
+	     : "Binary files %s and %s differ\n",
+	     filevec[0].name, filevec[1].name);
+}
+
+/* Report the differences of two files.  DEPTH is the current directory
+   depth. */
+int
+diff_2_files (filevec, depth)
+     struct file_data filevec[];
+     int depth;
+{
+  int diags;
+  int i;
+  struct change *e, *p;
+  struct change *script;
+  int changes;
+
+
+  /* If we have detected that either file is binary,
+     compare the two files as binary.  This can happen
+     only when the first chunk is read.
+     Also, --brief without any --ignore-* options means
+     we can speed things up by treating the files as binary.  */
+
+  if (read_files (filevec, no_details_flag & ~ignore_some_changes))
+    {
+      /* Files with different lengths must be different.  */
+      if (filevec[0].stat.st_size != filevec[1].stat.st_size
+	  && (filevec[0].desc < 0 || S_ISREG (filevec[0].stat.st_mode))
+	  && (filevec[1].desc < 0 || S_ISREG (filevec[1].stat.st_mode)))
+	changes = 1;
+
+      /* Standard input equals itself.  */
+      else if (filevec[0].desc == filevec[1].desc)
+	changes = 0;
+
+      else
+	/* Scan both files, a buffer at a time, looking for a difference.  */
+	{
+	  /* Allocate same-sized buffers for both files.  */
+	  size_t buffer_size = buffer_lcm (STAT_BLOCKSIZE (filevec[0].stat),
+					   STAT_BLOCKSIZE (filevec[1].stat));
+	  for (i = 0; i < 2; i++)
+	    filevec[i].buffer = xrealloc (filevec[i].buffer, buffer_size);
+
+	  for (;;  filevec[0].buffered_chars = filevec[1].buffered_chars = 0)
+	    {
+	      /* Read a buffer's worth from both files.  */
+	      for (i = 0; i < 2; i++)
+		if (0 <= filevec[i].desc)
+		  while (filevec[i].buffered_chars != buffer_size)
+		    {
+		      int r = read (filevec[i].desc,
+				    filevec[i].buffer
+				    + filevec[i].buffered_chars,
+				    buffer_size - filevec[i].buffered_chars);
+		      if (r == 0)
+			break;
+		      if (r < 0)
+			pfatal_with_name (filevec[i].name);
+		      filevec[i].buffered_chars += r;
+		    }
+
+	      /* If the buffers differ, the files differ.  */
+	      if (filevec[0].buffered_chars != filevec[1].buffered_chars
+		  || (filevec[0].buffered_chars != 0
+		      && memcmp (filevec[0].buffer,
+				 filevec[1].buffer,
+				 filevec[0].buffered_chars) != 0))
+		{
+		  changes = 1;
+		  break;
+		}
+
+	      /* If we reach end of file, the files are the same.  */
+	      if (filevec[0].buffered_chars != buffer_size)
+		{
+		  changes = 0;
+		  break;
+		}
+	    }
+	}
+
+      briefly_report (changes, filevec);
+    }
+  else
+    {
+      /* Allocate vectors for the results of comparison:
+	 a flag for each line of each file, saying whether that line
+	 is an insertion or deletion.
+	 Allocate an extra element, always zero, at each end of each vector.  */
+
+      size_t s = filevec[0].buffered_lines + filevec[1].buffered_lines + 4;
+      filevec[0].changed_flag = xmalloc (s);
+      bzero (filevec[0].changed_flag, s);
+      filevec[0].changed_flag++;
+      filevec[1].changed_flag = filevec[0].changed_flag
+				+ filevec[0].buffered_lines + 2;
+
+      /* Some lines are obviously insertions or deletions
+	 because they don't match anything.  Detect them now, and
+	 avoid even thinking about them in the main comparison algorithm.  */
+
+      discard_confusing_lines (filevec);
+
+      /* Now do the main comparison algorithm, considering just the
+	 undiscarded lines.  */
+
+      xvec = filevec[0].undiscarded;
+      yvec = filevec[1].undiscarded;
+      diags = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3;
+      fdiag = (int *) xmalloc (diags * (2 * sizeof (int)));
+      bdiag = fdiag + diags;
+      fdiag += filevec[1].nondiscarded_lines + 1;
+      bdiag += filevec[1].nondiscarded_lines + 1;
+
+      /* Set TOO_EXPENSIVE to be approximate square root of input size,
+	 bounded below by 256.  */
+      too_expensive = 1;
+      for (i = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines;
+	   i != 0; i >>= 2)
+	too_expensive <<= 1;
+      too_expensive = max (256, too_expensive);
+
+      files[0] = filevec[0];
+      files[1] = filevec[1];
+
+      compareseq (0, filevec[0].nondiscarded_lines,
+		  0, filevec[1].nondiscarded_lines, no_discards);
+
+      free (fdiag - (filevec[1].nondiscarded_lines + 1));
+
+      /* Modify the results slightly to make them prettier
+	 in cases where that can validly be done.  */
+
+      shift_boundaries (filevec);
+
+      /* Get the results of comparison in the form of a chain
+	 of `struct change's -- an edit script.  */
+
+      if (output_style == OUTPUT_ED)
+	script = build_reverse_script (filevec);
+      else
+	script = build_script (filevec);
+
+      /* Set CHANGES if we had any diffs.
+	 If some changes are ignored, we must scan the script to decide.  */
+      if (ignore_blank_lines_flag || ignore_regexp_list)
+	{
+	  struct change *next = script;
+	  changes = 0;
+
+	  while (next && changes == 0)
+	    {
+	      struct change *this, *end;
+	      int first0, last0, first1, last1, deletes, inserts;
+
+	      /* Find a set of changes that belong together.  */
+	      this = next;
+	      end = find_change (next);
+
+	      /* Disconnect them from the rest of the changes, making them
+		 a hunk, and remember the rest for next iteration.  */
+	      next = end->link;
+	      end->link = 0;
+
+	      /* Determine whether this hunk is really a difference.  */
+	      analyze_hunk (this, &first0, &last0, &first1, &last1,
+			    &deletes, &inserts);
+
+	      /* Reconnect the script so it will all be freed properly.  */
+	      end->link = next;
+
+	      if (deletes || inserts)
+		changes = 1;
+	    }
+	}
+      else
+	changes = (script != 0);
+
+      if (no_details_flag)
+	briefly_report (changes, filevec);
+      else
+	{
+	  if (changes || ! no_diff_means_no_output)
+	    {
+	      /* Record info for starting up output,
+		 to be used if and when we have some output to print.  */
+	      setup_output (files[0].name, files[1].name, depth);
+
+	      switch (output_style)
+		{
+		case OUTPUT_CONTEXT:
+		  print_context_script (script, 0);
+		  break;
+
+		case OUTPUT_UNIFIED:
+		  print_context_script (script, 1);
+		  break;
+
+		case OUTPUT_ED:
+		  print_ed_script (script);
+		  break;
+
+		case OUTPUT_FORWARD_ED:
+		  pr_forward_ed_script (script);
+		  break;
+
+		case OUTPUT_RCS:
+		  print_rcs_script (script);
+		  break;
+
+		case OUTPUT_NORMAL:
+		  print_normal_script (script);
+		  break;
+
+		case OUTPUT_IFDEF:
+		  print_ifdef_script (script);
+		  break;
+
+		case OUTPUT_SDIFF:
+		  print_sdiff_script (script);
+		}
+
+	      finish_output ();
+	    }
+	}
+
+      free (filevec[0].undiscarded);
+
+      free (filevec[0].changed_flag - 1);
+
+      for (i = 1; i >= 0; --i)
+	free (filevec[i].equivs);
+
+      for (i = 0; i < 2; ++i)
+	free (filevec[i].linbuf + filevec[i].linbuf_base);
+
+      for (e = script; e; e = p)
+	{
+	  p = e->link;
+	  free (e);
+	}
+
+      if (! ROBUST_OUTPUT_STYLE (output_style))
+	for (i = 0; i < 2; ++i)
+	  if (filevec[i].missing_newline)
+	    {
+	      error ("No newline at end of file %s", filevec[i].name, "");
+	      changes = 2;
+	    }
+    }
+
+  if (filevec[0].buffer != filevec[1].buffer)
+    free (filevec[0].buffer);
+  free (filevec[1].buffer);
+
+  return changes;
+}
diff --git a/gnu/usr.bin/diff/cmpbuf.c b/gnu/usr.bin/diff/cmpbuf.c
new file mode 100644
index 000000000000..e95a8f98ed9d
--- /dev/null
+++ b/gnu/usr.bin/diff/cmpbuf.c
@@ -0,0 +1,40 @@
+/* Buffer primitives for comparison operations.
+   Copyright (C) 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "system.h"
+#include "cmpbuf.h"
+
+/* Least common multiple of two buffer sizes A and B.  */
+
+size_t
+buffer_lcm (a, b)
+     size_t a, b;
+{
+  size_t m, n, r;
+
+  /* Yield reasonable values if buffer sizes are zero.  */
+  if (!a)
+    return b ? b : 8 * 1024;
+  if (!b)
+    return a;
+
+  /* n = gcd (a, b) */
+  for (m = a, n = b;  (r = m % n) != 0;  m = n, n = r)
+    continue;
+
+  return a/n * b;
+}
diff --git a/gnu/usr.bin/diff/cmpbuf.h b/gnu/usr.bin/diff/cmpbuf.h
new file mode 100644
index 000000000000..e3852b7bd4f4
--- /dev/null
+++ b/gnu/usr.bin/diff/cmpbuf.h
@@ -0,0 +1,20 @@
+/* Buffer primitives for comparison operations.
+   Copyright (C) 1993 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.  */
+
+size_t buffer_lcm PARAMS((size_t, size_t));
diff --git a/gnu/usr.bin/diff/config.h b/gnu/usr.bin/diff/config.h
new file mode 100644
index 000000000000..3d7e65b7b41e
--- /dev/null
+++ b/gnu/usr.bin/diff/config.h
@@ -0,0 +1,118 @@
+/* config.h.  Generated automatically by configure.  */
+/* config.hin.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if using alloca.c.  */
+/* #undef C_ALLOCA */
+
+/* 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 to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+   This function is required for alloca.c support on those systems.  */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix).  */
+/* #undef HAVE_ALLOCA_H */
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+/* #undef HAVE_DOPRNT */
+
+/* Define if your struct stat has st_blksize.  */
+#define HAVE_ST_BLKSIZE 1
+
+/* Define if you have <vfork.h>.  */
+/* #undef HAVE_VFORK_H */
+
+/* Define if you have the vprintf function.  */
+#define HAVE_VPRINTF 1
+
+/* Define if on MINIX.  */
+/* #undef _MINIX */
+
+/* Define to `int' if <sys/types.h> 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
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+	STACK_DIRECTION > 0 => grows toward higher addresses
+	STACK_DIRECTION < 0 => grows toward lower addresses
+	STACK_DIRECTION = 0 => direction of growth unknown
+ */
+/* #undef STACK_DIRECTION */
+
+/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly.  */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if <sys/wait.h> is compatible with Posix applications.  */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define vfork as fork if vfork does not work.  */
+/* #undef vfork */
+
+/* Define if you have the dup2 function.  */
+#define HAVE_DUP2 1
+
+/* Define if you have the memchr function.  */
+#define HAVE_MEMCHR 1
+
+/* Define if you have the sigaction function.  */
+#define HAVE_SIGACTION 1
+
+/* Define if you have the strchr function.  */
+#define HAVE_STRCHR 1
+
+/* Define if you have the strerror function.  */
+#define HAVE_STRERROR 1
+
+/* Define if you have the tmpnam function.  */
+#define HAVE_TMPNAM 1
+
+/* Define if you have the <dirent.h> header file.  */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the <fcntl.h> header file.  */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <limits.h> header file.  */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the <ndir.h> header file.  */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the <stdlib.h> header file.  */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/dir.h> header file.  */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the <sys/file.h> header file.  */
+#define HAVE_SYS_FILE_H 1
+
+/* Define if you have the <sys/ndir.h> header file.  */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <time.h> header file.  */
+#define HAVE_TIME_H 1
+
+/* Define if you have the <unistd.h> header file.  */
+#define HAVE_UNISTD_H 1
diff --git a/gnu/usr.bin/diff/context.c b/gnu/usr.bin/diff/context.c
new file mode 100644
index 000000000000..14f950c52699
--- /dev/null
+++ b/gnu/usr.bin/diff/context.c
@@ -0,0 +1,468 @@
+/* Context-format output routines for GNU DIFF.
+   Copyright (C) 1988,1989,1991,1992,1993,1994 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.  */
+
+#include "diff.h"
+
+static struct change *find_hunk PARAMS((struct change *));
+static void find_function PARAMS((struct file_data const *, int, char const **, size_t *));
+static void mark_ignorable PARAMS((struct change *));
+static void pr_context_hunk PARAMS((struct change *));
+static void pr_unidiff_hunk PARAMS((struct change *));
+static void print_context_label PARAMS ((char const *, struct file_data *, char const *));
+static void print_context_number_range PARAMS((struct file_data const *, int, int));
+static void print_unidiff_number_range PARAMS((struct file_data const *, int, int));
+
+/* Last place find_function started searching from.  */
+static int find_function_last_search;
+
+/* The value find_function returned when it started searching there.  */
+static int find_function_last_match;
+
+/* Print a label for a context diff, with a file name and date or a label.  */
+
+static void
+print_context_label (mark, inf, label)
+     char const *mark;
+     struct file_data *inf;
+     char const *label;
+{
+  if (label)
+    fprintf (outfile, "%s %s\n", mark, label);
+  else
+    {
+      char const *ct = ctime (&inf->stat.st_mtime);
+      if (!ct)
+	ct = "?\n";
+      /* See Posix.2 section 4.17.6.1.4 for this format.  */
+      fprintf (outfile, "%s %s\t%s", mark, inf->name, ct);
+    }
+}
+
+/* Print a header for a context diff, with the file names and dates.  */
+
+void
+print_context_header (inf, unidiff_flag)
+     struct file_data inf[];
+     int unidiff_flag;
+{
+  if (unidiff_flag)
+    {
+      print_context_label ("---", &inf[0], file_label[0]);
+      print_context_label ("+++", &inf[1], file_label[1]);
+    }
+  else
+    {
+      print_context_label ("***", &inf[0], file_label[0]);
+      print_context_label ("---", &inf[1], file_label[1]);
+    }
+}
+
+/* Print an edit script in context format.  */
+
+void
+print_context_script (script, unidiff_flag)
+     struct change *script;
+     int unidiff_flag;
+{
+  if (ignore_blank_lines_flag || ignore_regexp_list)
+    mark_ignorable (script);
+  else
+    {
+      struct change *e;
+      for (e = script; e; e = e->link)
+	e->ignore = 0;
+    }
+
+  find_function_last_search = - files[0].prefix_lines;
+  find_function_last_match = find_function_last_search - 1;
+
+  if (unidiff_flag)
+    print_script (script, find_hunk, pr_unidiff_hunk);
+  else
+    print_script (script, find_hunk, pr_context_hunk);
+}
+
+/* Print a pair of line numbers with a comma, translated for file FILE.
+   If the second number is not greater, use the first in place of it.
+
+   Args A and B are internal line numbers.
+   We print the translated (real) line numbers.  */
+
+static void
+print_context_number_range (file, a, b)
+     struct file_data const *file;
+     int a, b;
+{
+  int trans_a, trans_b;
+  translate_range (file, a, b, &trans_a, &trans_b);
+
+  /* Note: we can have B < A in the case of a range of no lines.
+     In this case, we should print the line number before the range,
+     which is B.  */
+  if (trans_b > trans_a)
+    fprintf (outfile, "%d,%d", trans_a, trans_b);
+  else
+    fprintf (outfile, "%d", trans_b);
+}
+
+/* Print a portion of an edit script in context format.
+   HUNK is the beginning of the portion to be printed.
+   The end is marked by a `link' that has been nulled out.
+
+   Prints out lines from both files, and precedes each
+   line with the appropriate flag-character.  */
+
+static void
+pr_context_hunk (hunk)
+     struct change *hunk;
+{
+  int first0, last0, first1, last1, show_from, show_to, i;
+  struct change *next;
+  char const *prefix;
+  char const *function;
+  size_t function_length;
+  FILE *out;
+
+  /* Determine range of line numbers involved in each file.  */
+
+  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
+
+  if (!show_from && !show_to)
+    return;
+
+  /* Include a context's width before and after.  */
+
+  i = - files[0].prefix_lines;
+  first0 = max (first0 - context, i);
+  first1 = max (first1 - context, i);
+  last0 = min (last0 + context, files[0].valid_lines - 1);
+  last1 = min (last1 + context, files[1].valid_lines - 1);
+
+  /* If desired, find the preceding function definition line in file 0.  */
+  function = 0;
+  if (function_regexp_list)
+    find_function (&files[0], first0, &function, &function_length);
+
+  begin_output ();
+  out = outfile;
+
+  /* If we looked for and found a function this is part of,
+     include its name in the header of the diff section.  */
+  fprintf (out, "***************");
+
+  if (function)
+    {
+      fprintf (out, " ");
+      fwrite (function, 1, min (function_length - 1, 40), out);
+    }
+
+  fprintf (out, "\n*** ");
+  print_context_number_range (&files[0], first0, last0);
+  fprintf (out, " ****\n");
+
+  if (show_from)
+    {
+      next = hunk;
+
+      for (i = first0; i <= last0; i++)
+	{
+	  /* Skip past changes that apply (in file 0)
+	     only to lines before line I.  */
+
+	  while (next && next->line0 + next->deleted <= i)
+	    next = next->link;
+
+	  /* Compute the marking for line I.  */
+
+	  prefix = " ";
+	  if (next && next->line0 <= i)
+	    /* The change NEXT covers this line.
+	       If lines were inserted here in file 1, this is "changed".
+	       Otherwise it is "deleted".  */
+	    prefix = (next->inserted > 0 ? "!" : "-");
+
+	  print_1_line (prefix, &files[0].linbuf[i]);
+	}
+    }
+
+  fprintf (out, "--- ");
+  print_context_number_range (&files[1], first1, last1);
+  fprintf (out, " ----\n");
+
+  if (show_to)
+    {
+      next = hunk;
+
+      for (i = first1; i <= last1; i++)
+	{
+	  /* Skip past changes that apply (in file 1)
+	     only to lines before line I.  */
+
+	  while (next && next->line1 + next->inserted <= i)
+	    next = next->link;
+
+	  /* Compute the marking for line I.  */
+
+	  prefix = " ";
+	  if (next && next->line1 <= i)
+	    /* The change NEXT covers this line.
+	       If lines were deleted here in file 0, this is "changed".
+	       Otherwise it is "inserted".  */
+	    prefix = (next->deleted > 0 ? "!" : "+");
+
+	  print_1_line (prefix, &files[1].linbuf[i]);
+	}
+    }
+}
+
+/* Print a pair of line numbers with a comma, translated for file FILE.
+   If the second number is smaller, use the first in place of it.
+   If the numbers are equal, print just one number.
+
+   Args A and B are internal line numbers.
+   We print the translated (real) line numbers.  */
+
+static void
+print_unidiff_number_range (file, a, b)
+     struct file_data const *file;
+     int a, b;
+{
+  int trans_a, trans_b;
+  translate_range (file, a, b, &trans_a, &trans_b);
+
+  /* Note: we can have B < A in the case of a range of no lines.
+     In this case, we should print the line number before the range,
+     which is B.  */
+  if (trans_b <= trans_a)
+    fprintf (outfile, trans_b == trans_a ? "%d" : "%d,0", trans_b);
+  else
+    fprintf (outfile, "%d,%d", trans_a, trans_b - trans_a + 1);
+}
+
+/* Print a portion of an edit script in unidiff format.
+   HUNK is the beginning of the portion to be printed.
+   The end is marked by a `link' that has been nulled out.
+
+   Prints out lines from both files, and precedes each
+   line with the appropriate flag-character.  */
+
+static void
+pr_unidiff_hunk (hunk)
+     struct change *hunk;
+{
+  int first0, last0, first1, last1, show_from, show_to, i, j, k;
+  struct change *next;
+  char const *function;
+  size_t function_length;
+  FILE *out;
+
+  /* Determine range of line numbers involved in each file.  */
+
+  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
+
+  if (!show_from && !show_to)
+    return;
+
+  /* Include a context's width before and after.  */
+
+  i = - files[0].prefix_lines;
+  first0 = max (first0 - context, i);
+  first1 = max (first1 - context, i);
+  last0 = min (last0 + context, files[0].valid_lines - 1);
+  last1 = min (last1 + context, files[1].valid_lines - 1);
+
+  /* If desired, find the preceding function definition line in file 0.  */
+  function = 0;
+  if (function_regexp_list)
+    find_function (&files[0], first0, &function, &function_length);
+
+  begin_output ();
+  out = outfile;
+
+  fprintf (out, "@@ -");
+  print_unidiff_number_range (&files[0], first0, last0);
+  fprintf (out, " +");
+  print_unidiff_number_range (&files[1], first1, last1);
+  fprintf (out, " @@");
+
+  /* If we looked for and found a function this is part of,
+     include its name in the header of the diff section.  */
+
+  if (function)
+    {
+      putc (' ', out);
+      fwrite (function, 1, min (function_length - 1, 40), out);
+    }
+  putc ('\n', out);
+
+  next = hunk;
+  i = first0;
+  j = first1;
+
+  while (i <= last0 || j <= last1)
+    {
+
+      /* If the line isn't a difference, output the context from file 0. */
+
+      if (!next || i < next->line0)
+	{
+	  putc (tab_align_flag ? '\t' : ' ', out);
+	  print_1_line (0, &files[0].linbuf[i++]);
+	  j++;
+	}
+      else
+	{
+	  /* For each difference, first output the deleted part. */
+
+	  k = next->deleted;
+	  while (k--)
+	    {
+	      putc ('-', out);
+	      if (tab_align_flag)
+		putc ('\t', out);
+	      print_1_line (0, &files[0].linbuf[i++]);
+	    }
+
+	  /* Then output the inserted part. */
+
+	  k = next->inserted;
+	  while (k--)
+	    {
+	      putc ('+', out);
+	      if (tab_align_flag)
+		putc ('\t', out);
+	      print_1_line (0, &files[1].linbuf[j++]);
+	    }
+
+	  /* We're done with this hunk, so on to the next! */
+
+	  next = next->link;
+	}
+    }
+}
+
+/* Scan a (forward-ordered) edit script for the first place that more than
+   2*CONTEXT unchanged lines appear, and return a pointer
+   to the `struct change' for the last change before those lines.  */
+
+static struct change *
+find_hunk (start)
+     struct change *start;
+{
+  struct change *prev;
+  int top0, top1;
+  int thresh;
+
+  do
+    {
+      /* Compute number of first line in each file beyond this changed.  */
+      top0 = start->line0 + start->deleted;
+      top1 = start->line1 + start->inserted;
+      prev = start;
+      start = start->link;
+      /* Threshold distance is 2*CONTEXT between two non-ignorable changes,
+	 but only CONTEXT if one is ignorable.  */
+      thresh = ((prev->ignore || (start && start->ignore))
+		? context
+		: 2 * context + 1);
+      /* It is not supposed to matter which file we check in the end-test.
+	 If it would matter, crash.  */
+      if (start && start->line0 - top0 != start->line1 - top1)
+	abort ();
+    } while (start
+	     /* Keep going if less than THRESH lines
+		elapse before the affected line.  */
+	     && start->line0 < top0 + thresh);
+
+  return prev;
+}
+
+/* Set the `ignore' flag properly in each change in SCRIPT.
+   It should be 1 if all the lines inserted or deleted in that change
+   are ignorable lines.  */
+
+static void
+mark_ignorable (script)
+     struct change *script;
+{
+  while (script)
+    {
+      struct change *next = script->link;
+      int first0, last0, first1, last1, deletes, inserts;
+
+      /* Turn this change into a hunk: detach it from the others.  */
+      script->link = 0;
+
+      /* Determine whether this change is ignorable.  */
+      analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts);
+      /* Reconnect the chain as before.  */
+      script->link = next;
+
+      /* If the change is ignorable, mark it.  */
+      script->ignore = (!deletes && !inserts);
+
+      /* Advance to the following change.  */
+      script = next;
+    }
+}
+
+/* Find the last function-header line in FILE prior to line number LINENUM.
+   This is a line containing a match for the regexp in `function_regexp'.
+   Store the address of the line text into LINEP and the length of the
+   line into LENP.
+   Do not store anything if no function-header is found.  */
+
+static void
+find_function (file, linenum, linep, lenp)
+     struct file_data const *file;
+     int linenum;
+     char const **linep;
+     size_t *lenp;
+{
+  int i = linenum;
+  int last = find_function_last_search;
+  find_function_last_search = i;
+
+  while (--i >= last)
+    {
+      /* See if this line is what we want.  */
+      struct regexp_list *r;
+      char const *line = file->linbuf[i];
+      size_t len = file->linbuf[i + 1] - line;
+
+      for (r = function_regexp_list; r; r = r->next)
+	if (0 <= re_search (&r->buf, line, len, 0, len, 0))
+	  {
+	    *linep = line;
+	    *lenp = len;
+	    find_function_last_match = i;
+	    return;
+	  }
+    }
+  /* If we search back to where we started searching the previous time,
+     find the line we found last time.  */
+  if (find_function_last_match >= - file->prefix_lines)
+    {
+      i = find_function_last_match;
+      *linep = file->linbuf[i];
+      *lenp = file->linbuf[i + 1] - *linep;
+      return;
+    }
+  return;
+}
diff --git a/gnu/usr.bin/diff/diff.c b/gnu/usr.bin/diff/diff.c
new file mode 100644
index 000000000000..ab1549be8643
--- /dev/null
+++ b/gnu/usr.bin/diff/diff.c
@@ -0,0 +1,1106 @@
+/* GNU DIFF main routine.
+   Copyright (C) 1988, 1989, 1992, 1993, 1994 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 DIFF was written by Mike Haertel, David Hayes,
+   Richard Stallman, Len Tower, and Paul Eggert.  */
+
+#define GDIFF_MAIN
+#include "diff.h"
+#include <signal.h>
+#include "getopt.h"
+#include "fnmatch.h"
+
+#ifndef DEFAULT_WIDTH
+#define DEFAULT_WIDTH 130
+#endif
+
+#ifndef GUTTER_WIDTH_MINIMUM
+#define GUTTER_WIDTH_MINIMUM 3
+#endif
+
+static char const *filetype PARAMS((struct stat const *));
+static char *option_list PARAMS((char **, int));
+static int add_exclude_file PARAMS((char const *));
+static int ck_atoi PARAMS((char const *, int *));
+static int compare_files PARAMS((char const *, char const *, char const *, char const *, int));
+static int specify_format PARAMS((char **, char *));
+static void add_exclude PARAMS((char const *));
+static void add_regexp PARAMS((struct regexp_list **, char const *));
+static void specify_style PARAMS((enum output_style));
+static void try_help PARAMS((char const *));
+static void check_stdout PARAMS((void));
+static void usage PARAMS((void));
+
+/* Nonzero for -r: if comparing two directories,
+   compare their common subdirectories recursively.  */
+
+static int recursive;
+
+/* For debugging: don't do discard_confusing_lines.  */
+
+int no_discards;
+
+#if HAVE_SETMODE
+/* I/O mode: nonzero only if using binary input/output.  */
+static int binary_I_O;
+#endif
+
+/* Return a string containing the command options with which diff was invoked.
+   Spaces appear between what were separate ARGV-elements.
+   There is a space at the beginning but none at the end.
+   If there were no options, the result is an empty string.
+
+   Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
+   the length of that vector.  */
+
+static char *
+option_list (optionvec, count)
+     char **optionvec;  /* Was `vector', but that collides on Alliant.  */
+     int count;
+{
+  int i;
+  size_t length = 0;
+  char *result;
+
+  for (i = 0; i < count; i++)
+    length += strlen (optionvec[i]) + 1;
+
+  result = xmalloc (length + 1);
+  result[0] = 0;
+
+  for (i = 0; i < count; i++)
+    {
+      strcat (result, " ");
+      strcat (result, optionvec[i]);
+    }
+
+  return result;
+}
+
+/* Convert STR to a positive integer, storing the result in *OUT.
+   If STR is not a valid integer, return -1 (otherwise 0). */
+static int
+ck_atoi (str, out)
+     char const *str;
+     int *out;
+{
+  char const *p;
+  for (p = str; *p; p++)
+    if (*p < '0' || *p > '9')
+      return -1;
+
+  *out = atoi (optarg);
+  return 0;
+}
+
+/* Keep track of excluded file name patterns.  */
+
+static char const **exclude;
+static int exclude_alloc, exclude_count;
+
+int
+excluded_filename (f)
+     char const *f;
+{
+  int i;
+  for (i = 0;  i < exclude_count;  i++)
+    if (fnmatch (exclude[i], f, 0) == 0)
+      return 1;
+  return 0;
+}
+
+static void
+add_exclude (pattern)
+     char const *pattern;
+{
+  if (exclude_alloc <= exclude_count)
+    exclude = (char const **)
+	      (exclude_alloc == 0
+	       ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude))
+	       : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude)));
+
+  exclude[exclude_count++] = pattern;
+}
+
+static int
+add_exclude_file (name)
+     char const *name;
+{
+  struct file_data f;
+  char *p, *q, *lim;
+
+  f.name = optarg;
+  f.desc = (strcmp (optarg, "-") == 0
+	    ? STDIN_FILENO
+	    : open (optarg, O_RDONLY, 0));
+  if (f.desc < 0 || fstat (f.desc, &f.stat) != 0)
+    return -1;
+
+  sip (&f, 1);
+  slurp (&f);
+
+  for (p = f.buffer, lim = p + f.buffered_chars;  p < lim;  p = q)
+    {
+      q = (char *) memchr (p, '\n', lim - p);
+      if (!q)
+	q = lim;
+      *q++ = 0;
+      add_exclude (p);
+    }
+
+  return close (f.desc);
+}
+
+/* The numbers 129- that appear in the fourth element of some entries
+   tell the big switch in `main' how to process those options.  */
+
+static struct option const longopts[] =
+{
+  {"ignore-blank-lines", 0, 0, 'B'},
+  {"context", 2, 0, 'C'},
+  {"ifdef", 1, 0, 'D'},
+  {"show-function-line", 1, 0, 'F'},
+  {"speed-large-files", 0, 0, 'H'},
+  {"ignore-matching-lines", 1, 0, 'I'},
+  {"label", 1, 0, 'L'},
+  {"file-label", 1, 0, 'L'},	/* An alias, no longer recommended */
+  {"new-file", 0, 0, 'N'},
+  {"entire-new-file", 0, 0, 'N'},	/* An alias, no longer recommended */
+  {"unidirectional-new-file", 0, 0, 'P'},
+  {"starting-file", 1, 0, 'S'},
+  {"initial-tab", 0, 0, 'T'},
+  {"width", 1, 0, 'W'},
+  {"text", 0, 0, 'a'},
+  {"ascii", 0, 0, 'a'},		/* An alias, no longer recommended */
+  {"ignore-space-change", 0, 0, 'b'},
+  {"minimal", 0, 0, 'd'},
+  {"ed", 0, 0, 'e'},
+  {"forward-ed", 0, 0, 'f'},
+  {"ignore-case", 0, 0, 'i'},
+  {"paginate", 0, 0, 'l'},
+  {"print", 0, 0, 'l'},		/* An alias, no longer recommended */
+  {"rcs", 0, 0, 'n'},
+  {"show-c-function", 0, 0, 'p'},
+  {"brief", 0, 0, 'q'},
+  {"recursive", 0, 0, 'r'},
+  {"report-identical-files", 0, 0, 's'},
+  {"expand-tabs", 0, 0, 't'},
+  {"version", 0, 0, 'v'},
+  {"ignore-all-space", 0, 0, 'w'},
+  {"exclude", 1, 0, 'x'},
+  {"exclude-from", 1, 0, 'X'},
+  {"side-by-side", 0, 0, 'y'},
+  {"unified", 2, 0, 'U'},
+  {"left-column", 0, 0, 129},
+  {"suppress-common-lines", 0, 0, 130},
+  {"sdiff-merge-assist", 0, 0, 131},
+  {"old-line-format", 1, 0, 132},
+  {"new-line-format", 1, 0, 133},
+  {"unchanged-line-format", 1, 0, 134},
+  {"line-format", 1, 0, 135},
+  {"old-group-format", 1, 0, 136},
+  {"new-group-format", 1, 0, 137},
+  {"unchanged-group-format", 1, 0, 138},
+  {"changed-group-format", 1, 0, 139},
+  {"horizon-lines", 1, 0, 140},
+  {"help", 0, 0, 141},
+  {"binary", 0, 0, 142},
+  {0, 0, 0, 0}
+};
+
+int
+main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  int val;
+  int c;
+  int prev = -1;
+  int width = DEFAULT_WIDTH;
+  int show_c_function = 0;
+
+  /* Do our initializations.  */
+  initialize_main (&argc, &argv);
+  program_name = argv[0];
+  output_style = OUTPUT_NORMAL;
+  context = -1;
+
+  /* Decode the options.  */
+
+  while ((c = getopt_long (argc, argv,
+			   "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y",
+			   longopts, 0)) != EOF)
+    {
+      switch (c)
+	{
+	  /* All digits combine in decimal to specify the context-size.  */
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	case '0':
+	  if (context == -1)
+	    context = 0;
+	  /* If a context length has already been specified,
+	     more digits allowed only if they follow right after the others.
+	     Reject two separate runs of digits, or digits after -C.  */
+	  else if (prev < '0' || prev > '9')
+	    fatal ("context length specified twice");
+
+	  context = context * 10 + c - '0';
+	  break;
+
+	case 'a':
+	  /* Treat all files as text files; never treat as binary.  */
+	  always_text_flag = 1;
+	  break;
+
+	case 'b':
+	  /* Ignore changes in amount of white space.  */
+	  ignore_space_change_flag = 1;
+	  ignore_some_changes = 1;
+	  ignore_some_line_changes = 1;
+	  break;
+
+	case 'B':
+	  /* Ignore changes affecting only blank lines.  */
+	  ignore_blank_lines_flag = 1;
+	  ignore_some_changes = 1;
+	  break;
+
+	case 'C':		/* +context[=lines] */
+	case 'U':		/* +unified[=lines] */
+	  if (optarg)
+	    {
+	      if (context >= 0)
+		fatal ("context length specified twice");
+
+	      if (ck_atoi (optarg, &context))
+		fatal ("invalid context length argument");
+	    }
+
+	  /* Falls through.  */
+	case 'c':
+	  /* Make context-style output.  */
+	  specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
+	  break;
+
+	case 'd':
+	  /* Don't discard lines.  This makes things slower (sometimes much
+	     slower) but will find a guaranteed minimal set of changes.  */
+	  no_discards = 1;
+	  break;
+
+	case 'D':
+	  /* Make merged #ifdef output.  */
+	  specify_style (OUTPUT_IFDEF);
+	  {
+	    int i, err = 0;
+	    static char const C_ifdef_group_formats[] =
+	      "#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
+	    char *b = xmalloc (sizeof (C_ifdef_group_formats)
+			       + 7 * strlen(optarg) - 14 /* 7*"%s" */
+			       - 8 /* 5*"%%" + 3*"%c" */);
+	    sprintf (b, C_ifdef_group_formats,
+		     optarg, optarg, 0,
+		     optarg, optarg, 0, 0,
+		     optarg, optarg, optarg);
+	    for (i = 0; i < 4; i++)
+	      {
+		err |= specify_format (&group_format[i], b);
+		b += strlen (b) + 1;
+	      }
+	    if (err)
+	      error ("conflicting #ifdef formats", 0, 0);
+	  }
+	  break;
+
+	case 'e':
+	  /* Make output that is a valid `ed' script.  */
+	  specify_style (OUTPUT_ED);
+	  break;
+
+	case 'f':
+	  /* Make output that looks vaguely like an `ed' script
+	     but has changes in the order they appear in the file.  */
+	  specify_style (OUTPUT_FORWARD_ED);
+	  break;
+
+	case 'F':
+	  /* Show, for each set of changes, the previous line that
+	     matches the specified regexp.  Currently affects only
+	     context-style output.  */
+	  add_regexp (&function_regexp_list, optarg);
+	  break;
+
+	case 'h':
+	  /* Split the files into chunks of around 1500 lines
+	     for faster processing.  Usually does not change the result.
+
+	     This currently has no effect.  */
+	  break;
+
+	case 'H':
+	  /* Turn on heuristics that speed processing of large files
+	     with a small density of changes.  */
+	  heuristic = 1;
+	  break;
+
+	case 'i':
+	  /* Ignore changes in case.  */
+	  ignore_case_flag = 1;
+	  ignore_some_changes = 1;
+	  ignore_some_line_changes = 1;
+	  break;
+
+	case 'I':
+	  /* Ignore changes affecting only lines that match the
+	     specified regexp.  */
+	  add_regexp (&ignore_regexp_list, optarg);
+	  ignore_some_changes = 1;
+	  break;
+
+	case 'l':
+	  /* Pass the output through `pr' to paginate it.  */
+	  paginate_flag = 1;
+#if !defined(SIGCHLD) && defined(SIGCLD)
+#define SIGCHLD SIGCLD
+#endif
+#ifdef SIGCHLD
+	  /* Pagination requires forking and waiting, and
+	     System V fork+wait does not work if SIGCHLD is ignored.  */
+	  signal (SIGCHLD, SIG_DFL);
+#endif
+	  break;
+
+	case 'L':
+	  /* Specify file labels for `-c' output headers.  */
+	  if (!file_label[0])
+	    file_label[0] = optarg;
+	  else if (!file_label[1])
+	    file_label[1] = optarg;
+	  else
+	    fatal ("too many file label options");
+	  break;
+
+	case 'n':
+	  /* Output RCS-style diffs, like `-f' except that each command
+	     specifies the number of lines affected.  */
+	  specify_style (OUTPUT_RCS);
+	  break;
+
+	case 'N':
+	  /* When comparing directories, if a file appears only in one
+	     directory, treat it as present but empty in the other.  */
+	  entire_new_file_flag = 1;
+	  break;
+
+	case 'p':
+	  /* Make context-style output and show name of last C function.  */
+	  show_c_function = 1;
+	  add_regexp (&function_regexp_list, "^[_a-zA-Z$]");
+	  break;
+
+	case 'P':
+	  /* When comparing directories, if a file appears only in
+	     the second directory of the two,
+	     treat it as present but empty in the other.  */
+	  unidirectional_new_file_flag = 1;
+	  break;
+
+	case 'q':
+	  no_details_flag = 1;
+	  break;
+
+	case 'r':
+	  /* When comparing directories,
+	     recursively compare any subdirectories found.  */
+	  recursive = 1;
+	  break;
+
+	case 's':
+	  /* Print a message if the files are the same.  */
+	  print_file_same_flag = 1;
+	  break;
+
+	case 'S':
+	  /* When comparing directories, start with the specified
+	     file name.  This is used for resuming an aborted comparison.  */
+	  dir_start_file = optarg;
+	  break;
+
+	case 't':
+	  /* Expand tabs to spaces in the output so that it preserves
+	     the alignment of the input files.  */
+	  tab_expand_flag = 1;
+	  break;
+
+	case 'T':
+	  /* Use a tab in the output, rather than a space, before the
+	     text of an input line, so as to keep the proper alignment
+	     in the input line without changing the characters in it.  */
+	  tab_align_flag = 1;
+	  break;
+
+	case 'u':
+	  /* Output the context diff in unidiff format.  */
+	  specify_style (OUTPUT_UNIFIED);
+	  break;
+
+	case 'v':
+	  printf ("diff - GNU diffutils version %s\n", version_string);
+	  exit (0);
+
+	case 'w':
+	  /* Ignore horizontal white space when comparing lines.  */
+	  ignore_all_space_flag = 1;
+	  ignore_some_changes = 1;
+	  ignore_some_line_changes = 1;
+	  break;
+
+	case 'x':
+	  add_exclude (optarg);
+	  break;
+
+	case 'X':
+	  if (add_exclude_file (optarg) != 0)
+	    pfatal_with_name (optarg);
+	  break;
+
+	case 'y':
+	  /* Use side-by-side (sdiff-style) columnar output. */
+	  specify_style (OUTPUT_SDIFF);
+	  break;
+
+	case 'W':
+	  /* Set the line width for OUTPUT_SDIFF.  */
+	  if (ck_atoi (optarg, &width) || width <= 0)
+	    fatal ("column width must be a positive integer");
+	  break;
+
+	case 129:
+	  sdiff_left_only = 1;
+	  break;
+
+	case 130:
+	  sdiff_skip_common_lines = 1;
+	  break;
+
+	case 131:
+	  /* sdiff-style columns output. */
+	  specify_style (OUTPUT_SDIFF);
+	  sdiff_help_sdiff = 1;
+	  break;
+
+	case 132:
+	case 133:
+	case 134:
+	  specify_style (OUTPUT_IFDEF);
+	  if (specify_format (&line_format[c - 132], optarg) != 0)
+	    error ("conflicting line format", 0, 0);
+	  break;
+
+	case 135:
+	  specify_style (OUTPUT_IFDEF);
+	  {
+	    int i, err = 0;
+	    for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
+	      err |= specify_format (&line_format[i], optarg);
+	    if (err)
+	      error ("conflicting line format", 0, 0);
+	  }
+	  break;
+
+	case 136:
+	case 137:
+	case 138:
+	case 139:
+	  specify_style (OUTPUT_IFDEF);
+	  if (specify_format (&group_format[c - 136], optarg) != 0)
+	    error ("conflicting group format", 0, 0);
+	  break;
+
+	case 140:
+	  if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0)
+	    fatal ("horizon must be a nonnegative integer");
+	  break;
+
+	case 141:
+	  usage ();
+	  check_stdout ();
+	  exit (0);
+
+	case 142:
+	  /* Use binary I/O when reading and writing data.
+	     On Posix hosts, this has no effect.  */
+#if HAVE_SETMODE
+	  binary_I_O = 1;
+	  setmode (STDOUT_FILENO, O_BINARY);
+#endif
+	  break;
+
+	default:
+	  try_help (0);
+	}
+      prev = c;
+    }
+
+  if (argc - optind != 2)
+    try_help (argc - optind < 2 ? "missing operand" : "extra operand");
+
+
+  {
+    /*
+     *	We maximize first the half line width, and then the gutter width,
+     *	according to the following constraints:
+     *	1.  Two half lines plus a gutter must fit in a line.
+     *	2.  If the half line width is nonzero:
+     *	    a.  The gutter width is at least GUTTER_WIDTH_MINIMUM.
+     *	    b.  If tabs are not expanded to spaces,
+     *		a half line plus a gutter is an integral number of tabs,
+     *		so that tabs in the right column line up.
+     */
+    int t = tab_expand_flag ? 1 : TAB_WIDTH;
+    int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t)  *  t;
+    sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)),
+    sdiff_column2_offset = sdiff_half_width ? off : width;
+  }
+
+  if (show_c_function && output_style != OUTPUT_UNIFIED)
+    specify_style (OUTPUT_CONTEXT);
+
+  if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED)
+    context = 0;
+  else if (context == -1)
+    /* Default amount of context for -c.  */
+    context = 3;
+
+  if (output_style == OUTPUT_IFDEF)
+    {
+      /* Format arrays are char *, not char const *,
+	 because integer formats are temporarily modified.
+	 But it is safe to assign a constant like "%=" to a format array,
+	 since "%=" does not format any integers.  */
+      int i;
+      for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
+	if (!line_format[i])
+	  line_format[i] = "%l\n";
+      if (!group_format[OLD])
+	group_format[OLD]
+	  = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<";
+      if (!group_format[NEW])
+	group_format[NEW]
+	  = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>";
+      if (!group_format[UNCHANGED])
+	group_format[UNCHANGED] = "%=";
+      if (!group_format[CHANGED])
+	group_format[CHANGED] = concat (group_format[OLD],
+					group_format[NEW], "");
+    }
+
+  no_diff_means_no_output =
+    (output_style == OUTPUT_IFDEF ?
+      (!*group_format[UNCHANGED]
+       || (strcmp (group_format[UNCHANGED], "%=") == 0
+	   && !*line_format[UNCHANGED]))
+     : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1);
+
+  switch_string = option_list (argv + 1, optind - 1);
+
+  val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
+
+  /* Print any messages that were saved up for last.  */
+  print_message_queue ();
+
+  check_stdout ();
+  exit (val);
+  return val;
+}
+
+/* Add the compiled form of regexp PATTERN to REGLIST.  */
+
+static void
+add_regexp (reglist, pattern)
+     struct regexp_list **reglist;
+     char const *pattern;
+{
+  struct regexp_list *r;
+  char const *m;
+
+  r = (struct regexp_list *) xmalloc (sizeof (*r));
+  bzero (r, sizeof (*r));
+  r->buf.fastmap = xmalloc (256);
+  m = re_compile_pattern (pattern, strlen (pattern), &r->buf);
+  if (m != 0)
+    error ("%s: %s", pattern, m);
+
+  /* Add to the start of the list, since it's easier than the end.  */
+  r->next = *reglist;
+  *reglist = r;
+}
+
+static void
+try_help (reason)
+     char const *reason;
+{
+  if (reason)
+    error ("%s", reason, 0);
+  error ("Try `%s --help' for more information.", program_name, 0);
+  exit (2);
+}
+
+static void
+check_stdout ()
+{
+  if (ferror (stdout) || fclose (stdout) != 0)
+    fatal ("write error");
+}
+
+static char const * const option_help[] = {
+"-i  --ignore-case  Consider upper- and lower-case to be the same.",
+"-w  --ignore-all-space  Ignore all white space.",
+"-b  --ignore-space-change  Ignore changes in the amount of white space.",
+"-B  --ignore-blank-lines  Ignore changes whose lines are all blank.",
+"-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE.",
+#if HAVE_SETMODE
+"--binary  Read and write data in binary mode.",
+#endif
+"-a  --text  Treat all files as text.\n",
+"-c  -C NUM  --context[=NUM]  Output NUM (default 2) lines of copied context.",
+"-u  -U NUM  --unified[=NUM]  Output NUM (default 2) lines of unified context.",
+"  -NUM  Use NUM context lines.",
+"  -L LABEL  --label LABEL  Use LABEL instead of file name.",
+"  -p  --show-c-function  Show which C function each change is in.",
+"  -F RE  --show-function-line=RE  Show the most recent line matching RE.",
+"-q  --brief  Output only whether files differ.",
+"-e  --ed  Output an ed script.",
+"-n  --rcs  Output an RCS format diff.",
+"-y  --side-by-side  Output in two columns.",
+"  -w NUM  --width=NUM  Output at most NUM (default 130) characters per line.",
+"  --left-column  Output only the left column of common lines.",
+"  --suppress-common-lines  Do not output common lines.",
+"-DNAME  --ifdef=NAME  Output merged file to show `#ifdef NAME' diffs.",
+"--GTYPE-group-format=GFMT  Similar, but format GTYPE input groups with GFMT.",
+"--line-format=LFMT  Similar, but format all input lines with LFMT.",
+"--LTYPE-line-format=LFMT  Similar, but format LTYPE input lines with LFMT.",
+"  LTYPE is `old', `new', or `unchanged'.  GTYPE is LTYPE or `changed'.",
+"  GFMT may contain:",
+"    %<  lines from FILE1",
+"    %>  lines from FILE2",
+"    %=  lines common to FILE1 and FILE2",
+"    %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER",
+"      LETTERs are as follows for new group, lower case for old group:",
+"        F  first line number",
+"        L  last line number",
+"        N  number of lines = L-F+1",
+"        E  F-1",
+"        M  L+1",
+"  LFMT may contain:",
+"    %L  contents of line",
+"    %l  contents of line, excluding any trailing newline",
+"    %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number",
+"  Either GFMT or LFMT may contain:",
+"    %%  %",
+"    %c'C'  the single character C",
+"    %c'\\OOO'  the character with octal code OOO\n",
+"-l  --paginate  Pass the output through `pr' to paginate it.",
+"-t  --expand-tabs  Expand tabs to spaces in output.",
+"-T  --initial-tab  Make tabs line up by prepending a tab.\n",
+"-r  --recursive  Recursively compare any subdirectories found.",
+"-N  --new-file  Treat absent files as empty.",
+"-P  --unidirectional-new-file  Treat absent first files as empty.",
+"-s  --report-identical-files  Report when two files are the same.",
+"-x PAT  --exclude=PAT  Exclude files that match PAT.",
+"-X FILE  --exclude-from=FILE  Exclude files that match any pattern in FILE.",
+"-S FILE  --starting-file=FILE  Start with FILE when comparing directories.\n",
+"--horizon-lines=NUM  Keep NUM lines of the common prefix and suffix.",
+"-d  --minimal  Try hard to find a smaller set of changes.",
+"-H  --speed-large-files  Assume large files and many scattered small changes.\n",
+"-v  --version  Output version info.",
+"--help  Output this help.",
+0
+};
+
+static void
+usage ()
+{
+  char const * const *p;
+
+  printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", program_name);
+  for (p = option_help;  *p;  p++)
+    printf ("  %s\n", *p);
+  printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n");
+}
+
+static int
+specify_format (var, value)
+     char **var;
+     char *value;
+{
+  int err = *var ? strcmp (*var, value) : 0;
+  *var = value;
+  return err;
+}
+
+static void
+specify_style (style)
+     enum output_style style;
+{
+  if (output_style != OUTPUT_NORMAL
+      && output_style != style)
+    error ("conflicting specifications of output style", 0, 0);
+  output_style = style;
+}
+
+static char const *
+filetype (st)
+     struct stat const *st;
+{
+  /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats.
+     To keep diagnostics grammatical, the returned string must start
+     with a consonant.  */
+
+  if (S_ISREG (st->st_mode))
+    {
+      if (st->st_size == 0)
+	return "regular empty file";
+      /* Posix.2 section 5.14.2 seems to suggest that we must read the file
+	 and guess whether it's C, Fortran, etc., but this is somewhat useless
+	 and doesn't reflect historical practice.  We're allowed to guess
+	 wrong, so we don't bother to read the file.  */
+      return "regular file";
+    }
+  if (S_ISDIR (st->st_mode)) return "directory";
+
+  /* other Posix.1 file types */
+#ifdef S_ISBLK
+  if (S_ISBLK (st->st_mode)) return "block special file";
+#endif
+#ifdef S_ISCHR
+  if (S_ISCHR (st->st_mode)) return "character special file";
+#endif
+#ifdef S_ISFIFO
+  if (S_ISFIFO (st->st_mode)) return "fifo";
+#endif
+
+  /* other Posix.1b file types */
+#ifdef S_TYPEISMQ
+  if (S_TYPEISMQ (st)) return "message queue";
+#endif
+#ifdef S_TYPEISSEM
+  if (S_TYPEISSEM (st)) return "semaphore";
+#endif
+#ifdef S_TYPEISSHM
+  if (S_TYPEISSHM (st)) return "shared memory object";
+#endif
+
+  /* other popular file types */
+  /* S_ISLNK is impossible with `fstat' and `stat'.  */
+#ifdef S_ISSOCK
+  if (S_ISSOCK (st->st_mode)) return "socket";
+#endif
+
+  return "weird file";
+}
+
+/* Compare two files (or dirs) with specified names
+   DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
+   (if DIR0 is 0, then the name is just NAME0, etc.)
+   This is self-contained; it opens the files and closes them.
+
+   Value is 0 if files are the same, 1 if different,
+   2 if there is a problem opening them.  */
+
+static int
+compare_files (dir0, name0, dir1, name1, depth)
+     char const *dir0, *dir1;
+     char const *name0, *name1;
+     int depth;
+{
+  struct file_data inf[2];
+  register int i;
+  int val;
+  int same_files;
+  int failed = 0;
+  char *free0 = 0, *free1 = 0;
+
+  /* If this is directory comparison, perhaps we have a file
+     that exists only in one of the directories.
+     If so, just print a message to that effect.  */
+
+  if (! ((name0 != 0 && name1 != 0)
+	 || (unidirectional_new_file_flag && name1 != 0)
+	 || entire_new_file_flag))
+    {
+      char const *name = name0 == 0 ? name1 : name0;
+      char const *dir = name0 == 0 ? dir1 : dir0;
+      message ("Only in %s: %s\n", dir, name);
+      /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
+      return 1;
+    }
+
+  bzero (inf, sizeof (inf));
+
+  /* Mark any nonexistent file with -1 in the desc field.  */
+  /* Mark unopened files (e.g. directories) with -2. */
+
+  inf[0].desc = name0 == 0 ? -1 : -2;
+  inf[1].desc = name1 == 0 ? -1 : -2;
+
+  /* Now record the full name of each file, including nonexistent ones.  */
+
+  if (name0 == 0)
+    name0 = name1;
+  if (name1 == 0)
+    name1 = name0;
+
+  inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0));
+  inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1));
+
+  /* Stat the files.  Record whether they are directories.  */
+
+  for (i = 0; i <= 1; i++)
+    {
+      if (inf[i].desc != -1)
+	{
+	  int stat_result;
+
+	  if (i && filename_cmp (inf[i].name, inf[0].name) == 0)
+	    {
+	      inf[i].stat = inf[0].stat;
+	      stat_result = 0;
+	    }
+	  else if (strcmp (inf[i].name, "-") == 0)
+	    {
+	      inf[i].desc = STDIN_FILENO;
+	      stat_result = fstat (STDIN_FILENO, &inf[i].stat);
+	      if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode))
+		{
+		  off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
+		  if (pos == -1)
+		    stat_result = -1;
+		  else
+		    {
+		      if (pos <= inf[i].stat.st_size)
+			inf[i].stat.st_size -= pos;
+		      else
+			inf[i].stat.st_size = 0;
+		      /* Posix.2 4.17.6.1.4 requires current time for stdin.  */
+		      time (&inf[i].stat.st_mtime);
+		    }
+		}
+	    }
+	  else
+	    stat_result = stat (inf[i].name, &inf[i].stat);
+
+	  if (stat_result != 0)
+	    {
+	      perror_with_name (inf[i].name);
+	      failed = 1;
+	    }
+	  else
+	    {
+	      inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
+	      if (inf[1 - i].desc == -1)
+		{
+		  inf[1 - i].dir_p = inf[i].dir_p;
+		  inf[1 - i].stat.st_mode = inf[i].stat.st_mode;
+		}
+	    }
+	}
+    }
+
+  if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p)
+    {
+      /* If one is a directory, and it was specified in the command line,
+	 use the file in that dir with the other file's basename.  */
+
+      int fnm_arg = inf[0].dir_p;
+      int dir_arg = 1 - fnm_arg;
+      char const *fnm = inf[fnm_arg].name;
+      char const *dir = inf[dir_arg].name;
+      char const *p = filename_lastdirchar (fnm);
+      char const *filename = inf[dir_arg].name
+	= dir_file_pathname (dir, p ? p + 1 : fnm);
+
+      if (strcmp (fnm, "-") == 0)
+	fatal ("can't compare - to a directory");
+
+      if (stat (filename, &inf[dir_arg].stat) != 0)
+	{
+	  perror_with_name (filename);
+	  failed = 1;
+	}
+      else
+	inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode);
+    }
+
+  if (failed)
+    {
+
+      /* If either file should exist but does not, return 2.  */
+
+      val = 2;
+
+    }
+  else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1
+			 && 0 < same_file (&inf[0].stat, &inf[1].stat))
+	   && no_diff_means_no_output)
+    {
+      /* The two named files are actually the same physical file.
+	 We know they are identical without actually reading them.  */
+
+      val = 0;
+    }
+  else if (inf[0].dir_p & inf[1].dir_p)
+    {
+      if (output_style == OUTPUT_IFDEF)
+	fatal ("-D option not supported with directories");
+
+      /* If both are directories, compare the files in them.  */
+
+      if (depth > 0 && !recursive)
+	{
+	  /* But don't compare dir contents one level down
+	     unless -r was specified.  */
+	  message ("Common subdirectories: %s and %s\n",
+		   inf[0].name, inf[1].name);
+	  val = 0;
+	}
+      else
+	{
+	  val = diff_dirs (inf, compare_files, depth);
+	}
+
+    }
+  else if ((inf[0].dir_p | inf[1].dir_p)
+	   || (depth > 0
+	       && (! S_ISREG (inf[0].stat.st_mode)
+		   || ! S_ISREG (inf[1].stat.st_mode))))
+    {
+      /* Perhaps we have a subdirectory that exists only in one directory.
+	 If so, just print a message to that effect.  */
+
+      if (inf[0].desc == -1 || inf[1].desc == -1)
+	{
+	  if ((inf[0].dir_p | inf[1].dir_p)
+	      && recursive
+	      && (entire_new_file_flag
+		  || (unidirectional_new_file_flag && inf[0].desc == -1)))
+	    val = diff_dirs (inf, compare_files, depth);
+	  else
+	    {
+	      char const *dir = (inf[0].desc == -1) ? dir1 : dir0;
+	      /* See Posix.2 section 4.17.6.1.1 for this format.  */
+	      message ("Only in %s: %s\n", dir, name0);
+	      val = 1;
+	    }
+	}
+      else
+	{
+	  /* We have two files that are not to be compared.  */
+
+	  /* See Posix.2 section 4.17.6.1.1 for this format.  */
+	  message5 ("File %s is a %s while file %s is a %s\n",
+		    inf[0].name, filetype (&inf[0].stat),
+		    inf[1].name, filetype (&inf[1].stat));
+
+	  /* This is a difference.  */
+	  val = 1;
+	}
+    }
+  else if ((no_details_flag & ~ignore_some_changes)
+	   && inf[0].stat.st_size != inf[1].stat.st_size
+	   && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode))
+	   && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode)))
+    {
+      message ("Files %s and %s differ\n", inf[0].name, inf[1].name);
+      val = 1;
+    }
+  else
+    {
+      /* Both exist and neither is a directory.  */
+
+      /* Open the files and record their descriptors.  */
+
+      if (inf[0].desc == -2)
+	if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0)
+	  {
+	    perror_with_name (inf[0].name);
+	    failed = 1;
+	  }
+      if (inf[1].desc == -2)
+	if (same_files)
+	  inf[1].desc = inf[0].desc;
+	else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0)
+	  {
+	    perror_with_name (inf[1].name);
+	    failed = 1;
+	  }
+
+#if HAVE_SETMODE
+      if (binary_I_O)
+	for (i = 0; i <= 1; i++)
+	  if (0 <= inf[i].desc)
+	    setmode (inf[i].desc, O_BINARY);
+#endif
+
+      /* Compare the files, if no error was found.  */
+
+      val = failed ? 2 : diff_2_files (inf, depth);
+
+      /* Close the file descriptors.  */
+
+      if (inf[0].desc >= 0 && close (inf[0].desc) != 0)
+	{
+	  perror_with_name (inf[0].name);
+	  val = 2;
+	}
+      if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc
+	  && close (inf[1].desc) != 0)
+	{
+	  perror_with_name (inf[1].name);
+	  val = 2;
+	}
+    }
+
+  /* Now the comparison has been done, if no error prevented it,
+     and VAL is the value this function will return.  */
+
+  if (val == 0 && !inf[0].dir_p)
+    {
+      if (print_file_same_flag)
+	message ("Files %s and %s are identical\n",
+		 inf[0].name, inf[1].name);
+    }
+  else
+    fflush (stdout);
+
+  if (free0)
+    free (free0);
+  if (free1)
+    free (free1);
+
+  return val;
+}
diff --git a/gnu/usr.bin/diff/diff.h b/gnu/usr.bin/diff/diff.h
new file mode 100644
index 000000000000..66c6940c72bf
--- /dev/null
+++ b/gnu/usr.bin/diff/diff.h
@@ -0,0 +1,340 @@
+/* Shared definitions for GNU DIFF
+   Copyright (C) 1988, 89, 91, 92, 93 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.  */
+
+#include "system.h"
+#include <stdio.h>
+#include "regex.h"
+
+#define TAB_WIDTH 8
+
+/* Variables for command line options */
+
+#ifndef GDIFF_MAIN
+#define EXTERN extern
+#else
+#define EXTERN
+#endif
+
+enum output_style {
+  /* Default output style.  */
+  OUTPUT_NORMAL,
+  /* Output the differences with lines of context before and after (-c).  */
+  OUTPUT_CONTEXT,
+  /* Output the differences in a unified context diff format (-u). */
+  OUTPUT_UNIFIED,
+  /* Output the differences as commands suitable for `ed' (-e).  */
+  OUTPUT_ED,
+  /* Output the diff as a forward ed script (-f).  */
+  OUTPUT_FORWARD_ED,
+  /* Like -f, but output a count of changed lines in each "command" (-n). */
+  OUTPUT_RCS,
+  /* Output merged #ifdef'd file (-D).  */
+  OUTPUT_IFDEF,
+  /* Output sdiff style (-y).  */
+  OUTPUT_SDIFF
+};
+
+/* True for output styles that are robust,
+   i.e. can handle a file that ends in a non-newline.  */
+#define ROBUST_OUTPUT_STYLE(S) ((S) != OUTPUT_ED && (S) != OUTPUT_FORWARD_ED)
+
+EXTERN enum output_style output_style;
+
+/* Nonzero if output cannot be generated for identical files.  */
+EXTERN int no_diff_means_no_output;
+
+/* Number of lines of context to show in each set of diffs.
+   This is zero when context is not to be shown.  */
+EXTERN int      context;
+
+/* Consider all files as text files (-a).
+   Don't interpret codes over 0177 as implying a "binary file".  */
+EXTERN int	always_text_flag;
+
+/* Number of lines to keep in identical prefix and suffix.  */
+EXTERN int      horizon_lines;
+
+/* Ignore changes in horizontal white space (-b).  */
+EXTERN int      ignore_space_change_flag;
+
+/* Ignore all horizontal white space (-w).  */
+EXTERN int      ignore_all_space_flag;
+
+/* Ignore changes that affect only blank lines (-B).  */
+EXTERN int      ignore_blank_lines_flag;
+
+/* 1 if lines may match even if their contents do not match exactly.
+   This depends on various options.  */
+EXTERN int      ignore_some_line_changes;
+
+/* 1 if files may match even if their contents are not byte-for-byte identical.
+   This depends on various options.  */
+EXTERN int      ignore_some_changes;
+
+/* Ignore differences in case of letters (-i).  */
+EXTERN int      ignore_case_flag;
+
+/* File labels for `-c' output headers (-L).  */
+EXTERN char *file_label[2];
+
+struct regexp_list
+{
+  struct re_pattern_buffer buf;
+  struct regexp_list *next;
+};
+
+/* Regexp to identify function-header lines (-F).  */
+EXTERN struct regexp_list *function_regexp_list;
+
+/* Ignore changes that affect only lines matching this regexp (-I).  */
+EXTERN struct regexp_list *ignore_regexp_list;
+
+/* Say only whether files differ, not how (-q).  */
+EXTERN int 	no_details_flag;
+
+/* Report files compared that match (-s).
+   Normally nothing is output when that happens.  */
+EXTERN int      print_file_same_flag;
+
+/* Output the differences with exactly 8 columns added to each line
+   so that any tabs in the text line up properly (-T).  */
+EXTERN int	tab_align_flag;
+
+/* Expand tabs in the output so the text lines up properly
+   despite the characters added to the front of each line (-t).  */
+EXTERN int	tab_expand_flag;
+
+/* In directory comparison, specify file to start with (-S).
+   All file names less than this name are ignored.  */
+EXTERN char	*dir_start_file;
+
+/* If a file is new (appears in only one dir)
+   include its entire contents (-N).
+   Then `patch' would create the file with appropriate contents.  */
+EXTERN int	entire_new_file_flag;
+
+/* If a file is new (appears in only the second dir)
+   include its entire contents (-P).
+   Then `patch' would create the file with appropriate contents.  */
+EXTERN int	unidirectional_new_file_flag;
+
+/* Pipe each file's output through pr (-l).  */
+EXTERN int	paginate_flag;
+
+enum line_class {
+  /* Lines taken from just the first file.  */
+  OLD,
+  /* Lines taken from just the second file.  */
+  NEW,
+  /* Lines common to both files.  */
+  UNCHANGED,
+  /* A hunk containing both old and new lines (line groups only).  */
+  CHANGED
+};
+
+/* Line group formats for old, new, unchanged, and changed groups.  */
+EXTERN char *group_format[CHANGED + 1];
+
+/* Line formats for old, new, and unchanged lines.  */
+EXTERN char *line_format[UNCHANGED + 1];
+
+/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */
+EXTERN int sdiff_help_sdiff;
+
+/* Tell OUTPUT_SDIFF to show only the left version of common lines. */
+EXTERN int sdiff_left_only;
+
+/* Tell OUTPUT_SDIFF to not show common lines. */
+EXTERN int sdiff_skip_common_lines;
+
+/* The half line width and column 2 offset for OUTPUT_SDIFF.  */
+EXTERN unsigned sdiff_half_width;
+EXTERN unsigned sdiff_column2_offset;
+
+/* String containing all the command options diff received,
+   with spaces between and at the beginning but none at the end.
+   If there were no options given, this string is empty.  */
+EXTERN char *	switch_string;
+
+/* Nonzero means use heuristics for better speed.  */
+EXTERN int	heuristic;
+
+/* Name of program the user invoked (for error messages).  */
+EXTERN char *program_name;
+
+/* The result of comparison is an "edit script": a chain of `struct change'.
+   Each `struct change' represents one place where some lines are deleted
+   and some are inserted.
+
+   LINE0 and LINE1 are the first affected lines in the two files (origin 0).
+   DELETED is the number of lines deleted here from file 0.
+   INSERTED is the number of lines inserted here in file 1.
+
+   If DELETED is 0 then LINE0 is the number of the line before
+   which the insertion was done; vice versa for INSERTED and LINE1.  */
+
+struct change
+{
+  struct change *link;		/* Previous or next edit command  */
+  int inserted;			/* # lines of file 1 changed here.  */
+  int deleted;			/* # lines of file 0 changed here.  */
+  int line0;			/* Line number of 1st deleted line.  */
+  int line1;			/* Line number of 1st inserted line.  */
+  char ignore;			/* Flag used in context.c */
+};
+
+/* Structures that describe the input files.  */
+
+/* Data on one input file being compared.  */
+
+struct file_data {
+    int             desc;	/* File descriptor  */
+    char const      *name;	/* File name  */
+    struct stat     stat;	/* File status from fstat()  */
+    int             dir_p;	/* nonzero if file is a directory  */
+
+    /* Buffer in which text of file is read.  */
+    char *	    buffer;
+    /* Allocated size of buffer.  */
+    size_t	    bufsize;
+    /* Number of valid characters now in the buffer. */
+    size_t	    buffered_chars;
+
+    /* Array of pointers to lines in the file.  */
+    char const **linbuf;
+
+    /* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
+       linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
+       linebuf[linbuf_base ... valid_lines - 1] contain valid data.
+       linebuf[linbuf_base ... alloc_lines - 1] are allocated.  */
+    int linbuf_base, buffered_lines, valid_lines, alloc_lines;
+
+    /* Pointer to end of prefix of this file to ignore when hashing. */
+    char const *prefix_end;
+
+    /* Count of lines in the prefix.
+       There are this many lines in the file before linbuf[0].  */
+    int prefix_lines;
+
+    /* Pointer to start of suffix of this file to ignore when hashing. */
+    char const *suffix_begin;
+
+    /* Vector, indexed by line number, containing an equivalence code for
+       each line.  It is this vector that is actually compared with that
+       of another file to generate differences. */
+    int		   *equivs;
+
+    /* Vector, like the previous one except that
+       the elements for discarded lines have been squeezed out.  */
+    int		   *undiscarded;
+
+    /* Vector mapping virtual line numbers (not counting discarded lines)
+       to real ones (counting those lines).  Both are origin-0.  */
+    int		   *realindexes;
+
+    /* Total number of nondiscarded lines. */
+    int		    nondiscarded_lines;
+
+    /* Vector, indexed by real origin-0 line number,
+       containing 1 for a line that is an insertion or a deletion.
+       The results of comparison are stored here.  */
+    char	   *changed_flag;
+
+    /* 1 if file ends in a line with no final newline. */
+    int		    missing_newline;
+
+    /* 1 more than the maximum equivalence value used for this or its
+       sibling file. */
+    int equiv_max;
+};
+
+/* Describe the two files currently being compared.  */
+
+EXTERN struct file_data files[2];
+
+/* Stdio stream to output diffs to.  */
+
+EXTERN FILE *outfile;
+
+/* Declare various functions.  */
+
+/* analyze.c */
+int diff_2_files PARAMS((struct file_data[], int));
+
+/* context.c */
+void print_context_header PARAMS((struct file_data[], int));
+void print_context_script PARAMS((struct change *, int));
+
+/* diff.c */
+int excluded_filename PARAMS((char const *));
+
+/* dir.c */
+int diff_dirs PARAMS((struct file_data const[], int (*) PARAMS((char const *, char const *, char const *, char const *, int)), int));
+
+/* ed.c */
+void print_ed_script PARAMS((struct change *));
+void pr_forward_ed_script PARAMS((struct change *));
+
+/* ifdef.c */
+void print_ifdef_script PARAMS((struct change *));
+
+/* io.c */
+int read_files PARAMS((struct file_data[], int));
+int sip PARAMS((struct file_data *, int));
+void slurp PARAMS((struct file_data *));
+
+/* normal.c */
+void print_normal_script PARAMS((struct change *));
+
+/* rcs.c */
+void print_rcs_script PARAMS((struct change *));
+
+/* side.c */
+void print_sdiff_script PARAMS((struct change *));
+
+/* util.c */
+VOID *xmalloc PARAMS((size_t));
+VOID *xrealloc PARAMS((VOID *, size_t));
+char *concat PARAMS((char const *, char const *, char const *));
+char *dir_file_pathname PARAMS((char const *, char const *));
+int change_letter PARAMS((int, int));
+int line_cmp PARAMS((char const *, char const *));
+int translate_line_number PARAMS((struct file_data const *, int));
+struct change *find_change PARAMS((struct change *));
+struct change *find_reverse_change PARAMS((struct change *));
+void analyze_hunk PARAMS((struct change *, int *, int *, int *, int *, int *, int *));
+void begin_output PARAMS((void));
+void debug_script PARAMS((struct change *));
+void error PARAMS((char const *, char const *, char const *));
+void fatal PARAMS((char const *));
+void finish_output PARAMS((void));
+void message PARAMS((char const *, char const *, char const *));
+void message5 PARAMS((char const *, char const *, char const *, char const *, char const *));
+void output_1_line PARAMS((char const *, char const *, char const *, char const *));
+void perror_with_name PARAMS((char const *));
+void pfatal_with_name PARAMS((char const *));
+void print_1_line PARAMS((char const *, char const * const *));
+void print_message_queue PARAMS((void));
+void print_number_range PARAMS((int, struct file_data *, int, int));
+void print_script PARAMS((struct change *, struct change * (*) PARAMS((struct change *)), void (*) PARAMS((struct change *))));
+void setup_output PARAMS((char const *, char const *, int));
+void translate_range PARAMS((struct file_data const *, int, int, int *, int *));
+
+/* version.c */
+extern char const version_string[];
diff --git a/gnu/usr.bin/diff/diff3.c b/gnu/usr.bin/diff/diff3.c
new file mode 100644
index 000000000000..5d94ab866dbc
--- /dev/null
+++ b/gnu/usr.bin/diff/diff3.c
@@ -0,0 +1,1778 @@
+/* Three way file comparison program (diff3) for Project GNU.
+   Copyright (C) 1988, 1989, 1992, 1993, 1994 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 */
+
+#include "system.h"
+#include <stdio.h>
+#include <signal.h>
+#include "getopt.h"
+
+extern char const version_string[];
+
+/*
+ * 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) */
+  size_t *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) */
+  size_t *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;
+
+/* Use a tab to align output lines (-T).  */
+static int tab_align_flag;
+
+/* 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 *program_name;
+
+static VOID *xmalloc PARAMS((size_t));
+static VOID *xrealloc PARAMS((VOID *, size_t));
+
+static char *read_diff PARAMS((char const *, char const *, char **));
+static char *scan_diff_line PARAMS((char *, char **, size_t *, char *, int));
+static enum diff_type process_diff_control PARAMS((char **, struct diff_block *));
+static int compare_line_list PARAMS((char * const[], size_t const[], char * const[], size_t const[], int));
+static int copy_stringlist PARAMS((char * const[], size_t const[], char *[], size_t[], int));
+static int dotlines PARAMS((FILE *, struct diff3_block *, int));
+static int output_diff3_edscript PARAMS((FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *));
+static int output_diff3_merge PARAMS((FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *));
+static size_t myread PARAMS((int, char *, size_t));
+static struct diff3_block *create_diff3_block PARAMS((int, int, int, int, int, int));
+static struct diff3_block *make_3way_diff PARAMS((struct diff_block *, struct diff_block *));
+static struct diff3_block *reverse_diff3_blocklist PARAMS((struct diff3_block *));
+static struct diff3_block *using_to_diff3_block PARAMS((struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *));
+static struct diff_block *process_diff PARAMS((char const *, char const *, struct diff_block **));
+static void check_stdout PARAMS((void));
+static void fatal PARAMS((char const *));
+static void output_diff3 PARAMS((FILE *, struct diff3_block *, int const[3], int const[3]));
+static void perror_with_exit PARAMS((char const *));
+static void try_help PARAMS((char const *));
+static void undotlines PARAMS((FILE *, int, int, int));
+static void usage PARAMS((void));
+
+static char const diff_program[] = DIFF_PROGRAM;
+
+static struct option const longopts[] =
+{
+  {"text", 0, 0, 'a'},
+  {"show-all", 0, 0, 'A'},
+  {"ed", 0, 0, 'e'},
+  {"show-overlap", 0, 0, 'E'},
+  {"label", 1, 0, 'L'},
+  {"merge", 0, 0, 'm'},
+  {"initial-tab", 0, 0, 'T'},
+  {"overlap-only", 0, 0, 'x'},
+  {"easy-only", 0, 0, '3'},
+  {"version", 0, 0, 'v'},
+  {"help", 0, 0, 129},
+  {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;
+{
+  int c, i;
+  int mapping[3];
+  int rev_mapping[3];
+  int incompat = 0;
+  int conflicts_found;
+  struct diff_block *thread0, *thread1, *last_block;
+  struct diff3_block *diff3;
+  int tag_count = 0;
+  char *tag_strings[3];
+  char *commonname;
+  char **file;
+  struct stat statb;
+
+  initialize_main (&argc, &argv);
+  program_name = argv[0];
+
+  while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 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 'T':
+	  tab_align_flag = 1;
+	  break;
+	case 'v':
+	  printf ("diff3 - GNU diffutils version %s\n", version_string);
+	  exit (0);
+	case 129:
+	  usage ();
+	  check_stdout ();
+	  exit (0);
+	case 'L':
+	  /* Handle up to three -L options.  */
+	  if (tag_count < 3)
+	    {
+	      tag_strings[tag_count++] = optarg;
+	      break;
+	    }
+	  try_help ("Too many labels were given.  The limit is 3.");
+	default:
+	  try_help (0);
+	}
+    }
+
+  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.  */
+    try_help ("incompatible options");
+
+  if (argc - optind != 3)
+    try_help (argc - optind < 3 ? "missing operand" : "extra operand");
+
+  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",
+		     program_name, file[i]);
+	    exit (2);
+	  }
+      }
+
+#if !defined(SIGCHLD) && defined(SIGCLD)
+#define SIGCHLD SIGCLD
+#endif
+#ifdef SIGCHLD
+  /* System V fork+wait does not work if SIGCHLD is ignored.  */
+  signal (SIGCHLD, SIG_DFL);
+#endif
+
+  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;
+    }
+
+  check_stdout ();
+  exit (conflicts_found);
+  return conflicts_found;
+}
+
+static void
+try_help (reason)
+     char const *reason;
+{
+  if (reason)
+    fprintf (stderr, "%s: %s\n", program_name, reason);
+  fprintf (stderr, "%s: Try `%s --help' for more information.\n",
+	   program_name, program_name);
+  exit (2);
+}
+
+static void
+check_stdout ()
+{
+  if (ferror (stdout) || fclose (stdout) != 0)
+    fatal ("write error");
+}
+
+/*
+ * Explain, patiently and kindly, how to use this program.
+ */
+static void
+usage ()
+{
+  printf ("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n\n", program_name);
+
+  printf ("%s", "\
+  -e  --ed  Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\
+  -E  --show-overlap  Output unmerged changes, bracketing conflicts.\n\
+  -A  --show-all  Output all changes, bracketing conflicts.\n\
+  -x  --overlap-only  Output overlapping changes.\n\
+  -X  Output overlapping changes, bracketing them.\n\
+  -3  --easy-only  Output unmerged nonoverlapping changes.\n\n");
+  printf ("%s", "\
+  -m  --merge  Output merged file instead of ed script (default -A).\n\
+  -L LABEL  --label=LABEL  Use LABEL instead of file name.\n\
+  -i  Append `w' and `q' commands to ed scripts.\n\
+  -a  --text  Treat all files as text.\n\
+  -T  --initial-tab  Make tabs line up by prepending a tab.\n\n");
+  printf ("%s", "\
+  -v  --version  Output version info.\n\
+  --help  Output this help.\n\n");
+  printf ("If a FILE is `-', read standard input.\n");
+}
+
+/*
+ * 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 memcmp'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 memcmp's (if a
+ *	    memcmp 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;
+
+  struct diff3_block const *last_diff3;
+
+  static struct diff3_block const zero_diff3;
+
+  /* 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] = 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 = 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 = 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 const *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 * const fromptrs[];
+     char *toptrs[];
+     size_t const fromlengths[];
+     size_t tolengths[];
+     int copynum;
+{
+  register char * const *f = fromptrs;
+  register char **t = toptrs;
+  register size_t const *fl = fromlengths;
+  register size_t *tl = tolengths;
+
+  while (copynum--)
+    {
+      if (*t)
+	{ if (*fl != *tl || memcmp (*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, size_t);
+      bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *)));
+      bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (size_t)));
+    }
+  else
+    {
+      D_LINEARRAY (result, FILE0) = 0;
+      D_LENARRAY (result, FILE0) = 0;
+    }
+
+  numlines = D_NUMLINES (result, FILE1);
+  if (numlines)
+    {
+      D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *);
+      D_LENARRAY (result, FILE1) = ALLOCATE (numlines, size_t);
+      bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *)));
+      bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (size_t)));
+    }
+  else
+    {
+      D_LINEARRAY (result, FILE1) = 0;
+      D_LENARRAY (result, FILE1) = 0;
+    }
+
+  numlines = D_NUMLINES (result, FILE2);
+  if (numlines)
+    {
+      D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *);
+      D_LENARRAY (result, FILE2) = ALLOCATE (numlines, size_t);
+      bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *)));
+      bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (size_t)));
+    }
+  else
+    {
+      D_LINEARRAY (result, FILE2) = 0;
+      D_LENARRAY (result, FILE2) = 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 * const list1[], * const list2[];
+     size_t const lengths1[], lengths2[];
+     int nl;
+{
+  char
+    * const *l1 = list1,
+    * const *l2 = list2;
+  size_t const
+    *lgths1 = lengths1,
+    *lgths2 = lengths2;
+
+  while (nl--)
+    if (!*l1 || !*l2 || *lgths1 != *lgths2++
+	|| memcmp (*l1++, *l2++, *lgths1++))
+      return 0;
+  return 1;
+}
+
+/*
+ * Routines to input and parse two way diffs.
+ */
+
+extern char **environ;
+
+static struct diff_block *
+process_diff (filea, fileb, last_block)
+     char const *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;
+  bptr = 0; /* Pacify `gcc -W'.  */
+
+  while (scan_diff < diff_limit)
+    {
+      bptr = ALLOCATE (1, struct diff_block);
+      bptr->lines[0] = bptr->lines[1] = 0;
+      bptr->lengths[0] = bptr->lengths[1] = 0;
+
+      dt = process_diff_control (&scan_diff, bptr);
+      if (dt == ERROR || *scan_diff != '\n')
+	{
+	  fprintf (stderr, "%s: diff error: ", program_name);
+	  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, size_t);
+	  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, size_t);
+	  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)	\
+	{ unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \
+	  do { holdnum = (c - '0' + holdnum * 10); }	\
+	  while (ISDIGIT (c = *++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 const *filea, *fileb;
+     char **output_placement;
+{
+  char *diff_result;
+  size_t bytes, current_chunk_size, total;
+  int fd, wstatus;
+  struct stat pipestat;
+
+  /* 302 / 1000 is log10(2.0) rounded up.  Subtract 1 for the sign bit;
+     add 1 for integer division truncation; add 1 more for a minus sign.  */
+#define INT_STRLEN_BOUND(type) ((sizeof(type)*CHAR_BIT - 1) * 302 / 1000 + 2)
+
+#if HAVE_FORK
+
+  char const *argv[7];
+  char horizon_arg[17 + INT_STRLEN_BOUND (int)];
+  char const **ap;
+  int fds[2];
+  pid_t pid;
+
+  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 = 0;
+
+  if (pipe (fds) != 0)
+    perror_with_exit ("pipe");
+
+  pid = vfork ();
+  if (pid == 0)
+    {
+      /* Child */
+      close (fds[0]);
+      if (fds[1] != STDOUT_FILENO)
+	{
+	  dup2 (fds[1], STDOUT_FILENO);
+	  close (fds[1]);
+	}
+      execve (diff_program, (char **) argv, environ);
+      /* Avoid stdio, because the parent process's buffers are inherited.  */
+      write (STDERR_FILENO, diff_program, strlen (diff_program));
+      write (STDERR_FILENO, ": not found\n", 12);
+      _exit (2);
+    }
+
+  if (pid == -1)
+    perror_with_exit ("fork failed");
+
+  close (fds[1]);		/* Prevent erroneous lack of EOF */
+  fd = fds[0];
+
+#else /* ! HAVE_FORK */
+
+  FILE *fpipe;
+  char *command = xmalloc (sizeof (diff_program) + 30 + INT_STRLEN_BOUND (int)
+			   + 4 * (strlen (filea) + strlen (fileb)));
+  char *p;
+  sprintf (command, "%s -a --horizon-lines=%d -- ",
+	   diff_program, horizon_lines);
+  p = command + strlen (command);
+  SYSTEM_QUOTE_ARG (p, filea);
+  *p++ = ' ';
+  SYSTEM_QUOTE_ARG (p, fileb);
+  *p = '\0';
+  fpipe = popen (command, "r");
+  if (!fpipe)
+    perror_with_exit (command);
+  free (command);
+  fd = fileno (fpipe);
+
+#endif /* ! HAVE_FORK */
+
+  current_chunk_size = 8 * 1024;
+  if (fstat (fd, &pipestat) == 0)
+    current_chunk_size = max (current_chunk_size, STAT_BLOCKSIZE (pipestat));
+
+  diff_result = xmalloc (current_chunk_size);
+  total = 0;
+  do {
+    bytes = myread (fd,
+		    diff_result + total,
+		    current_chunk_size - total);
+    total += bytes;
+    if (total == current_chunk_size)
+      {
+	if (current_chunk_size < 2 * current_chunk_size)
+	  current_chunk_size = 2 * current_chunk_size;
+	else if (current_chunk_size < (size_t) -1)
+	  current_chunk_size = (size_t) -1;
+	else
+	  fatal ("files are too large to fit into memory");
+	diff_result = 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;
+
+#if ! HAVE_FORK
+
+  wstatus = pclose (fpipe);
+
+#else /* HAVE_FORK */
+
+  if (close (fd) != 0)
+    perror_with_exit ("pipe close");
+  if (waitpid (pid, &wstatus, 0) < 0)
+    perror_with_exit ("waitpid failed");
+
+#endif /* HAVE_FORK */
+
+  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, leadingchar)
+     char *scan_ptr, **set_start;
+     size_t *set_length;
+     char *limit;
+     int leadingchar;
+{
+  char *line_ptr;
+
+  if (!(scan_ptr[0] == leadingchar
+	&& 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:", program_name);
+      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 const mapping[3], rev_mapping[3];
+{
+  int i;
+  int oddoneout;
+  char *cp;
+  struct diff3_block *ptr;
+  int line;
+  size_t length;
+  int dontprint;
+  static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
+  char const *line_prefix = tab_align_flag ? "\t" : "  ";
+
+  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;
+
+	  if (lowt <= hight)
+	    {
+	      line = 0;
+	      do
+		{
+		  fprintf (outputfile, line_prefix);
+		  cp = D_RELNUM (ptr, realfile, line);
+		  length = D_RELLEN (ptr, realfile, line);
+		  fwrite (cp, sizeof (char), length, outputfile);
+		}
+	      while (++line < hight - lowt + 1);
+	      if (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 const mapping[3], rev_mapping[3];
+     char const *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 const mapping[3], rev_mapping[3];
+     char const *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 const *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 = 0;  tmp;  tmp = next)
+    {
+      next = tmp->next;
+      tmp->next = prev;
+      prev = tmp;
+    }
+
+  return prev;
+}
+
+static size_t
+myread (fd, ptr, size)
+     int fd;
+     char *ptr;
+     size_t size;
+{
+  size_t result = read (fd, ptr, size);
+  if (result == -1)
+    perror_with_exit ("read failed");
+  return result;
+}
+
+static VOID *
+xmalloc (size)
+     size_t size;
+{
+  VOID *result = (VOID *) malloc (size ? size : 1);
+  if (!result)
+    fatal ("memory exhausted");
+  return result;
+}
+
+static VOID *
+xrealloc (ptr, size)
+     VOID *ptr;
+     size_t size;
+{
+  VOID *result = (VOID *) realloc (ptr, size ? size : 1);
+  if (!result)
+    fatal ("memory exhausted");
+  return result;
+}
+
+static void
+fatal (string)
+     char const *string;
+{
+  fprintf (stderr, "%s: %s\n", program_name, string);
+  exit (2);
+}
+
+static void
+perror_with_exit (string)
+     char const *string;
+{
+  int e = errno;
+  fprintf (stderr, "%s: ", program_name);
+  errno = e;
+  perror (string);
+  exit (2);
+}
diff --git a/gnu/usr.bin/diff/dir.c b/gnu/usr.bin/diff/dir.c
new file mode 100644
index 000000000000..036a86f1128e
--- /dev/null
+++ b/gnu/usr.bin/diff/dir.c
@@ -0,0 +1,216 @@
+/* Read, sort and compare two directories.  Used for GNU DIFF.
+   Copyright (C) 1988, 1989, 1992, 1993, 1994 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.  */
+
+#include "diff.h"
+
+/* Read the directory named by DIR and store into DIRDATA a sorted vector
+   of filenames for its contents.  DIR->desc == -1 means this directory is
+   known to be nonexistent, so set DIRDATA to an empty vector.
+   Return -1 (setting errno) if error, 0 otherwise.  */
+
+struct dirdata
+{
+  char const **names;	/* Sorted names of files in dir, 0-terminated.  */
+  char *data;	/* Allocated storage for file names.  */
+};
+
+static int compare_names PARAMS((void const *, void const *));
+static int dir_sort PARAMS((struct file_data const *, struct dirdata *));
+
+static int
+dir_sort (dir, dirdata)
+     struct file_data const *dir;
+     struct dirdata *dirdata;
+{
+  register struct dirent *next;
+  register int i;
+
+  /* Address of block containing the files that are described.  */
+  char const **names;
+
+  /* Number of files in directory.  */
+  size_t nnames;
+
+  /* Allocated and used storage for file name data.  */
+  char *data;
+  size_t data_alloc, data_used;
+
+  dirdata->names = 0;
+  dirdata->data = 0;
+  nnames = 0;
+  data = 0;
+
+  if (dir->desc != -1)
+    {
+      /* Open the directory and check for errors.  */
+      register DIR *reading = opendir (dir->name);
+      if (!reading)
+	return -1;
+
+      /* Initialize the table of filenames.  */
+
+      data_alloc = max (1, (size_t) dir->stat.st_size);
+      data_used = 0;
+      dirdata->data = data = xmalloc (data_alloc);
+
+      /* Read the directory entries, and insert the subfiles
+	 into the `data' table.  */
+
+      while ((errno = 0, (next = readdir (reading)) != 0))
+	{
+	  char *d_name = next->d_name;
+	  size_t d_size = NAMLEN (next) + 1;
+
+	  /* Ignore the files `.' and `..' */
+	  if (d_name[0] == '.'
+	      && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
+	    continue;
+
+	  if (excluded_filename (d_name))
+	    continue;
+
+	  while (data_alloc < data_used + d_size)
+	    dirdata->data = data = xrealloc (data, data_alloc *= 2);
+	  memcpy (data + data_used, d_name, d_size);
+	  data_used += d_size;
+	  nnames++;
+	}
+      if (errno)
+	{
+	  int e = errno;
+	  closedir (reading);
+	  errno = e;
+	  return -1;
+	}
+#if CLOSEDIR_VOID
+      closedir (reading);
+#else
+      if (closedir (reading) != 0)
+	return -1;
+#endif
+    }
+
+  /* Create the `names' table from the `data' table.  */
+  dirdata->names = names = (char const **) xmalloc (sizeof (char *)
+						    * (nnames + 1));
+  for (i = 0;  i < nnames;  i++)
+    {
+      names[i] = data;
+      data += strlen (data) + 1;
+    }
+  names[nnames] = 0;
+
+  /* Sort the table.  */
+  qsort (names, nnames, sizeof (char *), compare_names);
+
+  return 0;
+}
+
+/* Sort the files now in the table.  */
+
+static int
+compare_names (file1, file2)
+     void const *file1, *file2;
+{
+  return filename_cmp (* (char const *const *) file1,
+		       * (char const *const *) file2);
+}
+
+/* Compare the contents of two directories named in FILEVEC[0] and FILEVEC[1].
+   This is a top-level routine; it does everything necessary for diff
+   on two directories.
+
+   FILEVEC[0].desc == -1 says directory FILEVEC[0] doesn't exist,
+   but pretend it is empty.  Likewise for FILEVEC[1].
+
+   HANDLE_FILE is a caller-provided subroutine called to handle each file.
+   It gets five operands: dir and name (rel to original working dir) of file
+   in dir 0, dir and name pathname of file in dir 1, and the recursion depth.
+
+   For a file that appears in only one of the dirs, one of the name-args
+   to HANDLE_FILE is zero.
+
+   DEPTH is the current depth in recursion, used for skipping top-level
+   files by the -S option.
+
+   Returns the maximum of all the values returned by HANDLE_FILE,
+   or 2 if trouble is encountered in opening files.  */
+
+int
+diff_dirs (filevec, handle_file, depth)
+     struct file_data const filevec[];
+     int (*handle_file) PARAMS((char const *, char const *, char const *, char const *, int));
+     int depth;
+{
+  struct dirdata dirdata[2];
+  int val = 0;			/* Return value.  */
+  int i;
+
+  /* Get sorted contents of both dirs.  */
+  for (i = 0; i < 2; i++)
+    if (dir_sort (&filevec[i], &dirdata[i]) != 0)
+      {
+	perror_with_name (filevec[i].name);
+	val = 2;
+      }
+
+  if (val == 0)
+    {
+      register char const * const *names0 = dirdata[0].names;
+      register char const * const *names1 = dirdata[1].names;
+      char const *name0 = filevec[0].name;
+      char const *name1 = filevec[1].name;
+
+      /* If `-S name' was given, and this is the topmost level of comparison,
+	 ignore all file names less than the specified starting name.  */
+
+      if (dir_start_file && depth == 0)
+	{
+	  while (*names0 && filename_cmp (*names0, dir_start_file) < 0)
+	    names0++;
+	  while (*names1 && filename_cmp (*names1, dir_start_file) < 0)
+	    names1++;
+	}
+
+      /* Loop while files remain in one or both dirs.  */
+      while (*names0 || *names1)
+	{
+	  /* Compare next name in dir 0 with next name in dir 1.
+	     At the end of a dir,
+	     pretend the "next name" in that dir is very large.  */
+	  int nameorder = (!*names0 ? 1 : !*names1 ? -1
+			   : filename_cmp (*names0, *names1));
+	  int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *names0++,
+				   name1, nameorder < 0 ? 0 : *names1++,
+				   depth + 1);
+	  if (v1 > val)
+	    val = v1;
+	}
+    }
+
+  for (i = 0; i < 2; i++)
+    {
+      if (dirdata[i].names)
+	free (dirdata[i].names);
+      if (dirdata[i].data)
+	free (dirdata[i].data);
+    }
+
+  return val;
+}
diff --git a/gnu/usr.bin/diff/ed.c b/gnu/usr.bin/diff/ed.c
new file mode 100644
index 000000000000..717ef358d0ee
--- /dev/null
+++ b/gnu/usr.bin/diff/ed.c
@@ -0,0 +1,200 @@
+/* Output routines for ed-script format.
+   Copyright (C) 1988, 89, 91, 92, 93 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.  */
+
+#include "diff.h"
+
+static void print_ed_hunk PARAMS((struct change *));
+static void print_rcs_hunk PARAMS((struct change *));
+static void pr_forward_ed_hunk PARAMS((struct change *));
+
+/* Print our script as ed commands.  */
+
+void
+print_ed_script (script)
+    struct change *script;
+{
+  print_script (script, find_reverse_change, print_ed_hunk);
+}
+
+/* Print a hunk of an ed diff */
+
+static void
+print_ed_hunk (hunk)
+     struct change *hunk; 
+{
+  int f0, l0, f1, l1;
+  int deletes, inserts;
+
+#if 0
+  hunk = flip_script (hunk);
+#endif
+#ifdef DEBUG
+  debug_script (hunk);
+#endif
+
+  /* Determine range of line numbers involved in each file.  */
+  analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts);
+  if (!deletes && !inserts)
+    return;
+
+  begin_output ();
+
+  /* Print out the line number header for this hunk */
+  print_number_range (',', &files[0], f0, l0);
+  fprintf (outfile, "%c\n", change_letter (inserts, deletes));
+
+  /* Print new/changed lines from second file, if needed */
+  if (inserts)
+    {
+      int i;
+      int inserting = 1;
+      for (i = f1; i <= l1; i++)
+	{
+	  /* Resume the insert, if we stopped.  */
+	  if (! inserting)
+	    fprintf (outfile, "%da\n",
+		     i - f1 + translate_line_number (&files[0], f0) - 1);
+	  inserting = 1;
+
+	  /* If the file's line is just a dot, it would confuse `ed'.
+	     So output it with a double dot, and set the flag LEADING_DOT
+	     so that we will output another ed-command later
+	     to change the double dot into a single dot.  */
+
+	  if (files[1].linbuf[i][0] == '.'
+	      && files[1].linbuf[i][1] == '\n')
+	    {
+	      fprintf (outfile, "..\n");
+	      fprintf (outfile, ".\n");
+	      /* Now change that double dot to the desired single dot.  */
+	      fprintf (outfile, "%ds/^\\.\\././\n",
+		       i - f1 + translate_line_number (&files[0], f0));
+	      inserting = 0;
+	    }
+	  else
+	    /* Line is not `.', so output it unmodified.  */
+	    print_1_line ("", &files[1].linbuf[i]);
+	}
+
+      /* End insert mode, if we are still in it.  */
+      if (inserting)
+	fprintf (outfile, ".\n");
+    }
+}
+
+/* Print change script in the style of ed commands,
+   but print the changes in the order they appear in the input files,
+   which means that the commands are not truly useful with ed.  */
+
+void
+pr_forward_ed_script (script)
+     struct change *script;
+{
+  print_script (script, find_change, pr_forward_ed_hunk);
+}
+
+static void
+pr_forward_ed_hunk (hunk)
+     struct change *hunk;
+{
+  int i;
+  int f0, l0, f1, l1;
+  int deletes, inserts;
+
+  /* Determine range of line numbers involved in each file.  */
+  analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts);
+  if (!deletes && !inserts)
+    return;
+
+  begin_output ();
+
+  fprintf (outfile, "%c", change_letter (inserts, deletes));
+  print_number_range (' ', files, f0, l0);
+  fprintf (outfile, "\n");
+
+  /* If deletion only, print just the number range.  */
+
+  if (!inserts)
+    return;
+
+  /* For insertion (with or without deletion), print the number range
+     and the lines from file 2.  */
+
+  for (i = f1; i <= l1; i++)
+    print_1_line ("", &files[1].linbuf[i]);
+
+  fprintf (outfile, ".\n");
+}
+
+/* Print in a format somewhat like ed commands
+   except that each insert command states the number of lines it inserts.
+   This format is used for RCS.  */
+
+void
+print_rcs_script (script)
+     struct change *script;
+{
+  print_script (script, find_change, print_rcs_hunk);
+}
+
+/* Print a hunk of an RCS diff */
+
+static void
+print_rcs_hunk (hunk)
+     struct change *hunk;
+{
+  int i;
+  int f0, l0, f1, l1;
+  int deletes, inserts;
+  int tf0, tl0, tf1, tl1;
+
+  /* Determine range of line numbers involved in each file.  */
+  analyze_hunk (hunk, &f0, &l0, &f1, &l1, &deletes, &inserts);
+  if (!deletes && !inserts)
+    return;
+
+  begin_output ();
+
+  translate_range (&files[0], f0, l0, &tf0, &tl0);
+
+  if (deletes)
+    {
+      fprintf (outfile, "d");
+      /* For deletion, print just the starting line number from file 0
+	 and the number of lines deleted.  */
+      fprintf (outfile, "%d %d\n",
+	       tf0,
+	       (tl0 >= tf0 ? tl0 - tf0 + 1 : 1));	     
+    }
+
+  if (inserts)
+    {
+      fprintf (outfile, "a");
+
+      /* Take last-line-number from file 0 and # lines from file 1.  */
+      translate_range (&files[1], f1, l1, &tf1, &tl1);
+      fprintf (outfile, "%d %d\n",
+	       tl0,
+	       (tl1 >= tf1 ? tl1 - tf1 + 1 : 1));	     
+
+      /* Print the inserted lines.  */
+      for (i = f1; i <= l1; i++)
+	print_1_line ("", &files[1].linbuf[i]);
+    }
+}
diff --git a/gnu/usr.bin/diff/fnmatch.c b/gnu/usr.bin/diff/fnmatch.c
new file mode 100644
index 000000000000..22fa9df624d7
--- /dev/null
+++ b/gnu/usr.bin/diff/fnmatch.c
@@ -0,0 +1,209 @@
+/* Copyright (C) 1991, 1992, 1993 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+   using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+   (which it would do because it found this file in $srcdir).  */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <ctype.h>
+
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
+extern int errno;
+#endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+   it matches, nonzero if not.  */
+int
+fnmatch (pattern, string, flags)
+     const char *pattern;
+     const char *string;
+     int flags;
+{
+  register const char *p = pattern, *n = string;
+  register char c;
+
+/* Note that this evalutes C many times.  */
+#define FOLD(c)	((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c))
+
+  while ((c = *p++) != '\0')
+    {
+      c = FOLD (c);
+
+      switch (c)
+	{
+	case '?':
+	  if (*n == '\0')
+	    return FNM_NOMATCH;
+	  else if ((flags & FNM_FILE_NAME) && *n == '/')
+	    return FNM_NOMATCH;
+	  else if ((flags & FNM_PERIOD) && *n == '.' &&
+		   (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+	    return FNM_NOMATCH;
+	  break;
+
+	case '\\':
+	  if (!(flags & FNM_NOESCAPE))
+	    {
+	      c = *p++;
+	      c = FOLD (c);
+	    }
+	  if (FOLD (*n) != c)
+	    return FNM_NOMATCH;
+	  break;
+
+	case '*':
+	  if ((flags & FNM_PERIOD) && *n == '.' &&
+	      (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+	    return FNM_NOMATCH;
+
+	  for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+	    if (((flags & FNM_FILE_NAME) && *n == '/') ||
+		(c == '?' && *n == '\0'))
+	      return FNM_NOMATCH;
+
+	  if (c == '\0')
+	    return 0;
+
+	  {
+	    char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+	    c1 = FOLD (c1);
+	    for (--p; *n != '\0'; ++n)
+	      if ((c == '[' || FOLD (*n) == c1) &&
+		  fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+		return 0;
+	    return FNM_NOMATCH;
+	  }
+
+	case '[':
+	  {
+	    /* Nonzero if the sense of the character class is inverted.  */
+	    register int not;
+
+	    if (*n == '\0')
+	      return FNM_NOMATCH;
+
+	    if ((flags & FNM_PERIOD) && *n == '.' &&
+		(n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+	      return FNM_NOMATCH;
+
+	    not = (*p == '!' || *p == '^');
+	    if (not)
+	      ++p;
+
+	    c = *p++;
+	    for (;;)
+	      {
+		register char cstart = c, cend = c;
+
+		if (!(flags & FNM_NOESCAPE) && c == '\\')
+		  cstart = cend = *p++;
+
+		cstart = cend = FOLD (cstart);
+
+		if (c == '\0')
+		  /* [ (unterminated) loses.  */
+		  return FNM_NOMATCH;
+
+		c = *p++;
+		c = FOLD (c);
+
+		if ((flags & FNM_FILE_NAME) && c == '/')
+		  /* [/] can never match.  */
+		  return FNM_NOMATCH;
+
+		if (c == '-' && *p != ']')
+		  {
+		    cend = *p++;
+		    if (!(flags & FNM_NOESCAPE) && cend == '\\')
+		      cend = *p++;
+		    if (cend == '\0')
+		      return FNM_NOMATCH;
+		    cend = FOLD (cend);
+
+		    c = *p++;
+		  }
+
+		if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
+		  goto matched;
+
+		if (c == ']')
+		  break;
+	      }
+	    if (!not)
+	      return FNM_NOMATCH;
+	    break;
+
+	  matched:;
+	    /* Skip the rest of the [...] that already matched.  */
+	    while (c != ']')
+	      {
+		if (c == '\0')
+		  /* [... (unterminated) loses.  */
+		  return FNM_NOMATCH;
+
+		c = *p++;
+		if (!(flags & FNM_NOESCAPE) && c == '\\')
+		  /* XXX 1003.2d11 is unclear if this is right.  */
+		  ++p;
+	      }
+	    if (not)
+	      return FNM_NOMATCH;
+	  }
+	  break;
+
+	default:
+	  if (c != FOLD (*n))
+	    return FNM_NOMATCH;
+	}
+
+      ++n;
+    }
+
+  if (*n == '\0')
+    return 0;
+
+  if ((flags & FNM_LEADING_DIR) && *n == '/')
+    /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
+    return 0;
+
+  return FNM_NOMATCH;
+}
+
+#endif	/* _LIBC or not __GNU_LIBRARY__.  */
diff --git a/gnu/usr.bin/diff/fnmatch.h b/gnu/usr.bin/diff/fnmatch.h
new file mode 100644
index 000000000000..1a653ab6314b
--- /dev/null
+++ b/gnu/usr.bin/diff/fnmatch.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 1991, 1992, 1993 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef	_FNMATCH_H
+
+#define	_FNMATCH_H	1
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef	__P
+#define	__P(args)	args
+#else /* Not C++ or ANSI C.  */
+#undef	__P
+#define	__P(args)	()
+/* We can get away without defining `const' here only because in this file
+   it is used only inside the prototype for `fnmatch', which is elided in
+   non-ANSI C where `const' is problematical.  */
+#endif /* C++ or ANSI C.  */
+
+
+/* We #undef these before defining them because some losing systems
+   (HP-UX A.08.07 for example) define these in <unistd.h>.  */
+#undef	FNM_PATHNAME
+#undef	FNM_NOESCAPE
+#undef	FNM_PERIOD
+
+/* Bits set in the FLAGS argument to `fnmatch'.  */
+#define	FNM_PATHNAME	(1 << 0) /* No wildcard can ever match `/'.  */
+#define	FNM_NOESCAPE	(1 << 1) /* Backslashes don't quote special chars.  */
+#define	FNM_PERIOD	(1 << 2) /* Leading `.' is matched only explicitly.  */
+
+#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE)
+#define	FNM_FILE_NAME	FNM_PATHNAME /* Preferred GNU name.  */
+#define	FNM_LEADING_DIR	(1 << 3) /* Ignore `/...' after a match.  */
+#define	FNM_CASEFOLD	(1 << 4) /* Compare without regard to case.  */
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN.  */
+#define	FNM_NOMATCH	1
+
+/* Match STRING against the filename pattern PATTERN,
+   returning zero if it matches, FNM_NOMATCH if not.  */
+extern int fnmatch __P ((const char *__pattern, const char *__string,
+			 int __flags));
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/gnu/usr.bin/diff/getopt.c b/gnu/usr.bin/diff/getopt.c
new file mode 100644
index 000000000000..964189d2dd4f
--- /dev/null
+++ b/gnu/usr.bin/diff/getopt.c
@@ -0,0 +1,748 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94
+   	Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#endif	/* GNU C library.  */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* XXX 1003.2 says this must be 1 before any call.  */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+
+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define	my_index	strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+	return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#ifndef __STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+static const char *
+_getopt_initialize (optstring)
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind = 1;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+  return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns `EOF'.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0)
+    optstring = _getopt_initialize (optstring);
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      if (ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (last_nonopt != optind)
+	    first_nonopt = optind;
+
+	  /* Skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (optind < argc
+		 && (argv[optind][0] != '-' || argv[optind][1] == '\0'))
+	    optind++;
+	  last_nonopt = optind;
+	}
+
+      /* The special ARGV-element `--' means premature end of options.
+	 Skip it like a null option,
+	 then exchange with previous non-options as if it were an option,
+	 then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+	{
+	  optind++;
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (first_nonopt == last_nonopt)
+	    first_nonopt = optind;
+	  last_nonopt = argc;
+
+	  optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+	 and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (first_nonopt != last_nonopt)
+	    optind = first_nonopt;
+	  return EOF;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+	 either stop the scan or describe it to the caller and pass it by.  */
+
+      if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
+	{
+	  if (ordering == REQUIRE_ORDER)
+	    return EOF;
+	  optarg = argv[optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+	 Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+		  + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+	  || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+	/* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+	 or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	if (!strncmp (p->name, nextchar, nameend - nextchar))
+	  {
+	    if (nameend - nextchar == strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else
+	      /* Second or later nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (opterr)
+	    fprintf (stderr, "%s: option `%s' is ambiguous\n",
+		     argv[0], argv[optind]);
+	  nextchar += strlen (nextchar);
+	  optind++;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  optind++;
+	  if (*nameend)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+		 allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		optarg = nameend + 1;
+	      else
+		{
+		  if (opterr)
+		    {
+		      if (argv[optind - 1][1] == '-')
+			/* --option */
+			fprintf (stderr,
+				 "%s: option `--%s' doesn't allow an argument\n",
+				 argv[0], pfound->name);
+		      else
+			/* +option or -option */
+			fprintf (stderr,
+			     "%s: option `%c%s' doesn't allow an argument\n",
+			     argv[0], argv[optind - 1][0], pfound->name);
+		    }
+		  nextchar += strlen (nextchar);
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (optind < argc)
+		optarg = argv[optind++];
+	      else
+		{
+		  if (opterr)
+		    fprintf (stderr, "%s: option `%s' requires an argument\n",
+			     argv[0], argv[optind - 1]);
+		  nextchar += strlen (nextchar);
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  nextchar += strlen (nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+	 or the option starts with '--' or is not a valid short
+	 option, then it's an error.
+	 Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+	  || my_index (optstring, *nextchar) == NULL)
+	{
+	  if (opterr)
+	    {
+	      if (argv[optind][1] == '-')
+		/* --option */
+		fprintf (stderr, "%s: unrecognized option `--%s'\n",
+			 argv[0], nextchar);
+	      else
+		/* +option or -option */
+		fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+			 argv[0], argv[optind][0], nextchar);
+	    }
+	  nextchar = (char *) "";
+	  optind++;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (opterr)
+	  {
+	    if (posixly_correct)
+	      /* 1003.2 specifies the format of this message.  */
+	      fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+	    else
+	      fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c);
+	  }
+	optopt = c;
+	return '?';
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		optind++;
+	      }
+	    else
+	      optarg = NULL;
+	    nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		optind++;
+	      }
+	    else if (optind == argc)
+	      {
+		if (opterr)
+		  {
+		    /* 1003.2 specifies the format of this message.  */
+		    fprintf (stderr, "%s: option requires an argument -- %c\n",
+			     argv[0], c);
+		  }
+		optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+		 increment it again when taking next ARGV-elt as argument.  */
+	      optarg = argv[optind++];
+	    nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif	/* _LIBC or not __GNU_LIBRARY__.  */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == EOF)
+	break;
+
+      switch (c)
+	{
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/diff/getopt1.c b/gnu/usr.bin/diff/getopt1.c
new file mode 100644
index 000000000000..725c653bbcc2
--- /dev/null
+++ b/gnu/usr.bin/diff/getopt1.c
@@ -0,0 +1,180 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+	Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef	NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif	/* _LIBC or not __GNU_LIBRARY__.  */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+	{"add", 1, 0, 0},
+	{"append", 0, 0, 0},
+	{"delete", 1, 0, 0},
+	{"verbose", 0, 0, 0},
+	{"create", 0, 0, 0},
+	{"file", 1, 0, 0},
+	{0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+		       long_options, &option_index);
+      if (c == EOF)
+	break;
+
+      switch (c)
+	{
+	case 0:
+	  printf ("option %s", long_options[option_index].name);
+	  if (optarg)
+	    printf (" with arg %s", optarg);
+	  printf ("\n");
+	  break;
+
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case 'd':
+	  printf ("option d with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/diff/ifdef.c b/gnu/usr.bin/diff/ifdef.c
new file mode 100644
index 000000000000..2834cbdfa236
--- /dev/null
+++ b/gnu/usr.bin/diff/ifdef.c
@@ -0,0 +1,428 @@
+/* #ifdef-format output routines for GNU DIFF.
+   Copyright (C) 1989, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU DIFF.
+
+GNU DIFF is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY.  No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing.  Refer to the GNU DIFF General Public
+License for full details.
+
+Everyone is granted permission to copy, modify and redistribute
+GNU DIFF, but only under the conditions described in the
+GNU DIFF General Public License.   A copy of this license is
+supposed to have been given to you along with GNU DIFF so you
+can know your rights and responsibilities.  It should be in a
+file named COPYING.  Among other things, the copyright notice
+and this notice must be preserved on all copies.  */
+
+
+#include "diff.h"
+
+struct group
+{
+  struct file_data const *file;
+  int from, upto; /* start and limit lines for this group of lines */
+};
+
+static char *format_group PARAMS((FILE *, char *, int, struct group const *));
+static char *scan_char_literal PARAMS((char *, int *));
+static char *scan_printf_spec PARAMS((char *));
+static int groups_letter_value PARAMS((struct group const *, int));
+static void format_ifdef PARAMS((char *, int, int, int, int));
+static void print_ifdef_hunk PARAMS((struct change *));
+static void print_ifdef_lines PARAMS((FILE *, char *, struct group const *));
+
+static int next_line;
+
+/* Print the edit-script SCRIPT as a merged #ifdef file.  */
+
+void
+print_ifdef_script (script)
+     struct change *script;
+{
+  next_line = - files[0].prefix_lines;
+  print_script (script, find_change, print_ifdef_hunk);
+  if (next_line < files[0].valid_lines)
+    {
+      begin_output ();
+      format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
+		    next_line - files[0].valid_lines + files[1].valid_lines,
+		    files[1].valid_lines);
+    }
+}
+
+/* Print a hunk of an ifdef diff.
+   This is a contiguous portion of a complete edit script,
+   describing changes in consecutive lines.  */
+
+static void
+print_ifdef_hunk (hunk)
+     struct change *hunk;
+{
+  int first0, last0, first1, last1, deletes, inserts;
+  char *format;
+
+  /* Determine range of line numbers involved in each file.  */
+  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
+  if (inserts)
+    format = deletes ? group_format[CHANGED] : group_format[NEW];
+  else if (deletes)
+    format = group_format[OLD];
+  else
+    return;
+
+  begin_output ();
+
+  /* Print lines up to this change.  */
+  if (next_line < first0)
+    format_ifdef (group_format[UNCHANGED], next_line, first0,
+		  next_line - first0 + first1, first1);
+
+  /* Print this change.  */
+  next_line = last0 + 1;
+  format_ifdef (format, first0, next_line, first1, last1 + 1);
+}
+
+/* Print a set of lines according to FORMAT.
+   Lines BEG0 up to END0 are from the first file;
+   lines BEG1 up to END1 are from the second file.  */
+
+static void
+format_ifdef (format, beg0, end0, beg1, end1)
+     char *format;
+     int beg0, end0, beg1, end1;
+{
+  struct group groups[2];
+
+  groups[0].file = &files[0];
+  groups[0].from = beg0;
+  groups[0].upto = end0;
+  groups[1].file = &files[1];
+  groups[1].from = beg1;
+  groups[1].upto = end1;
+  format_group (outfile, format, '\0', groups);
+}
+
+/* Print to file OUT a set of lines according to FORMAT.
+   The format ends at the first free instance of ENDCHAR.
+   Yield the address of the terminating character.
+   GROUPS specifies which lines to print.
+   If OUT is zero, do not actually print anything; just scan the format.  */
+
+static char *
+format_group (out, format, endchar, groups)
+     register FILE *out;
+     char *format;
+     int endchar;
+     struct group const *groups;
+{
+  register char c;
+  register char *f = format;
+
+  while ((c = *f) != endchar && c != 0)
+    {
+      f++;
+      if (c == '%')
+	{
+	  char *spec = f;
+	  switch ((c = *f++))
+	    {
+	    case '%':
+	      break;
+
+	    case '(':
+	      /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
+	      {
+		int i, value[2];
+		FILE *thenout, *elseout;
+
+		for (i = 0; i < 2; i++)
+		  {
+		    unsigned char f0 = f[0];
+		    if (ISDIGIT (f0))
+		      {
+			value[i] = atoi (f);
+			while (ISDIGIT ((unsigned char) *++f))
+			  continue;
+		      }
+		    else
+		      {
+			value[i] = groups_letter_value (groups, f0);
+			if (value[i] < 0)
+			  goto bad_format;
+			f++;
+		      }
+		    if (*f++ != "=?"[i])
+		      goto bad_format;
+		  }
+		if (value[0] == value[1])
+		  thenout = out, elseout = 0;
+		else
+		  thenout = 0, elseout = out;
+		f = format_group (thenout, f, ':', groups);
+		if (*f)
+		  {
+		    f = format_group (elseout, f + 1, ')', groups);
+		    if (*f)
+		      f++;
+		  }
+	      }
+	      continue;
+
+	    case '<':
+	      /* Print lines deleted from first file.  */
+	      print_ifdef_lines (out, line_format[OLD], &groups[0]);
+	      continue;
+
+	    case '=':
+	      /* Print common lines.  */
+	      print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
+	      continue;
+
+	    case '>':
+	      /* Print lines inserted from second file.  */
+	      print_ifdef_lines (out, line_format[NEW], &groups[1]);
+	      continue;
+
+	    default:
+	      {
+		int value;
+		char *speclim;
+
+		f = scan_printf_spec (spec);
+		if (!f)
+		  goto bad_format;
+		speclim = f;
+		c = *f++;
+		switch (c)
+		  {
+		    case '\'':
+		      f = scan_char_literal (f, &value);
+		      if (!f)
+			goto bad_format;
+		      break;
+
+		    default:
+		      value = groups_letter_value (groups, c);
+		      if (value < 0)
+			goto bad_format;
+		      break;
+		  }
+		if (out)
+		  {
+		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
+		    *speclim = 0;
+		    fprintf (out, spec - 1, value);
+		    /* Undo the temporary replacement.  */
+		    *speclim = c;
+		  }
+	      }
+	      continue;
+
+	    bad_format:
+	      c = '%';
+	      f = spec;
+	      break;
+	    }
+	}
+      if (out)
+	putc (c, out);
+    }
+  return f;
+}
+
+/* For the line group pair G, return the number corresponding to LETTER.
+   Return -1 if LETTER is not a group format letter.  */
+static int
+groups_letter_value (g, letter)
+     struct group const *g;
+     int letter;
+{
+  if (ISUPPER (letter))
+    {
+      g++;
+      letter = tolower (letter);
+    }
+  switch (letter)
+    {
+      case 'e': return translate_line_number (g->file, g->from) - 1;
+      case 'f': return translate_line_number (g->file, g->from);
+      case 'l': return translate_line_number (g->file, g->upto) - 1;
+      case 'm': return translate_line_number (g->file, g->upto);
+      case 'n': return g->upto - g->from;
+      default: return -1;
+    }
+}
+
+/* Print to file OUT, using FORMAT to print the line group GROUP.
+   But do nothing if OUT is zero.  */
+static void
+print_ifdef_lines (out, format, group)
+     register FILE *out;
+     char *format;
+     struct group const *group;
+{
+  struct file_data const *file = group->file;
+  char const * const *linbuf = file->linbuf;
+  int from = group->from, upto = group->upto;
+
+  if (!out)
+    return;
+
+  /* If possible, use a single fwrite; it's faster.  */
+  if (!tab_expand_flag && format[0] == '%')
+    {
+      if (format[1] == 'l' && format[2] == '\n' && !format[3])
+	{
+	  fwrite (linbuf[from], sizeof (char),
+		  linbuf[upto] + (linbuf[upto][-1] != '\n') -  linbuf[from],
+		  out);
+	  return;
+	}
+      if (format[1] == 'L' && !format[2])
+	{
+	  fwrite (linbuf[from], sizeof (char),
+		  linbuf[upto] -  linbuf[from], out);
+	  return;
+	}
+    }
+
+  for (;  from < upto;  from++)
+    {
+      register char c;
+      register char *f = format;
+
+      while ((c = *f++) != 0)
+	{
+	  if (c == '%')
+	    {
+	      char *spec = f;
+	      switch ((c = *f++))
+		{
+		case '%':
+		  break;
+
+		case 'l':
+		  output_1_line (linbuf[from],
+				 linbuf[from + 1]
+				   - (linbuf[from + 1][-1] == '\n'), 0, 0);
+		  continue;
+
+		case 'L':
+		  output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
+		  continue;
+
+		default:
+		  {
+		    int value;
+		    char *speclim;
+
+		    f = scan_printf_spec (spec);
+		    if (!f)
+		      goto bad_format;
+		    speclim = f;
+		    c = *f++;
+		    switch (c)
+		      {
+			case '\'':
+			  f = scan_char_literal (f, &value);
+			  if (!f)
+			    goto bad_format;
+			  break;
+
+			case 'n':
+			  value = translate_line_number (file, from);
+			  break;
+
+			default:
+			  goto bad_format;
+		      }
+		    /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
+		    *speclim = 0;
+		    fprintf (out, spec - 1, value);
+		    /* Undo the temporary replacement.  */
+		    *speclim = c;
+		  }
+		  continue;
+
+		bad_format:
+		  c = '%';
+		  f = spec;
+		  break;
+		}
+	    }
+	  putc (c, out);
+	}
+    }
+}
+
+/* Scan the character literal represented in the string LIT; LIT points just
+   after the initial apostrophe.  Put the literal's value into *INTPTR.
+   Yield the address of the first character after the closing apostrophe,
+   or zero if the literal is ill-formed.  */
+static char *
+scan_char_literal (lit, intptr)
+     char *lit;
+     int *intptr;
+{
+  register char *p = lit;
+  int value, digits;
+  char c = *p++;
+
+  switch (c)
+    {
+      case 0:
+      case '\'':
+	return 0;
+
+      case '\\':
+	value = 0;
+	while ((c = *p++) != '\'')
+	  {
+	    unsigned digit = c - '0';
+	    if (8 <= digit)
+	      return 0;
+	    value = 8 * value + digit;
+	  }
+	digits = p - lit - 2;
+	if (! (1 <= digits && digits <= 3))
+	  return 0;
+	break;
+
+      default:
+	value = c;
+	if (*p++ != '\'')
+	  return 0;
+	break;
+    }
+  *intptr = value;
+  return p;
+}
+
+/* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
+   Return the address of the character following SPEC, or zero if failure.  */
+static char *
+scan_printf_spec (spec)
+     register char *spec;
+{
+  register unsigned char c;
+
+  while ((c = *spec++) == '-')
+    continue;
+  while (ISDIGIT (c))
+    c = *spec++;
+  if (c == '.')
+    while (ISDIGIT (c = *spec++))
+      continue;
+  switch (c)
+    {
+      case 'c': case 'd': case 'o': case 'x': case 'X':
+	return spec;
+
+      default:
+	return 0;
+    }
+}
diff --git a/gnu/usr.bin/diff/io.c b/gnu/usr.bin/diff/io.c
new file mode 100644
index 000000000000..660591551091
--- /dev/null
+++ b/gnu/usr.bin/diff/io.c
@@ -0,0 +1,714 @@
+/* File I/O for GNU DIFF.
+   Copyright (C) 1988, 1989, 1992, 1993, 1994 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.  */
+
+#include "diff.h"
+
+/* Rotate a value n bits to the left. */
+#define UINT_BIT (sizeof (unsigned) * CHAR_BIT)
+#define ROL(v, n) ((v) << (n) | (v) >> (UINT_BIT - (n)))
+
+/* Given a hash value and a new character, return a new hash value. */
+#define HASH(h, c) ((c) + ROL (h, 7))
+
+/* Guess remaining number of lines from number N of lines so far,
+   size S so far, and total size T.  */
+#define GUESS_LINES(n,s,t) (((t) - (s)) / ((n) < 10 ? 32 : (s) / ((n)-1)) + 5)
+
+/* Type used for fast prefix comparison in find_identical_ends.  */
+#ifndef word
+#define word int
+#endif
+
+/* Lines are put into equivalence classes (of lines that match in line_cmp).
+   Each equivalence class is represented by one of these structures,
+   but only while the classes are being computed.
+   Afterward, each class is represented by a number.  */
+struct equivclass
+{
+  int next;	/* Next item in this bucket. */
+  unsigned hash;	/* Hash of lines in this class.  */
+  char const *line;	/* A line that fits this class. */
+  size_t length;	/* That line's length, not counting its newline.  */
+};
+
+/* Hash-table: array of buckets, each being a chain of equivalence classes.
+   buckets[-1] is reserved for incomplete lines.  */
+static int *buckets;
+
+/* Number of buckets in the hash table array, not counting buckets[-1]. */
+static int nbuckets;
+
+/* Array in which the equivalence classes are allocated.
+   The bucket-chains go through the elements in this array.
+   The number of an equivalence class is its index in this array.  */
+static struct equivclass *equivs;
+
+/* Index of first free element in the array `equivs'.  */
+static int equivs_index;
+
+/* Number of elements allocated in the array `equivs'.  */
+static int equivs_alloc;
+
+static void find_and_hash_each_line PARAMS((struct file_data *));
+static void find_identical_ends PARAMS((struct file_data[]));
+static void prepare_text_end PARAMS((struct file_data *));
+
+/* Check for binary files and compare them for exact identity.  */
+
+/* Return 1 if BUF contains a non text character.
+   SIZE is the number of characters in BUF.  */
+
+#define binary_file_p(buf, size) (memchr (buf, '\0', size) != 0)
+
+/* Get ready to read the current file.
+   Return nonzero if SKIP_TEST is zero,
+   and if it appears to be a binary file.  */
+
+int
+sip (current, skip_test)
+     struct file_data *current;
+     int skip_test;
+{
+  /* If we have a nonexistent file at this stage, treat it as empty.  */
+  if (current->desc < 0)
+    {
+      /* Leave room for a sentinel.  */
+      current->bufsize = sizeof (word);
+      current->buffer = xmalloc (current->bufsize);
+    }
+  else
+    {
+      current->bufsize = STAT_BLOCKSIZE (current->stat);
+      current->buffer = xmalloc (current->bufsize);
+
+      if (! skip_test)
+	{
+	  /* Check first part of file to see if it's a binary file.  */
+#if HAVE_SETMODE
+	  int oldmode = setmode (current->desc, O_BINARY);
+#endif
+	  size_t n = read (current->desc, current->buffer, current->bufsize);
+	  if (n == -1)
+	    pfatal_with_name (current->name);
+	  current->buffered_chars = n;
+#if HAVE_SETMODE
+	  if (oldmode != O_BINARY)
+	    {
+	      if (lseek (current->desc, - (off_t) n, SEEK_CUR) == -1)
+		pfatal_with_name (current->name);
+	      setmode (current->desc, oldmode);
+	      current->buffered_chars = 0;
+	    }
+#endif
+	  return binary_file_p (current->buffer, n);
+	}
+    }
+
+  current->buffered_chars = 0;
+  return 0;
+}
+
+/* Slurp the rest of the current file completely into memory.  */
+
+void
+slurp (current)
+     struct file_data *current;
+{
+  size_t cc;
+
+  if (current->desc < 0)
+    /* The file is nonexistent.  */
+    ;
+  else if (S_ISREG (current->stat.st_mode))
+    {
+      /* It's a regular file; slurp in the rest all at once.  */
+
+      /* Get the size out of the stat block.
+	 Allocate enough room for appended newline and sentinel.  */
+      cc = current->stat.st_size + 1 + sizeof (word);
+      if (current->bufsize < cc)
+	{
+	  current->bufsize = cc;
+	  current->buffer = xrealloc (current->buffer, cc);
+	}
+
+      if (current->buffered_chars < current->stat.st_size)
+	{
+	  cc = read (current->desc,
+		     current->buffer + current->buffered_chars,
+		     current->stat.st_size - current->buffered_chars);
+	  if (cc == -1)
+	    pfatal_with_name (current->name);
+	  current->buffered_chars += cc;
+	}
+    }
+  /* It's not a regular file; read it, growing the buffer as needed.  */
+  else if (always_text_flag || current->buffered_chars != 0)
+    {
+      for (;;)
+	{
+	  if (current->buffered_chars == current->bufsize)
+	    {
+	      current->bufsize = current->bufsize * 2;
+	      current->buffer = xrealloc (current->buffer, current->bufsize);
+	    }
+	  cc = read (current->desc,
+		     current->buffer + current->buffered_chars,
+		     current->bufsize - current->buffered_chars);
+	  if (cc == 0)
+	    break;
+	  if (cc == -1)
+	    pfatal_with_name (current->name);
+	  current->buffered_chars += cc;
+	}
+      /* Allocate just enough room for appended newline and sentinel.  */
+      current->bufsize = current->buffered_chars + 1 + sizeof (word);
+      current->buffer = xrealloc (current->buffer, current->bufsize);
+    }
+}
+
+/* Split the file into lines, simultaneously computing the equivalence class for
+   each line. */
+
+static void
+find_and_hash_each_line (current)
+     struct file_data *current;
+{
+  unsigned h;
+  unsigned char const *p = (unsigned char const *) current->prefix_end;
+  unsigned char c;
+  int i, *bucket;
+  size_t length;
+
+  /* Cache often-used quantities in local variables to help the compiler.  */
+  char const **linbuf = current->linbuf;
+  int alloc_lines = current->alloc_lines;
+  int line = 0;
+  int linbuf_base = current->linbuf_base;
+  int *cureqs = (int *) xmalloc (alloc_lines * sizeof (int));
+  struct equivclass *eqs = equivs;
+  int eqs_index = equivs_index;
+  int eqs_alloc = equivs_alloc;
+  char const *suffix_begin = current->suffix_begin;
+  char const *bufend = current->buffer + current->buffered_chars;
+  int use_line_cmp = ignore_some_line_changes;
+
+  while ((char const *) p < suffix_begin)
+    {
+      char const *ip = (char const *) p;
+
+      /* Compute the equivalence class for this line.  */
+
+      h = 0;
+
+      /* Hash this line until we find a newline. */
+      if (ignore_case_flag)
+	{
+	  if (ignore_all_space_flag)
+	    while ((c = *p++) != '\n')
+	      {
+		if (! ISSPACE (c))
+		  h = HASH (h, ISUPPER (c) ? tolower (c) : c);
+	      }
+	  else if (ignore_space_change_flag)
+	    while ((c = *p++) != '\n')
+	      {
+		if (ISSPACE (c))
+		  {
+		    for (;;)
+		      {
+			c = *p++;
+			if (!ISSPACE (c))
+			  break;
+			if (c == '\n')
+			  goto hashing_done;
+		      }
+		    h = HASH (h, ' ');
+		  }
+		/* C is now the first non-space.  */
+		h = HASH (h, ISUPPER (c) ? tolower (c) : c);
+	      }
+	  else
+	    while ((c = *p++) != '\n')
+	      h = HASH (h, ISUPPER (c) ? tolower (c) : c);
+	}
+      else
+	{
+	  if (ignore_all_space_flag)
+	    while ((c = *p++) != '\n')
+	      {
+		if (! ISSPACE (c))
+		  h = HASH (h, c);
+	      }
+	  else if (ignore_space_change_flag)
+	    while ((c = *p++) != '\n')
+	      {
+		if (ISSPACE (c))
+		  {
+		    for (;;)
+		      {
+			c = *p++;
+			if (!ISSPACE (c))
+			  break;
+			if (c == '\n')
+			  goto hashing_done;
+		      }
+		    h = HASH (h, ' ');
+		  }
+		/* C is now the first non-space.  */
+		h = HASH (h, c);
+	      }
+	  else
+	    while ((c = *p++) != '\n')
+	      h = HASH (h, c);
+	}
+   hashing_done:;
+
+      bucket = &buckets[h % nbuckets];
+      length = (char const *) p - ip - 1;
+
+      if ((char const *) p == bufend
+	  && current->missing_newline
+	  && ROBUST_OUTPUT_STYLE (output_style))
+	{
+	  /* This line is incomplete.  If this is significant,
+	     put the line into bucket[-1].  */
+	  if (! (ignore_space_change_flag | ignore_all_space_flag))
+	    bucket = &buckets[-1];
+
+	  /* Omit the inserted newline when computing linbuf later.  */
+	  p--;
+	  bufend = suffix_begin = (char const *) p;
+	}
+
+      for (i = *bucket;  ;  i = eqs[i].next)
+	if (!i)
+	  {
+	    /* Create a new equivalence class in this bucket. */
+	    i = eqs_index++;
+	    if (i == eqs_alloc)
+	      eqs = (struct equivclass *)
+		      xrealloc (eqs, (eqs_alloc*=2) * sizeof(*eqs));
+	    eqs[i].next = *bucket;
+	    eqs[i].hash = h;
+	    eqs[i].line = ip;
+	    eqs[i].length = length;
+	    *bucket = i;
+	    break;
+	  }
+	else if (eqs[i].hash == h)
+	  {
+	    char const *eqline = eqs[i].line;
+
+	    /* Reuse existing equivalence class if the lines are identical.
+	       This detects the common case of exact identity
+	       faster than complete comparison would.  */
+	    if (eqs[i].length == length && memcmp (eqline, ip, length) == 0)
+	      break;
+
+	    /* Reuse existing class if line_cmp reports the lines equal.  */
+	    if (use_line_cmp && line_cmp (eqline, ip) == 0)
+	      break;
+	  }
+
+      /* Maybe increase the size of the line table. */
+      if (line == alloc_lines)
+	{
+	  /* Double (alloc_lines - linbuf_base) by adding to alloc_lines.  */
+	  alloc_lines = 2 * alloc_lines - linbuf_base;
+	  cureqs = (int *) xrealloc (cureqs, alloc_lines * sizeof (*cureqs));
+	  linbuf = (char const **) xrealloc (linbuf + linbuf_base,
+					     (alloc_lines - linbuf_base)
+					     * sizeof (*linbuf))
+		   - linbuf_base;
+	}
+      linbuf[line] = ip;
+      cureqs[line] = i;
+      ++line;
+    }
+
+  current->buffered_lines = line;
+
+  for (i = 0;  ;  i++)
+    {
+      /* Record the line start for lines in the suffix that we care about.
+	 Record one more line start than lines,
+	 so that we can compute the length of any buffered line.  */
+      if (line == alloc_lines)
+	{
+	  /* Double (alloc_lines - linbuf_base) by adding to alloc_lines.  */
+	  alloc_lines = 2 * alloc_lines - linbuf_base;
+	  linbuf = (char const **) xrealloc (linbuf + linbuf_base,
+					     (alloc_lines - linbuf_base)
+					     * sizeof (*linbuf))
+		   - linbuf_base;
+	}
+      linbuf[line] = (char const *) p;
+
+      if ((char const *) p == bufend)
+	break;
+
+      if (context <= i && no_diff_means_no_output)
+	break;
+
+      line++;
+
+      while (*p++ != '\n')
+	;
+    }
+
+  /* Done with cache in local variables.  */
+  current->linbuf = linbuf;
+  current->valid_lines = line;
+  current->alloc_lines = alloc_lines;
+  current->equivs = cureqs;
+  equivs = eqs;
+  equivs_alloc = eqs_alloc;
+  equivs_index = eqs_index;
+}
+
+/* Prepare the end of the text.  Make sure it's initialized.
+   Make sure text ends in a newline,
+   but remember that we had to add one.  */
+
+static void
+prepare_text_end (current)
+     struct file_data *current;
+{
+  size_t buffered_chars = current->buffered_chars;
+  char *p = current->buffer;
+
+  if (buffered_chars == 0 || p[buffered_chars - 1] == '\n')
+    current->missing_newline = 0;
+  else
+    {
+      p[buffered_chars++] = '\n';
+      current->buffered_chars = buffered_chars;
+      current->missing_newline = 1;
+    }
+
+  /* Don't use uninitialized storage when planting or using sentinels.  */
+  if (p)
+    bzero (p + buffered_chars, sizeof (word));
+}
+
+/* Given a vector of two file_data objects, find the identical
+   prefixes and suffixes of each object. */
+
+static void
+find_identical_ends (filevec)
+     struct file_data filevec[];
+{
+  word *w0, *w1;
+  char *p0, *p1, *buffer0, *buffer1;
+  char const *end0, *beg0;
+  char const **linbuf0, **linbuf1;
+  int i, lines;
+  size_t n0, n1, tem;
+  int alloc_lines0, alloc_lines1;
+  int buffered_prefix, prefix_count, prefix_mask;
+
+  slurp (&filevec[0]);
+  if (filevec[0].desc != filevec[1].desc)
+    slurp (&filevec[1]);
+  else
+    {
+      filevec[1].buffer = filevec[0].buffer;
+      filevec[1].bufsize = filevec[0].bufsize;
+      filevec[1].buffered_chars = filevec[0].buffered_chars;
+    }
+  for (i = 0; i < 2; i++)
+    prepare_text_end (&filevec[i]);
+
+  /* Find identical prefix.  */
+
+  p0 = buffer0 = filevec[0].buffer;
+  p1 = buffer1 = filevec[1].buffer;
+
+  n0 = filevec[0].buffered_chars;
+  n1 = filevec[1].buffered_chars;
+
+  if (p0 == p1)
+    /* The buffers are the same; sentinels won't work.  */
+    p0 = p1 += n1;
+  else
+    {
+      /* Insert end sentinels, in this case characters that are guaranteed
+	 to make the equality test false, and thus terminate the loop.  */
+
+      if (n0 < n1)
+	p0[n0] = ~p1[n0];
+      else
+	p1[n1] = ~p0[n1];
+
+      /* Loop until first mismatch, or to the sentinel characters.  */
+
+      /* Compare a word at a time for speed.  */
+      w0 = (word *) p0;
+      w1 = (word *) p1;
+      while (*w0++ == *w1++)
+	;
+      --w0, --w1;
+
+      /* Do the last few bytes of comparison a byte at a time.  */
+      p0 = (char *) w0;
+      p1 = (char *) w1;
+      while (*p0++ == *p1++)
+	;
+      --p0, --p1;
+
+      /* Don't mistakenly count missing newline as part of prefix. */
+      if (ROBUST_OUTPUT_STYLE (output_style)
+	  && (buffer0 + n0 - filevec[0].missing_newline < p0)
+	     !=
+	     (buffer1 + n1 - filevec[1].missing_newline < p1))
+	--p0, --p1;
+    }
+
+  /* Now P0 and P1 point at the first nonmatching characters.  */
+
+  /* Skip back to last line-beginning in the prefix,
+     and then discard up to HORIZON_LINES lines from the prefix.  */
+  i = horizon_lines;
+  while (p0 != buffer0 && (p0[-1] != '\n' || i--))
+    --p0, --p1;
+
+  /* Record the prefix.  */
+  filevec[0].prefix_end = p0;
+  filevec[1].prefix_end = p1;
+
+  /* Find identical suffix.  */
+
+  /* P0 and P1 point beyond the last chars not yet compared.  */
+  p0 = buffer0 + n0;
+  p1 = buffer1 + n1;
+
+  if (! ROBUST_OUTPUT_STYLE (output_style)
+      || filevec[0].missing_newline == filevec[1].missing_newline)
+    {
+      end0 = p0;	/* Addr of last char in file 0.  */
+
+      /* Get value of P0 at which we should stop scanning backward:
+	 this is when either P0 or P1 points just past the last char
+	 of the identical prefix.  */
+      beg0 = filevec[0].prefix_end + (n0 < n1 ? 0 : n0 - n1);
+
+      /* Scan back until chars don't match or we reach that point.  */
+      while (p0 != beg0)
+	if (*--p0 != *--p1)
+	  {
+	    /* Point at the first char of the matching suffix.  */
+	    ++p0, ++p1;
+	    beg0 = p0;
+	    break;
+	  }
+
+      /* Are we at a line-beginning in both files?  If not, add the rest of
+	 this line to the main body.  Discard up to HORIZON_LINES lines from
+	 the identical suffix.  Also, discard one extra line,
+	 because shift_boundaries may need it.  */
+      i = horizon_lines + !((buffer0 == p0 || p0[-1] == '\n')
+			    &&
+			    (buffer1 == p1 || p1[-1] == '\n'));
+      while (i-- && p0 != end0)
+	while (*p0++ != '\n')
+	  ;
+
+      p1 += p0 - beg0;
+    }
+
+  /* Record the suffix.  */
+  filevec[0].suffix_begin = p0;
+  filevec[1].suffix_begin = p1;
+
+  /* Calculate number of lines of prefix to save.
+
+     prefix_count == 0 means save the whole prefix;
+     we need this with for options like -D that output the whole file.
+     We also need it for options like -F that output some preceding line;
+     at least we will need to find the last few lines,
+     but since we don't know how many, it's easiest to find them all.
+
+     Otherwise, prefix_count != 0.  Save just prefix_count lines at start
+     of the line buffer; they'll be moved to the proper location later.
+     Handle 1 more line than the context says (because we count 1 too many),
+     rounded up to the next power of 2 to speed index computation.  */
+
+  if (no_diff_means_no_output && ! function_regexp_list)
+    {
+      for (prefix_count = 1;  prefix_count < context + 1;  prefix_count *= 2)
+	;
+      prefix_mask = prefix_count - 1;
+      alloc_lines0
+	= prefix_count
+	  + GUESS_LINES (0, 0, p0 - filevec[0].prefix_end)
+	  + context;
+    }
+  else
+    {
+      prefix_count = 0;
+      prefix_mask = ~0;
+      alloc_lines0 = GUESS_LINES (0, 0, n0);
+    }
+
+  lines = 0;
+  linbuf0 = (char const **) xmalloc (alloc_lines0 * sizeof (*linbuf0));
+
+  /* If the prefix is needed, find the prefix lines.  */
+  if (! (no_diff_means_no_output
+	 && filevec[0].prefix_end == p0
+	 && filevec[1].prefix_end == p1))
+    {
+      p0 = buffer0;
+      end0 = filevec[0].prefix_end;
+      while (p0 != end0)
+	{
+	  int l = lines++ & prefix_mask;
+	  if (l == alloc_lines0)
+	    linbuf0 = (char const **) xrealloc (linbuf0, (alloc_lines0 *= 2)
+							 * sizeof(*linbuf0));
+	  linbuf0[l] = p0;
+	  while (*p0++ != '\n')
+	    ;
+	}
+    }
+  buffered_prefix = prefix_count && context < lines ? context : lines;
+
+  /* Allocate line buffer 1.  */
+  tem = prefix_count ? filevec[1].suffix_begin - buffer1 : n1;
+
+  alloc_lines1
+    = (buffered_prefix
+       + GUESS_LINES (lines, filevec[1].prefix_end - buffer1, tem)
+       + context);
+  linbuf1 = (char const **) xmalloc (alloc_lines1 * sizeof (*linbuf1));
+
+  if (buffered_prefix != lines)
+    {
+      /* Rotate prefix lines to proper location.  */
+      for (i = 0;  i < buffered_prefix;  i++)
+	linbuf1[i] = linbuf0[(lines - context + i) & prefix_mask];
+      for (i = 0;  i < buffered_prefix;  i++)
+	linbuf0[i] = linbuf1[i];
+    }
+
+  /* Initialize line buffer 1 from line buffer 0.  */
+  for (i = 0; i < buffered_prefix; i++)
+    linbuf1[i] = linbuf0[i] - buffer0 + buffer1;
+
+  /* Record the line buffer, adjusted so that
+     linbuf*[0] points at the first differing line.  */
+  filevec[0].linbuf = linbuf0 + buffered_prefix;
+  filevec[1].linbuf = linbuf1 + buffered_prefix;
+  filevec[0].linbuf_base = filevec[1].linbuf_base = - buffered_prefix;
+  filevec[0].alloc_lines = alloc_lines0 - buffered_prefix;
+  filevec[1].alloc_lines = alloc_lines1 - buffered_prefix;
+  filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
+}
+
+/* Largest primes less than some power of two, for nbuckets.  Values range
+   from useful to preposterous.  If one of these numbers isn't prime
+   after all, don't blame it on me, blame it on primes (6) . . . */
+static int const primes[] =
+{
+  509,
+  1021,
+  2039,
+  4093,
+  8191,
+  16381,
+  32749,
+#if 32767 < INT_MAX
+  65521,
+  131071,
+  262139,
+  524287,
+  1048573,
+  2097143,
+  4194301,
+  8388593,
+  16777213,
+  33554393,
+  67108859,			/* Preposterously large . . . */
+  134217689,
+  268435399,
+  536870909,
+  1073741789,
+  2147483647,
+#endif
+  0
+};
+
+/* Given a vector of two file_data objects, read the file associated
+   with each one, and build the table of equivalence classes.
+   Return 1 if either file appears to be a binary file.
+   If PRETEND_BINARY is nonzero, pretend they are binary regardless.  */
+
+int
+read_files (filevec, pretend_binary)
+     struct file_data filevec[];
+     int pretend_binary;
+{
+  int i;
+  int skip_test = always_text_flag | pretend_binary;
+  int appears_binary = pretend_binary | sip (&filevec[0], skip_test);
+
+  if (filevec[0].desc != filevec[1].desc)
+    appears_binary |= sip (&filevec[1], skip_test | appears_binary);
+  else
+    {
+      filevec[1].buffer = filevec[0].buffer;
+      filevec[1].bufsize = filevec[0].bufsize;
+      filevec[1].buffered_chars = filevec[0].buffered_chars;
+    }
+  if (appears_binary)
+    {
+#if HAVE_SETMODE
+      setmode (filevec[0].desc, O_BINARY);
+      setmode (filevec[1].desc, O_BINARY);
+#endif
+      return 1;
+    }
+
+  find_identical_ends (filevec);
+
+  equivs_alloc = filevec[0].alloc_lines + filevec[1].alloc_lines + 1;
+  equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass));
+  /* Equivalence class 0 is permanently safe for lines that were not
+     hashed.  Real equivalence classes start at 1. */
+  equivs_index = 1;
+
+  for (i = 0;  primes[i] < equivs_alloc / 3;  i++)
+    if (! primes[i])
+      abort ();
+  nbuckets = primes[i];
+
+  buckets = (int *) xmalloc ((nbuckets + 1) * sizeof (*buckets));
+  bzero (buckets++, (nbuckets + 1) * sizeof (*buckets));
+
+  for (i = 0; i < 2; i++)
+    find_and_hash_each_line (&filevec[i]);
+
+  filevec[0].equiv_max = filevec[1].equiv_max = equivs_index;
+
+  free (equivs);
+  free (buckets - 1);
+
+  return 0;
+}
diff --git a/gnu/usr.bin/diff/normal.c b/gnu/usr.bin/diff/normal.c
new file mode 100644
index 000000000000..4d9e23cb72a0
--- /dev/null
+++ b/gnu/usr.bin/diff/normal.c
@@ -0,0 +1,71 @@
+/* Normal-format output routines for GNU DIFF.
+   Copyright (C) 1988, 1989, 1993 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.  */
+
+
+#include "diff.h"
+
+static void print_normal_hunk PARAMS((struct change *));
+
+/* Print the edit-script SCRIPT as a normal diff.
+   INF points to an array of descriptions of the two files.  */
+
+void
+print_normal_script (script)
+     struct change *script;
+{
+  print_script (script, find_change, print_normal_hunk);
+}
+
+/* Print a hunk of a normal diff.
+   This is a contiguous portion of a complete edit script,
+   describing changes in consecutive lines.  */
+
+static void
+print_normal_hunk (hunk)
+     struct change *hunk;
+{
+  int first0, last0, first1, last1, deletes, inserts;
+  register int i;
+
+  /* Determine range of line numbers involved in each file.  */
+  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
+  if (!deletes && !inserts)
+    return;
+
+  begin_output ();
+
+  /* Print out the line number header for this hunk */
+  print_number_range (',', &files[0], first0, last0);
+  fprintf (outfile, "%c", change_letter (inserts, deletes));
+  print_number_range (',', &files[1], first1, last1);
+  fprintf (outfile, "\n");
+
+  /* Print the lines that the first file has.  */
+  if (deletes)
+    for (i = first0; i <= last0; i++)
+      print_1_line ("<", &files[0].linbuf[i]);
+
+  if (inserts && deletes)
+    fprintf (outfile, "---\n");
+
+  /* Print the lines that the second file has.  */
+  if (inserts)
+    for (i = first1; i <= last1; i++)
+      print_1_line (">", &files[1].linbuf[i]);
+}
diff --git a/gnu/usr.bin/diff/sdiff.c b/gnu/usr.bin/diff/sdiff.c
new file mode 100644
index 000000000000..b64f1d038365
--- /dev/null
+++ b/gnu/usr.bin/diff/sdiff.c
@@ -0,0 +1,1180 @@
+/* SDIFF -- interactive merge front end to diff
+   Copyright (C) 1992, 1993, 1994 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 "system.h"
+#include <stdio.h>
+#include <signal.h>
+#include "getopt.h"
+
+/* Size of chunks read from files which must be parsed into lines. */
+#define SDIFF_BUFSIZE ((size_t) 65536)
+
+/* Default name of the diff program */
+#ifndef DIFF_PROGRAM
+#define DIFF_PROGRAM "/usr/bin/diff"
+#endif
+
+/* Users' editor of nonchoice */
+#ifndef DEFAULT_EDITOR_PROGRAM
+#define DEFAULT_EDITOR_PROGRAM "ed"
+#endif
+
+extern char version_string[];
+static char const *program_name;
+static char const *diffbin = DIFF_PROGRAM;
+static char const *edbin = DEFAULT_EDITOR_PROGRAM;
+static char const **diffargv;
+
+static char *tmpname;
+static int volatile tmpmade;
+
+#if HAVE_FORK
+static pid_t volatile diffpid;
+#endif
+
+struct line_filter;
+
+static FILE *ck_fopen PARAMS((char const *, char const *));
+static RETSIGTYPE catchsig PARAMS((int));
+static VOID *xmalloc PARAMS((size_t));
+static char const *expand_name PARAMS((char *, int, char const *));
+static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
+static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
+static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
+static int skip_white PARAMS((void));
+static size_t ck_fread PARAMS((char *, size_t, FILE *));
+static size_t lf_refill PARAMS((struct line_filter *));
+static void checksigs PARAMS((void));
+static void ck_fclose PARAMS((FILE *));
+static void ck_fflush PARAMS((FILE *));
+static void ck_fwrite PARAMS((char const *, size_t, FILE *));
+static void cleanup PARAMS((void));
+static void diffarg PARAMS((char const *));
+static void execdiff PARAMS((void));
+static void exiterr PARAMS((void));
+static void fatal PARAMS((char const *));
+static void flush_line PARAMS((void));
+static void give_help PARAMS((void));
+static void lf_copy PARAMS((struct line_filter *, int, FILE *));
+static void lf_init PARAMS((struct line_filter *, FILE *));
+static void lf_skip PARAMS((struct line_filter *, int));
+static void perror_fatal PARAMS((char const *));
+static void trapsigs PARAMS((void));
+static void try_help PARAMS((char const *));
+static void untrapsig PARAMS((int));
+static void usage PARAMS((void));
+
+/* this lossage until the gnu libc conquers the universe */
+#if HAVE_TMPNAM
+#define private_tempnam() tmpnam ((char *) 0)
+#else
+#ifndef PVT_tmpdir
+#define PVT_tmpdir "/tmp"
+#endif
+#ifndef TMPDIR_ENV
+#define TMPDIR_ENV "TMPDIR"
+#endif
+static char *private_tempnam PARAMS((void));
+static int exists PARAMS((char const *));
+#endif
+static int diraccess PARAMS((char const *));
+
+/* 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 const longopts[] =
+{
+  {"ignore-blank-lines", 0, 0, 'B'},
+  {"speed-large-files", 0, 0, 'H'},
+  {"ignore-matching-lines", 1, 0, 'I'},
+  {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
+  {"text", 0, 0, 'a'},
+  {"ignore-space-change", 0, 0, 'b'},
+  {"minimal", 0, 0, 'd'},
+  {"ignore-case", 0, 0, 'i'},
+  {"left-column", 0, 0, 'l'},
+  {"output", 1, 0, 'o'},
+  {"suppress-common-lines", 0, 0, 's'},
+  {"expand-tabs", 0, 0, 't'},
+  {"width", 1, 0, 'w'},
+  {"version", 0, 0, 'v'},
+  {"help", 0, 0, 129},
+  {0, 0, 0, 0}
+};
+
+static void
+try_help (reason)
+     char const *reason;
+{
+  if (reason)
+    fprintf (stderr, "%s: %s\n", program_name, reason);
+  fprintf (stderr, "%s: Try `%s --help' for more information.\n",
+	   program_name, program_name);
+  exit (2);
+}
+
+static void
+usage ()
+{
+  printf ("Usage: %s [OPTIONS]... FILE1 FILE2\n\n", program_name);
+  printf ("%s", "\
+  -o FILE  --output=FILE  Operate interactively, sending output to FILE.\n\n");
+  printf ("%s", "\
+  -i  --ignore-case  Consider upper- and lower-case to be the same.\n\
+  -W  --ignore-all-space  Ignore all white space.\n\
+  -b  --ignore-space-change  Ignore changes in the amount of white space.\n\
+  -B  --ignore-blank-lines  Ignore changes whose lines are all blank.\n\
+  -I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE.\n\
+  -a  --text  Treat all files as text.\n\n");
+  printf ("%s", "\
+  -w NUM  --width=NUM  Output at most NUM (default 130) characters per line.\n\
+  -l  --left-column  Output only the left column of common lines.\n\
+  -s  --suppress-common-lines  Do not output common lines.\n\n");
+  printf ("\
+  -t  --expand-tabs  Expand tabs to spaces in output.\n\n");
+  printf ("%s", "\
+  -d  --minimal  Try hard to find a smaller set of changes.\n\
+  -H  --speed-large-files  Assume large files and many scattered small changes.\n\n");
+ printf ("%s", "\
+  -v  --version  Output version info.\n\
+  --help  Output this help.\n\n\
+If FILE1 or FILE2 is `-', read standard input.\n");
+}
+
+static void
+cleanup ()
+{
+#if HAVE_FORK
+  if (0 < diffpid)
+    kill (diffpid, SIGPIPE);
+#endif
+  if (tmpmade)
+    unlink (tmpname);
+}
+
+static void
+exiterr ()
+{
+  cleanup ();
+  untrapsig (0);
+  checksigs ();
+  exit (2);
+}
+
+static void
+fatal (msg)
+     char const *msg;
+{
+  fprintf (stderr, "%s: %s\n", program_name, msg);
+  exiterr ();
+}
+
+static void
+perror_fatal (msg)
+     char const *msg;
+{
+  int e = errno;
+  checksigs ();
+  fprintf (stderr, "%s: ", program_name);
+  errno = e;
+  perror (msg);
+  exiterr ();
+}
+
+
+/* malloc freely or DIE! */
+static VOID *
+xmalloc (size)
+     size_t size;
+{
+  VOID *r = (VOID *) malloc (size);
+  if (!r)
+    fatal ("memory exhausted");
+  return r;
+}
+
+static FILE *
+ck_fopen (fname, type)
+     char const *fname, *type;
+{
+  FILE *r = fopen (fname, type);
+  if (!r)
+    perror_fatal (fname);
+  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 const *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");
+}
+
+static char const *
+expand_name (name, is_dir, other_name)
+     char *name;
+     int is_dir;
+     char const *other_name;
+{
+  if (strcmp (name, "-") == 0)
+    fatal ("cannot interactively merge standard input");
+  if (!is_dir)
+    return name;
+  else
+    {
+      /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
+      char const *p = filename_lastdirchar (other_name);
+      char const *base = p ? p+1 : other_name;
+      size_t namelen = strlen (name), baselen = strlen (base);
+      char *r = xmalloc (namelen + baselen + 2);
+      memcpy (r, name, namelen);
+      r[namelen] = '/';
+      memcpy (r + namelen + 1, base, 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';
+  checksigs ();
+  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 = (char *) 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 = (char *) 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 = (char *) memchr (start, '\n', lf->buflim + 1 - start);
+      size_t s = next - start;
+      if (bufsize <= s)
+	return 0;
+      memcpy (buffer, start, 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;
+  char *editor;
+  char *differ;
+
+  initialize_main (&argc, &argv);
+  program_name = argv[0];
+
+  editor = getenv ("EDITOR");
+  if (editor)
+    edbin = editor;
+  differ = getenv ("DIFF");
+  if (differ)
+    diffbin = differ;
+
+  diffarg ("diff");
+
+  /* parse command line args */
+  while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 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':
+	  printf ("sdiff - GNU diffutils version %s\n", version_string);
+	  exit (0);
+
+	case 'w':
+	  diffarg ("-W");
+	  diffarg (optarg);
+	  break;
+
+	case 'W':
+	  diffarg ("-w");
+	  break;
+
+	case 129:
+	  usage ();
+	  if (ferror (stdout) || fclose (stdout) != 0)
+	    fatal ("write error");
+	  exit (0);
+
+	default:
+	  try_help (0);
+	}
+    }
+
+  if (argc - optind != 2)
+    try_help (argc - optind < 2 ? "missing operand" : "extra operand");
+
+  if (! out_file)
+    {
+      /* easy case: diff does everything for us */
+      if (suppress_common_flag)
+	diffarg ("--suppress-common-lines");
+      diffarg ("-y");
+      diffarg ("--");
+      diffarg (argv[optind]);
+      diffarg (argv[optind + 1]);
+      diffarg (0);
+      execdiff ();
+    }
+  else
+    {
+      FILE *left, *right, *out, *diffout;
+      int interact_ok;
+      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");
+
+      diffarg ("--sdiff-merge-assist");
+      diffarg ("--");
+      diffarg (argv[optind]);
+      diffarg (argv[optind + 1]);
+      diffarg (0);
+
+      trapsigs ();
+
+#if ! HAVE_FORK
+      {
+	size_t cmdsize = 1;
+	char *p, *command;
+	int i;
+
+	for (i = 0;  diffargv[i];  i++)
+	  cmdsize += 4 * strlen (diffargv[i]) + 3;
+	command = p = xmalloc (cmdsize);
+	for (i = 0;  diffargv[i];  i++)
+	  {
+	    char const *a = diffargv[i];
+	    SYSTEM_QUOTE_ARG (p, a);
+	    *p++ = ' ';
+	  }
+	p[-1] = '\0';
+	diffout = popen (command, "r");
+	if (!diffout)
+	  perror_fatal (command);
+	free (command);
+      }
+#else /* HAVE_FORK */
+      {
+	int diff_fds[2];
+
+	if (pipe (diff_fds) != 0)
+	  perror_fatal ("pipe");
+
+	diffpid = vfork ();
+	if (diffpid < 0)
+	  perror_fatal ("fork failed");
+	if (!diffpid)
+	  {
+	    signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
+	    signal (SIGPIPE, SIG_DFL);
+
+	    close (diff_fds[0]);
+	    if (diff_fds[1] != STDOUT_FILENO)
+	      {
+		dup2 (diff_fds[1], STDOUT_FILENO);
+		close (diff_fds[1]);
+	      }
+
+	    execdiff ();
+	  }
+
+	close (diff_fds[1]);
+	diffout = fdopen (diff_fds[0], "r");
+	if (!diffout)
+	  perror_fatal ("fdopen");
+      }
+#endif /* HAVE_FORK */
+
+      lf_init (&diff_filt, diffout);
+      lf_init (&lfilt, left);
+      lf_init (&rfilt, right);
+
+      interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
+
+      ck_fclose (left);
+      ck_fclose (right);
+      ck_fclose (out);
+
+      {
+	int wstatus;
+
+#if ! HAVE_FORK
+	wstatus = pclose (diffout);
+#else
+	ck_fclose (diffout);
+	while (waitpid (diffpid, &wstatus, 0) < 0)
+	  if (errno == EINTR)
+	    checksigs ();
+	  else
+	    perror_fatal ("wait failed");
+	diffpid = 0;
+#endif
+
+	if (tmpmade)
+	  {
+	    unlink (tmpname);
+	    tmpmade = 0;
+	  }
+
+	if (! interact_ok)
+	  exiterr ();
+
+	if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
+	  fatal ("Subsidiary diff failed");
+
+	untrapsig (0);
+	checksigs ();
+	exit (WEXITSTATUS (wstatus));
+      }
+    }
+  return 0;			/* Fool -Wall . . . */
+}
+
+static void
+diffarg (a)
+     char const *a;
+{
+  static unsigned diffargs, diffargsmax;
+
+  if (diffargs == diffargsmax)
+    {
+      if (! diffargsmax)
+	{
+	  diffargv = (char const **) xmalloc (sizeof (char));
+	  diffargsmax = 8;
+	}
+      diffargsmax *= 2;
+      diffargv = (char const **) realloc (diffargv,
+					  diffargsmax * sizeof (char const *));
+      if (! diffargv)
+	fatal ("out of memory");
+    }
+  diffargv[diffargs++] = a;
+}
+
+static void
+execdiff ()
+{
+  execvp (diffbin, (char **) diffargv);
+  write (STDERR_FILENO, diffbin, strlen (diffbin));
+  write (STDERR_FILENO, ": not found\n", 12);
+  _exit (2);
+}
+
+
+
+
+/* Signal handling */
+
+#define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
+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
+};
+
+/* Prefer `sigaction' if it is available, since `signal' can lose signals.  */
+#if HAVE_SIGACTION
+static struct sigaction initial_action[NUM_SIGS];
+#define initial_handler(i) (initial_action[i].sa_handler)
+#else
+static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
+#define initial_handler(i) (initial_action[i])
+#endif
+
+static int volatile ignore_SIGINT;
+static int volatile signal_received;
+static int sigs_trapped;
+
+static RETSIGTYPE
+catchsig (s)
+     int s;
+{
+#if ! HAVE_SIGACTION
+  signal (s, SIG_IGN);
+#endif
+  if (! (s == SIGINT && ignore_SIGINT))
+    signal_received = s;
+}
+
+static void
+trapsigs ()
+{
+  int i;
+
+#if HAVE_SIGACTION
+  struct sigaction catchaction;
+  bzero (&catchaction, sizeof (catchaction));
+  catchaction.sa_handler = catchsig;
+#ifdef SA_INTERRUPT
+  /* Non-Posix BSD-style systems like SunOS 4.1.x need this
+     so that `read' calls are interrupted properly.  */
+  catchaction.sa_flags = SA_INTERRUPT;
+#endif
+  sigemptyset (&catchaction.sa_mask);
+  for (i = 0;  i < NUM_SIGS;  i++)
+    sigaddset (&catchaction.sa_mask, sigs[i]);
+  for (i = 0;  i < NUM_SIGS;  i++)
+    {
+      sigaction (sigs[i], 0, &initial_action[i]);
+      if (initial_handler (i) != SIG_IGN
+	  && sigaction (sigs[i], &catchaction, 0) != 0)
+	fatal ("signal error");
+    }
+#else /* ! HAVE_SIGACTION */
+  for (i = 0;  i < NUM_SIGS;  i++)
+    {
+      initial_action[i] = signal (sigs[i], SIG_IGN);
+      if (initial_handler (i) != SIG_IGN
+	  && signal (sigs[i], catchsig) != SIG_IGN)
+	fatal ("signal error");
+    }
+#endif /* ! HAVE_SIGACTION */
+
+#if !defined(SIGCHLD) && defined(SIGCLD)
+#define SIGCHLD SIGCLD
+#endif
+#ifdef SIGCHLD
+  /* System V fork+wait does not work if SIGCHLD is ignored.  */
+  signal (SIGCHLD, SIG_DFL);
+#endif
+
+  sigs_trapped = 1;
+}
+
+/* Untrap signal S, or all trapped signals if S is zero.  */
+static void
+untrapsig (s)
+     int s;
+{
+  int i;
+
+  if (sigs_trapped)
+    for (i = 0;  i < NUM_SIGS;  i++)
+      if ((!s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
+#if HAVE_SIGACTION
+	  sigaction (sigs[i], &initial_action[i], 0);
+#else
+	  signal (sigs[i], initial_action[i]);
+#endif
+}
+
+/* Exit if a signal has been received.  */
+static void
+checksigs ()
+{
+  int s = signal_received;
+  if (s)
+    {
+      cleanup ();
+
+      /* Yield an exit status indicating that a signal was received.  */
+      untrapsig (s);
+      kill (getpid (), s);
+
+      /* That didn't work, so exit with error status.  */
+      exit (2);
+    }
+}
+
+
+
+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;
+  for (;;)
+    {
+      c = getchar ();
+      if (!ISSPACE (c) || c == '\n')
+	break;
+      checksigs ();
+    }
+  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;
+
+      cmd1 = 0; /* Pacify `gcc -W'.  */
+
+      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:
+	      flush_line ();
+	      /* falls through */
+	    case '\n':
+	      give_help ();
+	      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 ()))
+	    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);
+
+	    {
+	      int wstatus;
+#if ! HAVE_FORK
+	      char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2);
+	      sprintf (command, "%s %s", edbin, tmpname);
+	      wstatus = system (command);
+	      free (command);
+#else /* HAVE_FORK */
+	      pid_t pid;
+
+	      ignore_SIGINT = 1;
+	      checksigs ();
+
+	      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 (STDERR_FILENO, edbin, strlen (edbin));
+		  write (STDERR_FILENO, ": not found\n", 12);
+		  _exit (1);
+		}
+
+	      if (pid < 0)
+		perror_fatal ("fork failed");
+
+	      while (waitpid (pid, &wstatus, 0) < 0)
+		if (errno == EINTR)
+		  checksigs ();
+		else
+		  perror_fatal ("wait failed");
+
+	      ignore_SIGINT = 0;
+#endif /* HAVE_FORK */
+
+	      if (wstatus != 0)
+		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 = xmalloc (SDIFF_BUFSIZE);
+	      size_t size;
+
+	      while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
+		{
+		  checksigs ();
+		  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 commands.  */
+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;
+
+      checksigs ();
+
+      switch (diff_help[0])
+	{
+	case ' ':
+	  puts (diff_help + 1);
+	  break;
+	case 'i':
+	  {
+	    int lenl = atoi (diff_help + 1), lenr, lenmax;
+	    char *p = strchr (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 = strchr (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 existing directory.  */
+static int
+diraccess (dir)
+     char const *dir;
+{
+  struct stat buf;
+  return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
+}
+
+#if ! HAVE_TMPNAM
+
+/* Return zero if we know that FILE does not exist.  */
+static int
+exists (file)
+     char const *file;
+{
+  struct stat buf;
+  return stat (file, &buf) == 0 || errno != ENOENT;
+}
+
+/* These are the characters used in temporary filenames.  */
+static char const letters[] =
+  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/* Generate a temporary filename and return it (in a newly allocated buffer).
+   Use the prefix "dif" as in tempnam.
+   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'.  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
+   return 0.  */
+static char *
+private_tempnam ()
+{
+  char const *dir = getenv (TMPDIR_ENV);
+  static char const tmpdir[] = PVT_tmpdir;
+  size_t index;
+  char *buf;
+  pid_t pid = getpid ();
+  size_t dlen;
+
+  if (!dir)
+    dir = tmpdir;
+
+  dlen = strlen (dir);
+
+  /* Remove trailing slashes from the directory name.  */
+  while (dlen && dir[dlen - 1] == '/')
+    --dlen;
+
+  buf = xmalloc (dlen + 1 + 3 + 5 + 1 + 3 + 1);
+
+  sprintf (buf, "%.*s/.", (int) dlen, dir);
+  if (diraccess (buf))
+    {
+      for (index = 0;
+	   index < ((sizeof (letters) - 1) * (sizeof (letters) - 1)
+		    * (sizeof (letters) - 1));
+	   ++index)
+	{
+	  /* Construct a file name and see if it already exists.
+
+	     We use a single counter in INDEX to cycle each of three
+	     character positions through each of 62 possible letters.  */
+
+	  sprintf (buf, "%.*s/dif%.5lu.%c%c%c", (int) dlen, dir,
+		   (unsigned long) pid % 100000,
+		   letters[index % (sizeof (letters) - 1)],
+		   letters[(index / (sizeof (letters) - 1))
+			   % (sizeof (letters) - 1)],
+		   letters[index / ((sizeof (letters) - 1) *
+				     (sizeof (letters) - 1))]);
+
+	  if (!exists (buf))
+	    return buf;
+	}
+      errno = EEXIST;
+    }
+
+  /* Don't free buf; `free' might change errno.  We'll exit soon anyway.  */
+  return 0;
+}
+
+#endif /* ! HAVE_TMPNAM */
diff --git a/gnu/usr.bin/diff/side.c b/gnu/usr.bin/diff/side.c
new file mode 100644
index 000000000000..a150b5e705fc
--- /dev/null
+++ b/gnu/usr.bin/diff/side.c
@@ -0,0 +1,284 @@
+/* sdiff-format output routines for GNU DIFF.
+   Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU DIFF.
+
+GNU DIFF is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY.  No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing.  Refer to the GNU DIFF General Public
+License for full details.
+
+Everyone is granted permission to copy, modify and redistribute
+GNU DIFF, but only under the conditions described in the
+GNU DIFF General Public License.   A copy of this license is
+supposed to have been given to you along with GNU DIFF so you
+can know your rights and responsibilities.  It should be in a
+file named COPYING.  Among other things, the copyright notice
+and this notice must be preserved on all copies.  */
+
+
+#include "diff.h"
+
+static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned));
+static unsigned tab_from_to PARAMS((unsigned, unsigned));
+static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *));
+static void print_sdiff_common_lines PARAMS((int, int));
+static void print_sdiff_hunk PARAMS((struct change *));
+
+/* Next line number to be printed in the two input files.  */
+static int next0, next1;
+
+/* Print the edit-script SCRIPT as a sdiff style output.  */
+
+void
+print_sdiff_script (script)
+     struct change *script;
+{
+  begin_output ();
+
+  next0 = next1 = - files[0].prefix_lines;
+  print_script (script, find_change, print_sdiff_hunk);
+
+  print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
+}
+
+/* Tab from column FROM to column TO, where FROM <= TO.  Yield TO.  */
+
+static unsigned
+tab_from_to (from, to)
+     unsigned from, to;
+{
+  FILE *out = outfile;
+  unsigned tab;
+
+  if (! tab_expand_flag)
+    for (tab = from + TAB_WIDTH - from % TAB_WIDTH;  tab <= to;  tab += TAB_WIDTH)
+      {
+	putc ('\t', out);
+	from = tab;
+      }
+  while (from++ < to)
+    putc (' ', out);
+  return to;
+}
+
+/*
+ * Print the text for half an sdiff line.  This means truncate to width
+ * observing tabs, and trim a trailing newline.  Returns the last column
+ * written (not the number of chars).
+ */
+static unsigned
+print_half_line (line, indent, out_bound)
+     char const * const *line;
+     unsigned indent, out_bound;
+{
+  FILE *out = outfile;
+  register unsigned in_position = 0, out_position = 0;
+  register char const
+	*text_pointer = line[0],
+	*text_limit = line[1];
+
+  while (text_pointer < text_limit)
+    {
+      register unsigned char c = *text_pointer++;
+
+      switch (c)
+	{
+	case '\t':
+	  {
+	    unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH;
+	    if (in_position == out_position)
+	      {
+		unsigned tabstop = out_position + spaces;
+		if (tab_expand_flag)
+		  {
+		    if (out_bound < tabstop)
+		      tabstop = out_bound;
+		    for (;  out_position < tabstop;  out_position++)
+		      putc (' ', out);
+		  }
+		else
+		  if (tabstop < out_bound)
+		    {
+		      out_position = tabstop;
+		      putc (c, out);
+		    }
+	      }
+	    in_position += spaces;
+	  }
+	  break;
+
+	case '\r':
+	  {
+	    putc (c, out);
+	    tab_from_to (0, indent);
+	    in_position = out_position = 0;
+	  }
+	  break;
+
+	case '\b':
+	  if (in_position != 0 && --in_position < out_bound)
+	    if (out_position <= in_position)
+	      /* Add spaces to make up for suppressed tab past out_bound.  */
+	      for (;  out_position < in_position;  out_position++)
+		putc (' ', out);
+	    else
+	      {
+		out_position = in_position;
+		putc (c, out);
+	      }
+	  break;
+
+	case '\f':
+	case '\v':
+	control_char:
+	  if (in_position < out_bound)
+	    putc (c, out);
+	  break;
+
+	default:
+	  if (! ISPRINT (c))
+	    goto control_char;
+	  /* falls through */
+	case ' ':
+	  if (in_position++ < out_bound)
+	    {
+	      out_position = in_position;
+	      putc (c, out);
+	    }
+	  break;
+
+	case '\n':
+	  return out_position;
+	}
+    }
+
+  return out_position;
+}
+
+/*
+ * Print side by side lines with a separator in the middle.
+ * 0 parameters are taken to indicate white space text.
+ * Blank lines that can easily be caught are reduced to a single newline.
+ */
+
+static void
+print_1sdiff_line (left, sep, right)
+     char const * const *left;
+     int sep;
+     char const * const *right;
+{
+  FILE *out = outfile;
+  unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset;
+  unsigned col = 0;
+  int put_newline = 0;
+
+  if (left)
+    {
+      if (left[1][-1] == '\n')
+	put_newline = 1;
+      col = print_half_line (left, 0, hw);
+    }
+
+  if (sep != ' ')
+    {
+      col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
+      if (sep == '|' && put_newline != (right[1][-1] == '\n'))
+	sep = put_newline ? '/' : '\\';
+      putc (sep, out);
+    }
+
+  if (right)
+    {
+      if (right[1][-1] == '\n')
+	put_newline = 1;
+      if (**right != '\n')
+	{
+	  col = tab_from_to (col, c2o);
+	  print_half_line (right, col, hw);
+	}
+    }
+
+  if (put_newline)
+    putc ('\n', out);
+}
+
+/* Print lines common to both files in side-by-side format.  */
+static void
+print_sdiff_common_lines (limit0, limit1)
+     int limit0, limit1;
+{
+  int i0 = next0, i1 = next1;
+
+  if (! sdiff_skip_common_lines  &&  (i0 != limit0 || i1 != limit1))
+    {
+      if (sdiff_help_sdiff)
+	fprintf (outfile, "i%d,%d\n", limit0 - i0, limit1 - i1);
+
+      if (! sdiff_left_only)
+	{
+	  while (i0 != limit0 && i1 != limit1)
+	    print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]);
+	  while (i1 != limit1)
+	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
+	}
+      while (i0 != limit0)
+	print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
+    }
+
+  next0 = limit0;
+  next1 = limit1;
+}
+
+/* Print a hunk of an sdiff diff.
+   This is a contiguous portion of a complete edit script,
+   describing changes in consecutive lines.  */
+
+static void
+print_sdiff_hunk (hunk)
+     struct change *hunk;
+{
+  int first0, last0, first1, last1, deletes, inserts;
+  register int i, j;
+
+  /* Determine range of line numbers involved in each file.  */
+  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
+  if (!deletes && !inserts)
+    return;
+
+  /* Print out lines up to this change.  */
+  print_sdiff_common_lines (first0, first1);
+
+  if (sdiff_help_sdiff)
+    fprintf (outfile, "c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1);
+
+  /* Print ``xxx  |  xxx '' lines */
+  if (inserts && deletes)
+    {
+      for (i = first0, j = first1;  i <= last0 && j <= last1; ++i, ++j)
+	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
+      deletes = i <= last0;
+      inserts = j <= last1;
+      next0 = first0 = i;
+      next1 = first1 = j;
+    }
+
+
+  /* Print ``     >  xxx '' lines */
+  if (inserts)
+    {
+      for (j = first1; j <= last1; ++j)
+	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
+      next1 = j;
+    }
+
+  /* Print ``xxx  <     '' lines */
+  if (deletes)
+    {
+      for (i = first0; i <= last0; ++i)
+	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
+      next0 = i;
+    }
+}
diff --git a/gnu/usr.bin/diff/system.h b/gnu/usr.bin/diff/system.h
new file mode 100644
index 000000000000..d60af60005f5
--- /dev/null
+++ b/gnu/usr.bin/diff/system.h
@@ -0,0 +1,267 @@
+/* System dependent declarations.
+   Copyright (C) 1988, 1989, 1992, 1993, 1994 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.  */
+
+/* We must define `volatile' and `const' first (the latter inside config.h),
+   so that they're used consistently in all system includes.  */
+#if !__STDC__
+#ifndef volatile
+#define volatile
+#endif
+#endif
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if __STDC__
+#define PARAMS(args) args
+#define VOID void
+#else
+#define PARAMS(args) ()
+#define VOID char
+#endif
+
+#if STAT_MACROS_BROKEN
+#undef S_ISBLK
+#undef S_ISCHR
+#undef S_ISDIR
+#undef S_ISFIFO
+#undef S_ISREG
+#undef S_ISSOCK
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFFIFO)
+#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#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_TIME_H
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#endif
+
+#if !HAVE_DUP2
+#define dup2(f,t)	(close (t),  fcntl (f,F_DUPFD,t))
+#endif
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#ifndef STAT_BLOCKSIZE
+#if HAVE_ST_BLKSIZE
+#define STAT_BLOCKSIZE(s) (s).st_blksize
+#else
+#define STAT_BLOCKSIZE(s) (8 * 1024)
+#endif
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) ((dirent)->d_namlen)
+# if HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#if HAVE_VFORK_H
+#include <vfork.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
+#endif
+#ifndef getenv
+char *getenv ();
+#endif
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#if STDC_HEADERS || HAVE_STRING_H
+# include <string.h>
+# ifndef bzero
+#  define bzero(s, n) memset (s, 0, n)
+# endif
+#else
+# if !HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCHR
+#  define memcmp(s1, s2, n) bcmp (s1, s2, n)
+#  define memcpy(d, s, n) bcopy (s, d, n)
+void *memchr ();
+# endif
+#endif
+
+#include <ctype.h>
+/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given
+   as an argument to <ctype.h> macros like `isspace'.  */
+#if STDC_HEADERS
+#define CTYPE_DOMAIN(c) 1
+#else
+#define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)
+#endif
+#ifndef ISPRINT
+#define ISPRINT(c) (CTYPE_DOMAIN (c) && isprint (c))
+#endif
+#ifndef ISSPACE
+#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c))
+#endif
+#ifndef ISUPPER
+#define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c))
+#endif
+
+#ifndef ISDIGIT
+#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
+#endif
+
+#include <errno.h>
+#if !STDC_HEADERS
+extern int errno;
+#endif
+
+#ifdef min
+#undef min
+#endif
+#ifdef max
+#undef max
+#endif
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+
+/* This section contains Posix-compliant defaults for macros
+   that are meant to be overridden by hand in config.h as needed.  */
+
+#ifndef filename_cmp
+#define filename_cmp(a, b) strcmp (a, b)
+#endif
+
+#ifndef filename_lastdirchar
+#define filename_lastdirchar(filename) strrchr (filename, '/')
+#endif
+
+#ifndef HAVE_FORK
+#define HAVE_FORK 1
+#endif
+
+#ifndef HAVE_SETMODE
+#define HAVE_SETMODE 0
+#endif
+
+#ifndef initialize_main
+#define initialize_main(argcp, argvp)
+#endif
+
+/* Do struct stat *S, *T describe the same file?  Answer -1 if unknown.  */
+#ifndef same_file
+#define same_file(s,t) ((s)->st_ino==(t)->st_ino && (s)->st_dev==(t)->st_dev)
+#endif
+
+/* Place into Q a quoted version of A suitable for `popen' or `system',
+   incrementing Q and junking A.
+   Do not increment Q by more than 4 * strlen (A) + 2.  */
+#ifndef SYSTEM_QUOTE_ARG
+#define SYSTEM_QUOTE_ARG(q, a) \
+  { \
+    *(q)++ = '\''; \
+    for (;  *(a);  *(q)++ = *(a)++) \
+      if (*(a) == '\'') \
+	{ \
+	  *(q)++ = '\''; \
+	  *(q)++ = '\\'; \
+	  *(q)++ = '\''; \
+	} \
+    *(q)++ = '\''; \
+  }
+#endif
diff --git a/gnu/usr.bin/diff/util.c b/gnu/usr.bin/diff/util.c
new file mode 100644
index 000000000000..bbc3bff91a4b
--- /dev/null
+++ b/gnu/usr.bin/diff/util.c
@@ -0,0 +1,754 @@
+/* Support routines for GNU DIFF.
+   Copyright (C) 1988, 1989, 1992, 1993, 1994 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.  */
+
+#include "diff.h"
+
+#ifndef PR_PROGRAM
+#define PR_PROGRAM "/bin/pr"
+#endif
+
+/* Queue up one-line messages to be printed at the end,
+   when -l is specified.  Each message is recorded with a `struct msg'.  */
+
+struct msg
+{
+  struct msg *next;
+  char const *format;
+  char const *arg1;
+  char const *arg2;
+  char const *arg3;
+  char const *arg4;
+};
+
+/* Head of the chain of queues messages.  */
+
+static struct msg *msg_chain;
+
+/* Tail of the chain of queues messages.  */
+
+static struct msg **msg_chain_end = &msg_chain;
+
+/* Use when a system call returns non-zero status.
+   TEXT should normally be the file name.  */
+
+void
+perror_with_name (text)
+     char const *text;
+{
+  int e = errno;
+  fprintf (stderr, "%s: ", program_name);
+  errno = e;
+  perror (text);
+}
+
+/* Use when a system call returns non-zero status and that is fatal.  */
+
+void
+pfatal_with_name (text)
+     char const *text;
+{
+  int e = errno;
+  print_message_queue ();
+  fprintf (stderr, "%s: ", program_name);
+  errno = e;
+  perror (text);
+  exit (2);
+}
+
+/* Print an error message from the format-string FORMAT
+   with args ARG1 and ARG2.  */
+
+void
+error (format, arg, arg1)
+     char const *format, *arg, *arg1;
+{
+  fprintf (stderr, "%s: ", program_name);
+  fprintf (stderr, format, arg, arg1);
+  fprintf (stderr, "\n");
+}
+
+/* Print an error message containing the string TEXT, then exit.  */
+
+void
+fatal (m)
+     char const *m;
+{
+  print_message_queue ();
+  error ("%s", m, 0);
+  exit (2);
+}
+
+/* Like printf, except if -l in effect then save the message and print later.
+   This is used for things like "binary files differ" and "Only in ...".  */
+
+void
+message (format, arg1, arg2)
+     char const *format, *arg1, *arg2;
+{
+  message5 (format, arg1, arg2, 0, 0);
+}
+
+void
+message5 (format, arg1, arg2, arg3, arg4)
+     char const *format, *arg1, *arg2, *arg3, *arg4;
+{
+  if (paginate_flag)
+    {
+      struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
+      new->format = format;
+      new->arg1 = concat (arg1, "", "");
+      new->arg2 = concat (arg2, "", "");
+      new->arg3 = arg3 ? concat (arg3, "", "") : 0;
+      new->arg4 = arg4 ? concat (arg4, "", "") : 0;
+      new->next = 0;
+      *msg_chain_end = new;
+      msg_chain_end = &new->next;
+    }
+  else
+    {
+      if (sdiff_help_sdiff)
+	putchar (' ');
+      printf (format, arg1, arg2, arg3, arg4);
+    }
+}
+
+/* Output all the messages that were saved up by calls to `message'.  */
+
+void
+print_message_queue ()
+{
+  struct msg *m;
+
+  for (m = msg_chain; m; m = m->next)
+    printf (m->format, m->arg1, m->arg2, m->arg3, m->arg4);
+}
+
+/* Call before outputting the results of comparing files NAME0 and NAME1
+   to set up OUTFILE, the stdio stream for the output to go to.
+
+   Usually, OUTFILE is just stdout.  But when -l was specified
+   we fork off a `pr' and make OUTFILE a pipe to it.
+   `pr' then outputs to our stdout.  */
+
+static char const *current_name0;
+static char const *current_name1;
+static int current_depth;
+
+void
+setup_output (name0, name1, depth)
+     char const *name0, *name1;
+     int depth;
+{
+  current_name0 = name0;
+  current_name1 = name1;
+  current_depth = depth;
+  outfile = 0;
+}
+
+#if HAVE_FORK
+static pid_t pr_pid;
+#endif
+
+void
+begin_output ()
+{
+  char *name;
+
+  if (outfile != 0)
+    return;
+
+  /* Construct the header of this piece of diff.  */
+  name = xmalloc (strlen (current_name0) + strlen (current_name1)
+		  + strlen (switch_string) + 7);
+  /* Posix.2 section 4.17.6.1.1 specifies this format.  But there is a
+     bug in the first printing (IEEE Std 1003.2-1992 p 251 l 3304):
+     it says that we must print only the last component of the pathnames.
+     This requirement is silly and does not match historical practice.  */
+  sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
+
+  if (paginate_flag)
+    {
+      /* Make OUTFILE a pipe to a subsidiary `pr'.  */
+
+#if HAVE_FORK
+      int pipes[2];
+
+      if (pipe (pipes) != 0)
+	pfatal_with_name ("pipe");
+
+      fflush (stdout);
+
+      pr_pid = vfork ();
+      if (pr_pid < 0)
+	pfatal_with_name ("vfork");
+
+      if (pr_pid == 0)
+	{
+	  close (pipes[1]);
+	  if (pipes[0] != STDIN_FILENO)
+	    {
+	      if (dup2 (pipes[0], STDIN_FILENO) < 0)
+		pfatal_with_name ("dup2");
+	      close (pipes[0]);
+	    }
+
+	  execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, 0);
+	  pfatal_with_name (PR_PROGRAM);
+	}
+      else
+	{
+	  close (pipes[0]);
+	  outfile = fdopen (pipes[1], "w");
+	  if (!outfile)
+	    pfatal_with_name ("fdopen");
+	}
+#else /* ! HAVE_FORK */
+      char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10);
+      char *p;
+      char const *a = name;
+      sprintf (command, "%s -f -h ", PR_PROGRAM);
+      p = command + strlen (command);
+      SYSTEM_QUOTE_ARG (p, a);
+      *p = 0;
+      outfile = popen (command, "w");
+      if (!outfile)
+	pfatal_with_name (command);
+      free (command);
+#endif /* ! HAVE_FORK */
+    }
+  else
+    {
+
+      /* If -l was not specified, output the diff straight to `stdout'.  */
+
+      outfile = stdout;
+
+      /* If handling multiple files (because scanning a directory),
+	 print which files the following output is about.  */
+      if (current_depth > 0)
+	printf ("%s\n", name);
+    }
+
+  free (name);
+
+  /* A special header is needed at the beginning of context output.  */
+  switch (output_style)
+    {
+    case OUTPUT_CONTEXT:
+      print_context_header (files, 0);
+      break;
+
+    case OUTPUT_UNIFIED:
+      print_context_header (files, 1);
+      break;
+
+    default:
+      break;
+    }
+}
+
+/* Call after the end of output of diffs for one file.
+   Close OUTFILE and get rid of the `pr' subfork.  */
+
+void
+finish_output ()
+{
+  if (outfile != 0 && outfile != stdout)
+    {
+      int wstatus;
+      if (ferror (outfile))
+	fatal ("write error");
+#if ! HAVE_FORK
+      wstatus = pclose (outfile);
+#else /* HAVE_FORK */
+      if (fclose (outfile) != 0)
+	pfatal_with_name ("write error");
+      if (waitpid (pr_pid, &wstatus, 0) < 0)
+	pfatal_with_name ("waitpid");
+#endif /* HAVE_FORK */
+      if (wstatus != 0)
+	fatal ("subsidiary pr failed");
+    }
+
+  outfile = 0;
+}
+
+/* Compare two lines (typically one from each input file)
+   according to the command line options.
+   For efficiency, this is invoked only when the lines do not match exactly
+   but an option like -i might cause us to ignore the difference.
+   Return nonzero if the lines differ.  */
+
+int
+line_cmp (s1, s2)
+     char const *s1, *s2;
+{
+  register unsigned char const *t1 = (unsigned char const *) s1;
+  register unsigned char const *t2 = (unsigned char const *) s2;
+
+  while (1)
+    {
+      register unsigned char c1 = *t1++;
+      register unsigned char c2 = *t2++;
+
+      /* Test for exact char equality first, since it's a common case.  */
+      if (c1 != c2)
+	{
+	  /* Ignore horizontal white space if -b or -w is specified.  */
+
+	  if (ignore_all_space_flag)
+	    {
+	      /* For -w, just skip past any white space.  */
+	      while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
+	      while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
+	    }
+	  else if (ignore_space_change_flag)
+	    {
+	      /* For -b, advance past any sequence of white space in line 1
+		 and consider it just one Space, or nothing at all
+		 if it is at the end of the line.  */
+	      if (ISSPACE (c1))
+		{
+		  while (c1 != '\n')
+		    {
+		      c1 = *t1++;
+		      if (! ISSPACE (c1))
+			{
+			  --t1;
+			  c1 = ' ';
+			  break;
+			}
+		    }
+		}
+
+	      /* Likewise for line 2.  */
+	      if (ISSPACE (c2))
+		{
+		  while (c2 != '\n')
+		    {
+		      c2 = *t2++;
+		      if (! ISSPACE (c2))
+			{
+			  --t2;
+			  c2 = ' ';
+			  break;
+			}
+		    }
+		}
+
+	      if (c1 != c2)
+		{
+		  /* If we went too far when doing the simple test
+		     for equality, go back to the first non-white-space
+		     character in both sides and try again.  */
+		  if (c2 == ' ' && c1 != '\n'
+		      && (unsigned char const *) s1 + 1 < t1
+		      && ISSPACE(t1[-2]))
+		    {
+		      --t1;
+		      continue;
+		    }
+		  if (c1 == ' ' && c2 != '\n'
+		      && (unsigned char const *) s2 + 1 < t2
+		      && ISSPACE(t2[-2]))
+		    {
+		      --t2;
+		      continue;
+		    }
+		}
+	    }
+
+	  /* Lowercase all letters if -i is specified.  */
+
+	  if (ignore_case_flag)
+	    {
+	      if (ISUPPER (c1))
+		c1 = tolower (c1);
+	      if (ISUPPER (c2))
+		c2 = tolower (c2);
+	    }
+
+	  if (c1 != c2)
+	    break;
+	}
+      if (c1 == '\n')
+	return 0;
+    }
+
+  return (1);
+}
+
+/* Find the consecutive changes at the start of the script START.
+   Return the last link before the first gap.  */
+
+struct change *
+find_change (start)
+     struct change *start;
+{
+  return start;
+}
+
+struct change *
+find_reverse_change (start)
+     struct change *start;
+{
+  return start;
+}
+
+/* Divide SCRIPT into pieces by calling HUNKFUN and
+   print each piece with PRINTFUN.
+   Both functions take one arg, an edit script.
+
+   HUNKFUN is called with the tail of the script
+   and returns the last link that belongs together with the start
+   of the tail.
+
+   PRINTFUN takes a subscript which belongs together (with a null
+   link at the end) and prints it.  */
+
+void
+print_script (script, hunkfun, printfun)
+     struct change *script;
+     struct change * (*hunkfun) PARAMS((struct change *));
+     void (*printfun) PARAMS((struct change *));
+{
+  struct change *next = script;
+
+  while (next)
+    {
+      struct change *this, *end;
+
+      /* Find a set of changes that belong together.  */
+      this = next;
+      end = (*hunkfun) (next);
+
+      /* Disconnect them from the rest of the changes,
+	 making them a hunk, and remember the rest for next iteration.  */
+      next = end->link;
+      end->link = 0;
+#ifdef DEBUG
+      debug_script (this);
+#endif
+
+      /* Print this hunk.  */
+      (*printfun) (this);
+
+      /* Reconnect the script so it will all be freed properly.  */
+      end->link = next;
+    }
+}
+
+/* Print the text of a single line LINE,
+   flagging it with the characters in LINE_FLAG (which say whether
+   the line is inserted, deleted, changed, etc.).  */
+
+void
+print_1_line (line_flag, line)
+     char const *line_flag;
+     char const * const *line;
+{
+  char const *text = line[0], *limit = line[1]; /* Help the compiler.  */
+  FILE *out = outfile; /* Help the compiler some more.  */
+  char const *flag_format = 0;
+
+  /* If -T was specified, use a Tab between the line-flag and the text.
+     Otherwise use a Space (as Unix diff does).
+     Print neither space nor tab if line-flags are empty.  */
+
+  if (line_flag && *line_flag)
+    {
+      flag_format = tab_align_flag ? "%s\t" : "%s ";
+      fprintf (out, flag_format, line_flag);
+    }
+
+  output_1_line (text, limit, flag_format, line_flag);
+
+  if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
+    fprintf (out, "\n\\ No newline at end of file\n");
+}
+
+/* Output a line from TEXT up to LIMIT.  Without -t, output verbatim.
+   With -t, expand white space characters to spaces, and if FLAG_FORMAT
+   is nonzero, output it with argument LINE_FLAG after every
+   internal carriage return, so that tab stops continue to line up.  */
+
+void
+output_1_line (text, limit, flag_format, line_flag)
+     char const *text, *limit, *flag_format, *line_flag;
+{
+  if (!tab_expand_flag)
+    fwrite (text, sizeof (char), limit - text, outfile);
+  else
+    {
+      register FILE *out = outfile;
+      register unsigned char c;
+      register char const *t = text;
+      register unsigned column = 0;
+
+      while (t < limit)
+	switch ((c = *t++))
+	  {
+	  case '\t':
+	    {
+	      unsigned spaces = TAB_WIDTH - column % TAB_WIDTH;
+	      column += spaces;
+	      do
+		putc (' ', out);
+	      while (--spaces);
+	    }
+	    break;
+
+	  case '\r':
+	    putc (c, out);
+	    if (flag_format && t < limit && *t != '\n')
+	      fprintf (out, flag_format, line_flag);
+	    column = 0;
+	    break;
+
+	  case '\b':
+	    if (column == 0)
+	      continue;
+	    column--;
+	    putc (c, out);
+	    break;
+
+	  default:
+	    if (ISPRINT (c))
+	      column++;
+	    putc (c, out);
+	    break;
+	  }
+    }
+}
+
+int
+change_letter (inserts, deletes)
+     int inserts, deletes;
+{
+  if (!inserts)
+    return 'd';
+  else if (!deletes)
+    return 'a';
+  else
+    return 'c';
+}
+
+/* Translate an internal line number (an index into diff's table of lines)
+   into an actual line number in the input file.
+   The internal line number is LNUM.  FILE points to the data on the file.
+
+   Internal line numbers count from 0 starting after the prefix.
+   Actual line numbers count from 1 within the entire file.  */
+
+int
+translate_line_number (file, lnum)
+     struct file_data const *file;
+     int lnum;
+{
+  return lnum + file->prefix_lines + 1;
+}
+
+void
+translate_range (file, a, b, aptr, bptr)
+     struct file_data const *file;
+     int a, b;
+     int *aptr, *bptr;
+{
+  *aptr = translate_line_number (file, a - 1) + 1;
+  *bptr = translate_line_number (file, b + 1) - 1;
+}
+
+/* Print a pair of line numbers with SEPCHAR, translated for file FILE.
+   If the two numbers are identical, print just one number.
+
+   Args A and B are internal line numbers.
+   We print the translated (real) line numbers.  */
+
+void
+print_number_range (sepchar, file, a, b)
+     int sepchar;
+     struct file_data *file;
+     int a, b;
+{
+  int trans_a, trans_b;
+  translate_range (file, a, b, &trans_a, &trans_b);
+
+  /* Note: we can have B < A in the case of a range of no lines.
+     In this case, we should print the line number before the range,
+     which is B.  */
+  if (trans_b > trans_a)
+    fprintf (outfile, "%d%c%d", trans_a, sepchar, trans_b);
+  else
+    fprintf (outfile, "%d", trans_b);
+}
+
+/* Look at a hunk of edit script and report the range of lines in each file
+   that it applies to.  HUNK is the start of the hunk, which is a chain
+   of `struct change'.  The first and last line numbers of file 0 are stored in
+   *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
+   Note that these are internal line numbers that count from 0.
+
+   If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
+
+   Also set *DELETES nonzero if any lines of file 0 are deleted
+   and set *INSERTS nonzero if any lines of file 1 are inserted.
+   If only ignorable lines are inserted or deleted, both are
+   set to 0.  */
+
+void
+analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
+     struct change *hunk;
+     int *first0, *last0, *first1, *last1;
+     int *deletes, *inserts;
+{
+  int l0, l1, show_from, show_to;
+  int i;
+  int trivial = ignore_blank_lines_flag || ignore_regexp_list;
+  struct change *next;
+
+  show_from = show_to = 0;
+
+  *first0 = hunk->line0;
+  *first1 = hunk->line1;
+
+  next = hunk;
+  do
+    {
+      l0 = next->line0 + next->deleted - 1;
+      l1 = next->line1 + next->inserted - 1;
+      show_from += next->deleted;
+      show_to += next->inserted;
+
+      for (i = next->line0; i <= l0 && trivial; i++)
+	if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n')
+	  {
+	    struct regexp_list *r;
+	    char const *line = files[0].linbuf[i];
+	    int len = files[0].linbuf[i + 1] - line;
+
+	    for (r = ignore_regexp_list; r; r = r->next)
+	      if (0 <= re_search (&r->buf, line, len, 0, len, 0))
+		break;	/* Found a match.  Ignore this line.  */
+	    /* If we got all the way through the regexp list without
+	       finding a match, then it's nontrivial.  */
+	    if (!r)
+	      trivial = 0;
+	  }
+
+      for (i = next->line1; i <= l1 && trivial; i++)
+	if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n')
+	  {
+	    struct regexp_list *r;
+	    char const *line = files[1].linbuf[i];
+	    int len = files[1].linbuf[i + 1] - line;
+
+	    for (r = ignore_regexp_list; r; r = r->next)
+	      if (0 <= re_search (&r->buf, line, len, 0, len, 0))
+		break;	/* Found a match.  Ignore this line.  */
+	    /* If we got all the way through the regexp list without
+	       finding a match, then it's nontrivial.  */
+	    if (!r)
+	      trivial = 0;
+	  }
+    }
+  while ((next = next->link) != 0);
+
+  *last0 = l0;
+  *last1 = l1;
+
+  /* If all inserted or deleted lines are ignorable,
+     tell the caller to ignore this hunk.  */
+
+  if (trivial)
+    show_from = show_to = 0;
+
+  *deletes = show_from;
+  *inserts = show_to;
+}
+
+/* malloc a block of memory, with fatal error message if we can't do it. */
+
+VOID *
+xmalloc (size)
+     size_t size;
+{
+  register VOID *value;
+
+  if (size == 0)
+    size = 1;
+
+  value = (VOID *) malloc (size);
+
+  if (!value)
+    fatal ("memory exhausted");
+  return value;
+}
+
+/* realloc a block of memory, with fatal error message if we can't do it. */
+
+VOID *
+xrealloc (old, size)
+     VOID *old;
+     size_t size;
+{
+  register VOID *value;
+
+  if (size == 0)
+    size = 1;
+
+  value = (VOID *) realloc (old, size);
+
+  if (!value)
+    fatal ("memory exhausted");
+  return value;
+}
+
+/* Concatenate three strings, returning a newly malloc'd string.  */
+
+char *
+concat (s1, s2, s3)
+     char const *s1, *s2, *s3;
+{
+  size_t len = strlen (s1) + strlen (s2) + strlen (s3);
+  char *new = xmalloc (len + 1);
+  sprintf (new, "%s%s%s", s1, s2, s3);
+  return new;
+}
+
+/* Yield the newly malloc'd pathname
+   of the file in DIR whose filename is FILE.  */
+
+char *
+dir_file_pathname (dir, file)
+     char const *dir, *file;
+{
+  char const *p = filename_lastdirchar (dir);
+  return concat (dir, "/" + (p && !p[1]), file);
+}
+
+void
+debug_script (sp)
+     struct change *sp;
+{
+  fflush (stdout);
+  for (; sp; sp = sp->link)
+    fprintf (stderr, "%3d %3d delete %d insert %d\n",
+	     sp->line0, sp->line1, sp->deleted, sp->inserted);
+  fflush (stderr);
+}
diff --git a/gnu/usr.bin/diff/version.c b/gnu/usr.bin/diff/version.c
new file mode 100644
index 000000000000..02993976351b
--- /dev/null
+++ b/gnu/usr.bin/diff/version.c
@@ -0,0 +1,5 @@
+/* Version number of GNU diff.  */
+
+#include <config.h>
+
+char const version_string[] = "2.7";
diff --git a/gnu/usr.bin/diff/xmalloc.c b/gnu/usr.bin/diff/xmalloc.c
new file mode 100644
index 000000000000..dc44ba4cf7db
--- /dev/null
+++ b/gnu/usr.bin/diff/xmalloc.c
@@ -0,0 +1,81 @@
+/* xmalloc.c -- malloc with out of memory checking
+   Copyright (C) 1990, 1991, 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
+void free ();
+#endif
+
+#if __STDC__ && defined (HAVE_VPRINTF)
+void error (int, int, char const *, ...);
+#else
+void error ();
+#endif
+
+/* Allocate N bytes of memory dynamically, with error checking.  */
+
+VOID *
+xmalloc (n)
+     size_t n;
+{
+  VOID *p;
+
+  p = malloc (n);
+  if (p == 0)
+    /* Must exit with 2 for `cmp'.  */
+    error (2, 0, "memory exhausted");
+  return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+   with error checking.
+   If P is NULL, run xmalloc.
+   If N is 0, run free and return NULL.  */
+
+VOID *
+xrealloc (p, n)
+     VOID *p;
+     size_t n;
+{
+  if (p == 0)
+    return xmalloc (n);
+  if (n == 0)
+    {
+      free (p);
+      return 0;
+    }
+  p = realloc (p, n);
+  if (p == 0)
+    /* Must exit with 2 for `cmp'.  */
+    error (2, 0, "memory exhausted");
+  return p;
+}