OSDN Git Service

Initial revision
authorgrimmd <none@none>
Fri, 27 Oct 2000 03:07:23 +0000 (03:07 +0000)
committergrimmd <none@none>
Fri, 27 Oct 2000 03:07:23 +0000 (03:07 +0000)
78 files changed:
Src/CHANGELO [new file with mode: 0644]
Src/COPYING [new file with mode: 0644]
Src/ChildFrm.cpp [new file with mode: 0644]
Src/ChildFrm.h [new file with mode: 0644]
Src/Diff.cpp [new file with mode: 0644]
Src/DiffContext.cpp [new file with mode: 0644]
Src/DiffContext.h [new file with mode: 0644]
Src/Dir.cpp [new file with mode: 0644]
Src/DirDoc.cpp [new file with mode: 0644]
Src/DirDoc.h [new file with mode: 0644]
Src/DirFrame.cpp [new file with mode: 0644]
Src/DirFrame.h [new file with mode: 0644]
Src/DirView.cpp [new file with mode: 0644]
Src/DirView.h [new file with mode: 0644]
Src/MainFrm.cpp [new file with mode: 0644]
Src/MainFrm.h [new file with mode: 0644]
Src/Merge.cpp [new file with mode: 0644]
Src/Merge.dsp [new file with mode: 0644]
Src/Merge.h [new file with mode: 0644]
Src/Merge.rc [new file with mode: 0644]
Src/MergeDoc.cpp [new file with mode: 0644]
Src/MergeDoc.h [new file with mode: 0644]
Src/OpenDlg.cpp [new file with mode: 0644]
Src/OpenDlg.h [new file with mode: 0644]
Src/PropGeneral.cpp [new file with mode: 0644]
Src/PropGeneral.h [new file with mode: 0644]
Src/PropVss.cpp [new file with mode: 0644]
Src/PropVss.h [new file with mode: 0644]
Src/STACK.C [new file with mode: 0644]
Src/Splash.cpp [new file with mode: 0644]
Src/Splash.h [new file with mode: 0644]
Src/StdAfx.cpp [new file with mode: 0644]
Src/StdAfx.h [new file with mode: 0644]
Src/VssPrompt.cpp [new file with mode: 0644]
Src/VssPrompt.h [new file with mode: 0644]
Src/diffmain.c [new file with mode: 0644]
Src/diffutils/CONFIG.H [new file with mode: 0644]
Src/diffutils/GnuVersion.c [new file with mode: 0644]
Src/diffutils/lib/ALLOCA.C [new file with mode: 0644]
Src/diffutils/lib/CMPBUF.C [new file with mode: 0644]
Src/diffutils/lib/CMPBUF.H [new file with mode: 0644]
Src/diffutils/lib/ERROR.C [new file with mode: 0644]
Src/diffutils/lib/FNMATCH.C [new file with mode: 0644]
Src/diffutils/lib/FNMATCH.H [new file with mode: 0644]
Src/diffutils/lib/GETOPT.C [new file with mode: 0644]
Src/diffutils/lib/GETOPT.H [new file with mode: 0644]
Src/diffutils/lib/GETOPT1.C [new file with mode: 0644]
Src/diffutils/lib/REGEX.C [new file with mode: 0644]
Src/diffutils/lib/REGEX.H [new file with mode: 0644]
Src/diffutils/lib/XMALLOC.C [new file with mode: 0644]
Src/diffutils/src/CMP.C [new file with mode: 0644]
Src/diffutils/src/CONTEXT.C [new file with mode: 0644]
Src/diffutils/src/DIFF.C [new file with mode: 0644]
Src/diffutils/src/DIFF.H [new file with mode: 0644]
Src/diffutils/src/DIR.C [new file with mode: 0644]
Src/diffutils/src/DIRENT.C [new file with mode: 0644]
Src/diffutils/src/DIRENT.H [new file with mode: 0644]
Src/diffutils/src/ED.C [new file with mode: 0644]
Src/diffutils/src/IFDEF.C [new file with mode: 0644]
Src/diffutils/src/IO.C [new file with mode: 0644]
Src/diffutils/src/NORMAL.C [new file with mode: 0644]
Src/diffutils/src/SDIFF.C [new file with mode: 0644]
Src/diffutils/src/SIDE.C [new file with mode: 0644]
Src/diffutils/src/SYSTEM.H [new file with mode: 0644]
Src/diffutils/src/UTIL.C [new file with mode: 0644]
Src/diffutils/src/analyze.c [new file with mode: 0644]
Src/res/Merge.ico [new file with mode: 0644]
Src/res/Merge.rc2 [new file with mode: 0644]
Src/res/MergeDoc.ico [new file with mode: 0644]
Src/res/Toolbar.bmp [new file with mode: 0644]
Src/res/binary.bmp [new file with mode: 0644]
Src/res/equal.bmp [new file with mode: 0644]
Src/res/notequal.bmp [new file with mode: 0644]
Src/res/rfolder.bmp [new file with mode: 0644]
Src/res/splash1.bmp [new file with mode: 0644]
Src/res/unknown.bmp [new file with mode: 0644]
Src/resource.h [new file with mode: 0644]
Src/ssauto.h [new file with mode: 0644]

diff --git a/Src/CHANGELO b/Src/CHANGELO
new file mode 100644 (file)
index 0000000..744aa27
--- /dev/null
@@ -0,0 +1,1481 @@
+##########################################################################
+Begin WinMerge revision notes
+##########################################################################
+Tue Dec 02 00:10:21 1997  Dean Grimm (dean@cortron.com)
+
+       * Removed command line functions from diff.c
+
+       * converted diff.c to c++
+
+       * added extern "C" to all headers
+
+       * analyze.c (diff_2_files): changed to return pointer to script
+
+       * diff.h: changed prototype for diff_2_files
+
+##########################################################################
+Begin GNU diff revision notes
+##########################################################################
+
+Mon Sep 27 00:23:37 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * Version 2.5 released.
+
+       * analyze.c (diff_2_files): Work around memcmp bug with size=0.
+
+       * cmp.c (main, usage, version_string): Add --version option.
+       * Makefile.in (cmp): Add version.o.
+
+       * diff.c (add_exclude_file): Cast memchr to (char *)
+       to suppress bogus warnings on some nonstandard hosts.
+       * sdiff.c (lf_copy, lf_skip, lf_snarf): Likewise.
+
+       * diff3.c, sdiff.c, util.c (xmalloc, xrealloc): Cast malloc, realloc
+       to (VOID *) to suppress bogus warnings on some nonstandard hosts.
+
+       * sdiff.c, util.c (memchr): Make first arg char const *
+       to match standard.
+
+       * system.h (malloc, realloc): Declare only if !HAVE_STDLIB_H.
+       (memchr): Declare only if !HAVE_MEMCHR.  These changes are
+       needed to keep some nonstandard hosts happy.
+
+       * xmalloc.c: Include <sys/types.h> always; some nonstandard hosts
+       need it for size_t even if STDC_HEADERS.
+
+Sat Sep 18 01:33:07 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * configure.in (AC_STAT_MACROS_BROKEN): Add.
+       * system.h (S_IS{BLK,CHR,DIR,FIFO,REG,SOCK}): Fix defns if
+       STAT_MACROS_BROKEN.
+
+       * Makefile.in (diff3, sdiff, cmp): Do not link $(ALLOCA).
+
+       * analyze.c (discard_confusing_lines): Make defn static, like decl.
+       * sdiff.c (xmalloc): Likewise.
+
+       * ifdef.c (format_group): Ensure isdigit argument isn't < 0.
+
+       * side.c (print_half_line): Use isprint, since some hosts lack isgraph.
+       * util.c (output_1_line): Likewise.  Ensure its argument isn't < 0.
+       (xmalloc, xrealloc): Remove needless casts.
+
+       * system.h (volatile, const):
+       Define these before including any system headers,
+       so that they're used consistently in all system includes.
+       (getenv, malloc, realloc): Declare even if HAVE_STDLIB_H, since some
+       <stdlib.h>s don't declare them.
+       (memchr): Likewise for <string.h>.
+
+       * cmp.c, diff3.c, diff.h, sdiff.c: Include "system.h" first.
+       * diff.c: Remove redundant "system.h" inclusion.
+
+       * diff3.c (xmalloc): Now static.
+       (xmalloc, realloc): Remove needless casts.
+       (READNUM): Ensure isdigit argument isn't negative.
+
+Wed Sep 14 07:14:15 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * Version 2.4 released.
+
+       * ifdef.c (scan_char_literal): New function, for new %c'x' and
+       %c'\ooo' format specs.
+       (format_group, print_ifdef_lines): Use it.  Remove %0 format spec.
+
+       * cmp.c (cmp): Don't try to read past end of file; this doesn't
+       work on ttys.
+
+       * system.h, version.c: #include <config.h>, not "config.h", to allow
+       configuring in a separate directory when the source directory has
+       already been configured.
+       * Makefile.in (COMPILE): New defn, with proper -I options so that
+       `#include <config.h>' works.
+       (.c.o, diff3.o, sdiff.o): Use it.
+
+Mon Sep 13 06:45:43 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c (main, longopts): Add --line-format=FORMAT option.
+       (specify_format): Args no longer const pointers.  All callers changed.
+
+       * ifdef.c: Add support for %?c, %(A=B?T:E), PRINTF_SPECn formats.
+       (struct group): New struct.
+       (print_ifdef_lines): Use it to simplify argument passing.
+       Remove the convention that last arg -1 signifies that the lines
+       from file 2 are the same as the lines from file 1; this
+       convention no longer works, now that line numbers might be
+       printed out, since the line numbers may differ.
+       Add first FILE * argument to output to.  All callers changed.
+       Use a faster test for the single-fwrite optimization.
+       (format_group, scan_printf_spec, groups_letter_value): New functions.
+
+       * diff.h (group_format, line_format): No longer const pointers.
+       (format_ifdef): 1st arg is no longer const pointer.
+
+       * configure.in: Configure HAVE_LIMITS_H, HAVE_STDLIB_H.
+       * system.h <limits.h>, <stdlib.h>, <string.h>:
+       Include only if HAVE_LIMITS_H etc.
+
+       * system.h (memcmp, memcpy, strchr, strrchr, struct dirent): Prefer
+       these standard names to the traditional names (bcmp, bcpy, index,
+       rindex, struct direct).  All callers changed.
+
+       * system.h (PARAMS, VOID):
+       Define earlier so that malloc decl can use VOID.
+       (STAT_BLOCKSIZE): Simplify ersatz defn; just use 8K.
+
+Fri Sep  3 00:21:02 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c (compare_files): Two files with the same name must be
+       the same file; avoid a needless `stat' in that case.
+
+Fri Aug 27 06:59:03 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * Pervasive changes for portability to 64-bit hosts:
+       Add prototypes to function declarations.
+       Use size_t, not int, when needed.
+
+       * Other pervasive changes:
+       Use `const' more often.
+       Use STD{IN,OUT,ERR}_FILENO instead of [012].
+       Use 0, not NULL, for portability to broken hosts.
+
+       * Makefile.in: (srcs, objs, distfiles, cmp): New files cmpbuf.[ch].
+       (distfiles): New files config.h.in, mkinstalldirs.
+       (.c.o): Add -DHAVE_CONFIG_H.
+
+       * analyze.c: (diag): Pacify `gcc -Wall' with a useless assignment.
+       (diff_2_files): Use l.c.m., not max, of files' buffer sizes.
+
+       * cmp.c: Make globals static when possible.
+
+       (file): Now a 2-element array; replaces `file1' and `file2'.
+       (file_desc, buffer): Likewise, for file[12]_desc and buf[12].
+       (main): Likewise, for stat_buf[12].  Index these variables with `i'.
+
+       (ignore_initial): New var.
+       (long_options): Now const.  Add `--ignore-initial'.
+       (usage): Sort options and add `--ignore-initial'.
+       (main, cmp): Add `--ignore-initial' support.
+
+       (main): `cmp - -' now succeeds.
+       When comparing standard input to a file, and using a shortcut (e.g.
+       looking at file sizes or inode numbers), take the lseek offset into
+       account before deciding whether the files are identical.
+       Avoid mentioning `dev_t', `ino_t' for portability to nonstandard hosts.
+       Use l.c.m. of files' buffer sizes, not 8 * 1024.
+       ferror (stdout) does not imply errno has a useful value.
+       If 2nd file is "-", treat it first, in case stdin is closed.
+
+       (cmp): Always compute `char_number', `smaller' for speed and simplicity.
+       Say `cmp: EOF on input', not `/usr/gnu/bin/cmp: EOF on input',
+       as per Posix.2.
+
+       (block_compare_and_count): Increment line_number argument.
+       Remove end_char argument; it's always '\n'.  All callers changed.
+       Do not assume sizeof(long) == 4; this isn't true on some 64-bit hosts.
+       (block_compare): Minimize differences with block_compare_and_count.
+
+       (block_read): Coalesce `bp += nread's.
+
+       (printc): Remove `FILE *' arg; output to stdout.  All callers changed.
+
+       * configure.in: Configure HAVE_SIGACTION, RETSIGTYPE, HAVE_VPRINTF.
+       Configure into config.h.
+
+       * context.c (print_context_label):
+       Standard input's st_mtime is no longer a special case
+       here, since `compare_files' now sets it to the current time.
+
+       * diff.c (usage): Sort options.
+       (filetype): New function.
+       (compare_files): Set stdin's st_mtime to be the current time.
+       Leave its name "-" instead of changing it to "Standard Input";
+       to test whether a file is stdin, we must compare its name to "-" instead
+       of its desc to 0, since if it's closed other file descs may be 0.
+       When comparing standard input to a file, and using a shortcut (e.g.
+       looking at file sizes or inode numbers), take the lseek offset into
+       account before deciding whether the files are identical.
+       Pretend that nonexistent files have the same filetype as existing files.
+       Rename `errorcount' to `failed', since it's boolean.
+       In directory comparisons, if a file is neither a regular file nor a
+       directory, just print its type and the other file's type.
+
+       * diff.h (Is_space, textchar): Remove.
+       (struct msg, msg_chain, msg_chain_end): Move to util.c.
+       (VOID): Move to system.h.
+       (line_cmp, version_string, change_letter, print_number_range,
+       find_change): New decls.
+
+       * diff.texi:
+       whitespace -> white space.  It now stands for whatever isspace yields.
+       Add --ignore-initial.
+
+       * diff3.c (VOID): Move to system.h.
+       (version_string): Now char[].
+       (usage): Sort options.
+       (process_diff): Pacify `gcc -Wall' with a useless assignment.
+       (read_diff): pid is of type pid_t, not int.  Use waitpid if available.
+       (output_diff3): Simplify test for `\ No newline at end of file' message.
+
+       * dir.c (struct dirdata): Rename `files' to `names' to avoid confusion
+       with external struct file_data `files'.
+
+       * io.c (line_cmp): Move declaration to diff.h.
+       (textchar): Remove.
+       (find_and_hash_each_line): Use locale's definition of white space
+       instead of using one hardwired defn for -b and another for -w.
+
+       * normal.c (change_letter, print_number_range, find_change):
+       Move decls to diff.h.
+       (print_normal_hunk): Now static.
+
+       * sdiff.c (SEEK_SET): Move to system.h.
+       (version_string): Now char[], not char*.
+       (private_tempnam): Remove hardcoded limit on temporary file names.
+       (exiterr, perror_fatal, main): When exiting because of a signal,
+       exit with that signal's status.
+       (lf_refill, main, skip_white, edit, interact): Check for signal.
+       (ignore_SIGINT): Renamed from `ignore_signals'.
+       (NUM_SIGS, initial_handler): New macros.
+       (initial_action, signal_received, sigs_trapped): New vars.
+       (catchsig, trapsigs): Use sigaction if possible, since this closes the
+       windows of vulnerability that `signal' has.  Use RETSIGTYPE not void.
+       When a signal comes in, just set a global variable; this is safer.
+       (checksigs, untrapsig): New functions.
+       (edit): Pacify `gcc -Wall' with a useless assignment.
+       Respond to each empty line with help, not to every other empty line.
+       (private_tempnam): Remove hardcoded limit on temporary file name length.
+       Don't assume sizeof (pid_t) <= sizeof (int).
+
+       * system.h: (S_IXOTH, S_IXGRP, S_IXUSR,
+       SEEK_SET, SEEK_CUR,
+       STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO):
+       New macros, if system doesn't define them.
+       (volatile): Don't define if already defined.
+       (PARAMS): New macro.
+       (VOID): Move here from diff.h.
+
+       * util.c (struct msg, msg_chain, msg_chain_end): Moved here from diff.h.
+       (message5): New function.
+       (pr_pid): New var.
+       (begin_output): Allocate `name' more precisely.
+       Put child pid into pr_pid, so that we can wait for it later.
+       Don't check execl's return value, since any return must be an error.
+       (finish_output): Detect and report output errors.
+       Use waitpid if available.  Check pr exit status.
+       (line_cmp): Use locale's definition of white space
+       instead of using one hardwired defn for -b and another for -w.
+       (analyze_cmp): Avoid double negation with `! nontrivial'.
+       Pacify `gcc -Wall' be rewriting for-loop into do-while-loop.
+       (dir_file_pathname): New function.
+
+       * version.c (version_string): Now char[], not char*.
+
+Thu Jul 29 20:44:30 1993  David J. MacKenzie  (djm@wookumz.gnu.ai.mit.edu)
+
+       * Makefile.in (config.status): Run config.status --recheck, not
+       configure, to get the right args passed.
+
+Thu Jul 22 10:46:30 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * Makefile.in (dist): Replace `if [ ! TEST ]; then ACTION; fi'
+       with `[ TEST ] || ACTION || exit' so that the containing for-loop exits
+       with proper status for `make'.
+
+Thu Jul  8 19:47:22 1993  David J. MacKenzie  (djm@goldman.gnu.ai.mit.edu)
+
+       * Makefile.in (installdirs): New target.
+       (install): Use it.
+       (Makefile, config.status, configure): New targets.
+
+Sat Jun  5 23:10:40 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * Makefile.in (dist): Switch from .z to .gz.
+
+Wed May 26 17:16:02 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c (main): Cast args to compare_files, for traditional C.
+       * side.c (print_sdiff_common_lines_print_sdiff_hunk): Likewise.
+       * analyze.c, diff3.c, sdiff.c, util.c: Don't assume NULL is defined
+       properly.
+
+Tue May 25 14:54:05 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * analyze.c (diff_2_files):  With -q, do not report that files differ
+       if all their differences are ignored.
+       (briefly_report): New function.
+       * diff.h (ignore_some_changes): New variable.
+       * diff.c (compare_files): Don't use the file size shortcut if
+       ignore_some_changes is nonzero, since the file size may differ
+       merely due to ignored changes.
+       (main):  Set ignore_some_changes if we might ignore some changes.
+       Remove unsystematic assignment of 0 to static vars.
+       * io.c (read_files): New argument PRETEND_BINARY says whether to
+       pretend the files are binary.
+
+       * diff3.c (tab_align_flag): New variable, for new -T option.
+       (main, usage, output_diff3): Add support for -T.
+
+Sun May 23 15:25:29 1993  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * dir.c (dir_sort): Always init `data' to avoid GCC warning.
+
+Sat May 22 15:35:02 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * Makefile.in (dist): Change name of package from diff to diffutils.
+       Don't bother to build .Z dist; .z suffices.
+
+Fri May 21 16:35:22 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c: Include "system.h" to get memchr declaration.
+       * system.h (memchr): Declare if !HAVE_MEMCHR, not if
+       !HAVE_MEMCHR && !STDC_HEADERS.
+
+Wed May 19 17:43:55 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * Version 2.3 released.
+
+Fri Apr 23 17:18:44 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * io.c (find_identical_ends): Do not discard the last HORIZON_LINES
+       lines of the prefix, or the first HORIZON_LINES lines of the suffix.
+       * diff.c (main, longopts, usage): Add --horizon-lines option.
+       * diff3.c (main, process_diff, read_diff): Invoke second diff
+       with --horizon-lines determined by the first diff.
+       * diff.h, diff3.c (horizon_lines): New variable.
+
+Mon Mar 22 16:16:00 1993  Roland McGrath  (roland@churchy.gnu.ai.mit.edu)
+
+       * system.h [HAVE_STRING_H || STDC_HEADERS] (bcopy, bcmp, bzero):
+       Don't define if already defined.
+
+Fri Mar  5 00:20:16 1993  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * diff.c (main): Use NULL in arg to compare_files.
+
+Thu Feb 25 15:26:01 1993  Roland McGrath  (roland@churchy.gnu.ai.mit.edu)
+
+       * system.h: Declare memchr #if !HAVE_MEMCHR && !STDC_HEADERS,
+       not #if !HAVE_MEMCHR || !STDC_HEADERS.
+
+Mon Feb 22 15:04:46 1993  Richard Stallman  (rms@geech.gnu.ai.mit.edu)
+
+       * io.c (find_identical_ends): Move complicated arg outside GUESS_LINES.
+
+Mon Feb 22 12:56:12 1993  Roland McGrath  (roland@churchy.gnu.ai.mit.edu)
+
+       * Makefile.in (.c.o): Add -I$(srcdir); put $(CFLAGS) last before $<.
+
+Sat Feb 20 19:18:56 1993  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * io.c (binary_file_p): Return zero if file size is zero.
+
+Fri Feb 19 17:31:32 1993  Roland McGrath  (roland@geech.gnu.ai.mit.edu)
+
+       * Version 2.2 released.
+
+       * system.h [HAVE_STRING_H || STDC_HEADERS] (index, rindex): Don't
+       define if already defined.
+
+Wed Feb 17 17:08:00 1993  Roland McGrath  (roland@churchy.gnu.ai.mit.edu)
+
+       * Makefile.in (srcs): Remove limits.h.
+
+Thu Feb 11 03:36:00 1993  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * diff3.c (xmalloc): No longer static.
+
+       * sdiff.c (edit): Allocate buf dynamically.
+
+       * dir.c (dir_sort): Handle VOID_CLOSEDIR.
+
+Wed Feb 10 00:15:54 1993  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * limits.h: File deleted (should never have been there). 
+
+Tue Feb  9 03:53:22 1993  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * Makefile.in (.c.o, diff3.o, sdiff.o): Put $(CFLAGS) last.
+
+Wed Feb  3 15:42:10 1993  David J. MacKenzie  (djm@goldman.gnu.ai.mit.edu)
+
+       * system.h: Don't #define const; let configure do it.
+
+Mon Feb  1 02:13:23 1993  Paul Eggert  (eggert@hal.gnu.ai.mit.edu)
+
+       * Version 2.1 released.
+
+       * Makefile.in (dist): Survive ln failures.  Create .tar.z
+       (gzipped tar) file as well as .tar.Z (compressed tar) file.
+
+Fri Jan  8 22:31:41 1993  Paul Eggert  (eggert@twinsun.com)
+
+       * side.c (print_half_line): When the input position falls
+       outside the column, do not output a tab even if the output
+       position still falls within the column.
+
+Mon Dec 21 13:54:36 1992  David J. MacKenzie  (djm@kropotkin.gnu.ai.mit.edu)
+
+       * Makefile.in (.c.o): Add -I.
+
+Fri Dec 18 14:08:20 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * configure.in: Add HAVE_FCNTL_H, since system.h uses it.
+
+Tue Nov 24 10:06:48 1992  David J. MacKenzie  (djm@goldman.gnu.ai.mit.edu)
+
+       * Makefile.in: Note change from USG to HAVE_STRING_H.
+
+Mon Nov 23 18:44:00 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * io.c (find_and_hash_each_line): When running out of lines,
+       double the number of allocated lines, instead of just doubling
+       that number minus the prefix lines.  This is more likely to
+       avoid the need for further memory allocation.
+
+Wed Nov 18 20:40:28 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * dir.c (dir_sort): Plug memory leak: space holding directory
+       contents was not being reclaimed.  Get directory size from
+       struct file_data for initial guess at memory needed.
+       Detect errors when reading and closing directory.
+       (diff_dirs): Pass struct file_data to dir_sort.  Finish plugging leak.
+       * diff.c (compare_files): Pass struct file_data to diff_dirs.
+
+       * io.c (find_and_hash_each_line): Don't assume alloc_lines is
+       nonzero when allocating more lines.
+
+Thu Nov 12 16:02:18 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c (main): Add `-U lines' as an alias for `--unified=lines'.
+
+       * diff3.c (usage): Add third --label option in example.
+
+       * util.c (analyze_hunk): Fix test for ignoring blank lines.
+
+       * configure.in, system.h: Avoid USG; use HAVE_TIME_H etc. instead.
+
+Mon Nov  9 05:13:25 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * diff3.c (main, usage): Add -A or --show-all.
+       -m now defaults to -A, not -E.  Allow up to three -L options.
+       (output_diff3_edscript, output_diff3_merge):
+       Remove spurious differences between these two functions.
+       Output ||||||| for -A.  Distinguish between conflicts and overlaps.
+       (dotlines, undotlines): New functions that output `Ns', not `N,Ns'.
+       (output_diff3_edscript, output_diff3_merge): Use them.
+
+       * io.c (find_identical_ends): shift_boundaries needs an extra
+       identical line at the end, not at the beginning.
+
+       * sdiff.c (edit): execvp wants char **, not const char **.
+
+Mon Oct 19 04:39:32 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * context.c (print_context_script, find_function): Context
+       line numbers start with - file->prefix_lines, not 0.
+
+       * io.c (binary_file_p): Undo last change; it was a library bug.
+
+Sun Oct 18 00:17:29 1992  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * io.c (binary_file_p): Consider empty file as non-binary.
+
+Mon Oct  5 05:18:46 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * diff3.c (main, make_3way_diff, using_to_diff3_block): Don't
+       report bogus differences (for one of -mexEX3) just because the
+       file0-file1 diffs don't line up with the file0-file2 diffs.
+       (This is entirely possible since we don't use diff's -n
+       option.)  Always compare file1 to file2, so that diff3 sees
+       those changes directly.  Typically, file2 is now the common
+       file, not file0.
+       (output_diff3_merge): The input file is file 0, not the common file.
+
+       (FC, FO): New macros; they replace FILE1, FILE0 for two-way diffs,
+       to distinguish them from three-way diffs.
+
+       * diff3.c (using_to_diff3_block): Fold repeated code into loops.
+
+       * diff3.c (make_3way_diff, process_diff): Have the *_end
+       variable point to the next field to be changed, not to the last
+       object allocated; this saves an if-then-else.
+
+       * diff3.c (process_diff): Use D_NUMLINES instead of its definiens.
+
+       * diff3.c: Make fns and vars static unless they must be external.
+
+Wed Sep 30 09:21:59 1992  Paul Eggert  (eggert@twinsun.com)
+       * analyze.c (diff_2_files): OUTPUT_IFDEF is now robust.
+       * diff.h (ROBUST_OUTPUT_STYLE): Likewise.
+       (default_line_format): Remove.  All refs removed.
+
+       * ifdef.c (print_ifdef_lines): Add %L.  Optimize %l\n even if user
+       specified it, as opposed to its being the default.
+Tue Sep 29 19:01:28 1992  Paul Eggert  (eggert@twinsun.com)
+       * diff.c (longopts, main): --{old,new,unchanged,changed}--group-format
+       are new options, so that -D is no longer overloaded.  Set
+       no_diff_means_no_output if --unchanged-{line,group}-format allows it.
+       * diff.h (enum line_class): New type.
+       (group_format, line_format): Use it to regularize option flags.
+       All refs changed.
+
+       * ifdef.c (format_ifdef, print_ifdef_lines): %n is no longer a format.
+
+Mon Sep 28 04:51:42 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c (main, usage): Replace --line-prefix with the more general
+       --{old,new,unchanged}-line-format options.
+       * ifdef.c (format_ifdef, print_ifdef_lines): Likewise.
+       * diff.h (line_format): Renamed from line_prefix.  All refs changed.
+       * diff.h, ifdef.c (default_line_format): New variable.
+       * util.c (output_1_line): New function.
+       (print_1_line): Use it.
+
+       * ifdef.c: (format_ifdef, print_ifdef_lines): Add %0 format.
+
+Sun Sep 27 05:38:13 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c (main): Add -E or --line-prefix option.  Add -D'=xxx'
+       for common lines.  Change default -D< format from copy of -D>
+       format to to -D<%<; similarly for default -D> format.
+       * diff.h (common_format, line_prefix): New variables.
+       * ifdef.c (format_ifdef): New function.
+       (print_ifdef_script, print_ifdef_hunk, print_ifdef_lines):
+       Use it for -D'=xxx', -E.
+
+       * context.c (find_hunk): Glue together two non-ignorable changes that
+       are exactly CONTEXT * 2 lines apart.  This shortens output, removes
+       a behavioral discontinuity at CONTEXT = 0, and is more compatible
+       with traditional diff.
+
+       * io.c (find_identical_ends): Slurp stdin at most once.
+
+       * util.c (print_line_line): line_flag is const char *.
+
+Thu Sep 24 15:18:07 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * ifdef.c (print_ifdef_lines): New function, which fwrites a sequence
+       of lines all at once for speed.
+       (print_ifdef_script, print_ifdef_hunk): Use it.
+
+Thu Sep 24 05:54:14 1992  Paul Eggert  (eggert@twinsun.com)
+
+       * diff.c (main): Support new -D options for if-then-else formats.
+       (specify_format): New function.
+       * diff.h (ifndef_format, ifdef_format, ifnelse_format): New variables.
+       * ifdef.c (print_ifdef_hunk): Use the new variables instead of
+       a hardwired format.
+
+       * side.c (print_1sdiff_line): Represent incomplete lines on output.
+       (print_sdiff_script): Likewise.  Don't print 'q' at end,
+       since that doesn't work with incomplete lines.
+       * sdiff.c (interact): Don't assume diff output ends with 'q' line.
+       * diff.h (ROBUST_OUTPUT_STYLE): OUTPUT_SDIFF is now robust.
+
+       * sdiff.c (lf_copy, lf_snarf): Use memchr instead of index,
+       to avoid dumping core when files contain null characters.
+       (memchr): New function (if memchr is missing).
+
+       * io.c (sip): New arg SKIP_TEST to skip test for binary file.
+       (read_files): Don't bother testing second file if first is binary.
+
+Thu Sep 17 21:17:49 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * system.h [!USG && !_POSIX_VERSION]: Protect from conflicting
+       prototype for wait in sys/wait.h.
+
+Wed Sep 16 12:32:18 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * Makefile.in: Include binprefix in -DDIFF_PROGRAM.
+
+Tue Sep 15 14:27:25 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * Version 2.0.
+
+Sat Sep 12 01:31:19 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * util.c, diff.h, system.h [!HAVE_MEMCHR]: Don't use void *
+       and const when declaring memchr replacement.  Declare memchr
+       if !STDC_HEADERS && !USG.
+
+Thu Sep 10 15:17:32 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * Makefile.in (uninstall): New target.
+
+       * diff.c (excluded_filename): Use fnmatch, not wildmat.
+       (usage): Document -x, -X, --exclude, --exclude-from.
+       Makefile.in: Use fnmatch.c, not wildmat.c.
+
+Sun Sep  6 23:46:25 1992  Paul Eggert (eggert@twinsun.com)
+
+       * configure.in: Add HAVE_MEMCHR.
+       * diff.h, util.c: Use it instead of MEMCHR_MISSING.
+
+Sun Sep  6 07:25:49 1992  Paul Eggert (eggert@twinsun.com)
+
+       * diff.h: (struct line_def): Replace this 3-word struct with char *.
+       This uses less memory, particularly for large files with short lines.
+       (struct file_data): New member linbuf_base counts number of lines
+       in common prefix that are not recorded in linbuf;
+       this uses less memory if files are identical or differ only at end.
+       New member buffered_lines counts possibly differing lines.
+       New member valid_lines counts valid data.
+       New member alloc_lines - linbuf_base replaces old linbufsize.
+       linbuf[0] now always points at first differing line.
+       Remove unused members ltran, suffix_lines.
+       Add const where appropriate.
+       (Is_space): New macro, for consistent definition of `white space'.
+       (excluded_filename, memchr, sip, slurp): New declarations.
+       * ed.c (print_ed_hunk): Adjust to diff.h's struct changes.
+       * context.c (pr_context_hunk): Likewise.
+       * ifdef.c (print_ifdef_script): Likewise.
+       * side.c (print_sdiff_script, print_half_line): Likewise.
+       * util.c (analyze_hunk, line_cmp, print_1_line): Likewise.
+
+       * analyze.c (shift_boundaries): Remove unneeded variable `end' and
+       unnecessary comparisons of `preceding' and `other_preceding' against 0.
+       (diff_2_files): When comparing files byte-by-byte for equality,
+       don't slurp them all in at once; just compare them a buffer at a time.
+       This can win big if they differ early on.
+       Move some code to compare_files to enable this change.
+       Use only one buffer for stdin with `diff - -'.
+       (discard_confusing_lines, diff_2_files): Coalesce malloc/free calls.
+       (build_script): Remove obsolete OUTPUT_RCS code.
+
+       * diff.c (add_exclude, add_exclude_file, excluded_filename): New fns.
+       (main): Use them for the new --exclude and --exclude-from options.
+       (compare_files): Don't open a file unless it must be read.
+       Treat `diff file file' and `diff file dir' similarly.
+       Move some code here from diff_2_files to enable this.
+       Simplify file vs dir warning.
+
+       * dir.c (dir_sort): Support new --exclude* options.
+
+       * io.c (struct equivclass): Put hash code and line length here instead
+       of struct line_def, so that they can be shared.
+       (find_and_hash_each_line): Compute equivalence class as we go,
+       instead of doing it in a separate pass; this thrashes memory less.
+       Make buckets realloc-able, since we can't preallocate them.
+       Record one more line start than there are lines, so that we can compute
+       any line's length by subtracting its start from the next line's,
+       instead of storing the length explicitly.  This saves memory.
+       Move prefix-handling code to find_identical_ends;
+       this wins with large prefixes.
+       Use Is_space, not is_space, for consistent treatment of white space.
+       (prepare_text_end): New function.
+       (find_identical_ends): Move slurping here, so it's only done when
+       needed.  Work even if the buffers are the same (because of `diff - -').
+       Compare prefixes a word at a time for speed.
+       (find_equiv_class): Delete; now done by find_and_hash_each_line.
+       (read_files): Don't slurp unless needed.
+       find_equiv_class's work is now folded into find_and_hash_each_line.
+       Don't copy stdin buffer if `diff - -'.
+       Check for running out of primes.
+       (sip, slurp): Split first part of `slurp' into another function `sip'.
+       `sip' sets things up and perhaps reads the first ST_BLKSIZE buffer to
+       see whether the file is binary; `slurp' now just finishes the job.
+       This lets diff_2_files compare binary files lazily.
+       Allocate a one-word sentinel to allow word-at-a-time prefix comparison.
+       Count prefix lines only if needed, only count the first file's prefix.
+       Don't bother to count suffix lines; it's never needed.
+       Set up linbuf[0] to point at first differing line.
+       (binary_file_p): Change test for binary files:
+       if it has a null byte in its first buffer, it's binary.
+       (primes): Add more primes.
+
+       * util.c (line_cmp): Use bcmp for speed.
+       Use Is_space, not is_space, for consistent treatment of white space.
+       (translate_line_number): Internal line numbers now count from 0
+       starting after the prefix.
+       (memchr): New function (if memchr is missing).
+
+       * Makefile.in: Document HAVE_ST_BLKSIZE.  Link with wildmat.o.
+       * system.h (STAT_BLOCKSIZE): New macro based on HAVE_ST_BLKSIZE.
+       * configure.in: Add AC_ST_BLKSIZE.
+       * wildmat.c: New file.
+
+Fri Sep  4 01:28:51 1992  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * sdiff.c (xmalloc): Renamed from ck_malloc.  Callers changed.
+
+Thu Sep  3 15:28:59 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * diff.h: Don't declare free, index, rindex.
+
+Tue Aug 11 22:18:06 1992  John Gilmore  (gnu at cygnus.com)
+
+       * io.c (binary_file_p):  Use heuristic to avoid declaring info
+       files as binary files.  Allow about 1.5% non-printing
+       characters (in info's case, ^_).
+
+Tue Jul  7 01:09:26 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * diff.h: Replace function_regexp and ignore_regexp with lists
+       of compiled regexps.
+       * analyze.c, context.c, util.c: Test whether the lists, not
+       the old variables, are empty.
+       * util.c (analyze_hunk), context.c (find_function): Compare
+       lines with the lists of regexps.
+       * diff.c (add_regexp): New function.
+       (main): Use it.
+
+       * diff3: Add -v --version option.
+       * Makefile.in: Link with version.o.
+
+       * system.h: New file.
+       * diff.h, cmp.c, diff3.c, sdiff.c: Use it.
+
+       * diff.h, diff3.c: Include string.h or strings.h, as appropriate.
+       Declare malloc and realloc.
+
+       * diff3.c (perror_with_exit): Include program name in message.
+
+       * diff3.c: Lowercase error messages for GNU standards.
+
+       * sdiff.c [USG || STDC_HEADERS]: Define bcopy in terms of memcpy.
+
+       * sdiff.c: Use the version number from version.c.
+       * Makefile.in: Link with version.o.
+
+       * cmp.c error.c xmalloc.c: New files from textutils.
+       * Makefile.in: Add rules for them.
+
+       * diff.c (longopts): --unidirectional-new-file is like -P, not -N.
+       Rename --file-label to --label (leave old name, but undocumented).
+
+       * sdiff.c, diff.c (usage): Condense messages and fix some errors.
+
+       * diff3.c (main, usage): Add long-named options.
+
+Fri Jul  3 14:31:18 1992  David J. MacKenzie  (djm@nutrimat.gnu.ai.mit.edu)
+
+       * diff.h, diff3.c, sdiff.c: Change FOO_MISSING macros to HAVE_FOO.
+
+Thu Jun 25 16:59:47 1992  David J. MacKenzie  (djm@apple-gunkies.gnu.ai.mit.edu)
+
+       * diff.c: --reversed-ed -> --forward-ed.
+
+Wed Feb 26 12:17:32 1992  Paul Eggert  (eggert@yata.uucp)
+
+       * analyze.c, diff.c, diff.h, io.c: For -y, compare even if same file.
+
+Fri Feb 14 22:46:38 1992  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * io.c, diff3.c, analyze.c: Add extra parentheses.
+
+Sun Feb  9 00:22:42 1992  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
+
+       * diff.h (unidirectional_new_file_flag): New variable.
+       * diff.c (main): Set that for -P.
+       (compare_files): Support -P, somewhat like -N.
+       (longopts): Support long name for -P.
+
+Sat Jan  4 20:10:34 1992  Paul Eggert (eggert at yata.uucp)
+
+       * Makefile.in: Distribute diff.info-* too.
+
+       * README, sdiff.c: version number now matches version.c.
+
+       * configure: Fix and document vfork test.
+
+       * ifdef.c: Don't dump core if `diff -Dx f f'.
+
+Mon Dec 23 23:36:08 1991  David J. MacKenzie  (djm at wookumz.gnu.ai.mit.edu)
+
+       * diff.h, diff3.c, sdiff.c: Change POSIX ifdefs to
+       HAVE_UNISTD_H and _POSIX_VERSION.
+
+Wed Dec 18 17:00:31 1991  David J. MacKenzie  (djm at wookumz.gnu.ai.mit.edu)
+
+       * Makefile.in (srcs): Add sdiff.c.
+       (tapefiles): Add diff.texi and diff.info.
+
+       * diff.h, diff3.c, sdiff.c: Use HAVE_VFORK_H instead of
+       VFORK_HEADER and VFORK_WORKS.
+
+Tue Dec 17 00:02:59 1991  Paul Eggert  (eggert at yata.uucp)
+
+       * Makefile.in (all): Add diff.info, sdiff.
+
+       * configure, diff.c, sdiff.c:
+       Prefix long options with `--', not `+'.
+       * diff.c: Regularize option names.
+
+       * configure: Fix check for vfork.
+       * configure, diff.c, diff.h, diff3.c, sdiff.c:
+       Use Posix definitions when possible.
+
+       * context.c: Align context with tab if -T is given.  Tune.
+       * diff.c, diff.h, side.c: Calculate column widths so that tabs line up.
+       * io.c: Add distinction between white space and printing chars.
+       * side.c: Don't expand tabs unless -t is given.
+       * side.c, util.c: Tab expansion now knows about '\b', '\f', '\r', '\v'.
+       * util.c: -w skips all white space.  Remove lint.  Tune.
+
+       * sdiff.c: Support many more diff options, e.g. `-', `sdiff file dir'.
+       Ignore interrupts while the subsidiary editor is in control.
+       Clean up temporary file and kill subsidiary diff if interrupted.
+       Ensure subsidiary diff doesn't ignore SIGPIPE.
+       Don't get confused while waiting for two subprocesses.
+       Don't let buffers overflow.  Check for I/O errors.
+       Convert to GNU style.  Tune.
+
+       * sdiff.c, util.c: Don't lose errno.
+       Don't confuse sdiff with messages like `Binary files differ'.
+       * sdiff.c, side.c: Don't assume that common lines are identical.
+       Simplify --sdiff-merge-assist format.
+
+Mon Sep 16 16:42:01 1991  Tom Lord  (lord at churchy.gnu.ai.mit.edu)
+
+       * Makefile.in, sdiff.c: introduced sdiff front end to diff.
+
+       * Makefile.in, analyze.c, diff.c, diff.h, io.c, side.c: Added
+       sdiff-style output format to diff.
+
+Mon Aug 26 16:44:55 1991  David J. MacKenzie  (djm at pogo.gnu.ai.mit.edu)
+
+       * Makefile.in, configure: Only put $< in Makefile if using VPATH,
+       because older makes don't understand it.
+
+Fri Aug  2 12:22:30 1991  David J. MacKenzie  (djm at apple-gunkies)
+
+       * configure: Create config.status.  Remove it and Makefile if
+       interrupted while creating them.
+
+Thu Aug  1 22:24:31 1991  David J. MacKenzie  (djm at apple-gunkies)
+
+       * configure: Check for +srcdir etc. arg and look for
+       Makefile.in in that directory.  Set VPATH if srcdir is not `.'.
+       * Makefile.in: Get rid of $(archpfx).
+
+Tue Jul 30 21:28:44 1991  Richard Stallman  (rms at mole.gnu.ai.mit.edu)
+
+       * Makefile.in (prefix): Renamed from DESTDIR.
+
+Wed Jul 24 23:08:56 1991  David J. MacKenzie  (djm at wookumz.gnu.ai.mit.edu)
+
+       * diff.h, diff3.c: Rearrange ifdefs to use POSIX,
+       STDC_HEADERS, VFORK_MISSING, DIRENT.  This way it works on
+       more systems that aren't pure USG or BSD.
+       Don't not define const if __GNUC__ is defined -- that would
+       break with -traditional. 
+       * configure: Check for those features.
+
+Wed Jul 10 01:39:23 1991  David J. MacKenzie  (djm at wookumz.gnu.ai.mit.edu)
+
+       * configure, Makefile.in: $(INSTALLPROG) -> $(INSTALL).
+
+Sat Jul  6 16:39:04 1991  David J. MacKenzie  (djm at geech.gnu.ai.mit.edu)
+
+       * Replace Makefile with configure and Makefile.in.
+       Update README with current compilation instructions.
+
+Sat Jul  6 14:03:29 1991  Richard Stallman  (rms at mole.gnu.ai.mit.edu)
+
+       * util.c (setup_output): Just save the args for later use.
+       (begin_output): Do the real work, with the values that were saved.
+       It's safe to call begin_output more than once.
+       Print the special headers for context format here.
+       * analyze.c (diff_2_files): Don't print special headers here.
+       * context.c (pr_context_hunk, pr_unidiff_hunk): Call begin_output.
+       * ed.c (print_ed_hunk, print_forward_ed_hunk, print_rcs_hunk):
+       * normal.c (print_normal_hunk): Likewise.
+       * ifdef.c (print_ifdef_hunk): Likewise.
+       * util.c (finish_output): Don't die if begin_output was not called.
+
+Thu Jun 20 23:10:01 1991  David J. MacKenzie  (djm at geech.gnu.ai.mit.edu)
+
+       * Makefile: Add TAGS, distclean, and realclean targets.
+       Set SHELL.
+
+Tue Apr 30 13:54:36 1991  Richard Stallman  (rms at mole.gnu.ai.mit.edu)
+
+       * diff.h (TRUE, FALSE): Undefine these before defining.
+
+Thu Mar 14 18:27:27 1991  Richard Stallman  (rms@mole.ai.mit.edu)
+
+       * Makefile (objs): Include $(ALLOCA).
+
+Sat Mar  9 22:34:03 1991  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.h: Include regex.h.
+
+Thu Feb 28 18:59:53 1991  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * Makefile (diff3): Link with GNU getopt.
+
+Sat Feb 23 12:49:43 1991  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * io.c (find_equiv_class): Make hash code unsigned before mod.
+
+       * diff.h (files): Add EXTERN.
+
+Sun Jan 13 21:33:01 1991  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.c: +print option renamed +paginate.  Remove +all-text.
+
+Mon Jan  7 06:18:01 1991  David J. MacKenzie  (djm at geech.ai.mit.edu)
+
+       * Makefile (dist): New target, replacing diff.tar and
+       diff.tar.Z, to encode version number in distribution directory
+       and tar file names.
+
+Sun Jan  6 18:42:23 1991  Michael I Bushnell  (mib at geech.ai.mit.edu)
+
+       * Version 1.15 released.
+
+       * version.c: Updated from 1.15 alpha to 1.15
+
+       * context.c (print_context_number_range,
+       print_unidiff_number_range): Don't print N,M when N=M, print
+       just N instead.
+       
+       * README: Updated for version 1.15.
+       Makefile: Updated for version 1.15.
+
+       * diff3.c (main): Don't get confused if one of the arguments
+       is a directory.
+
+       * diff.c (compare_files): Don't get confused if comparing
+       standard input to a directory; print error instead.
+
+       * analyze.c (diff_2_files), context.c (print_context_header,
+       print_context_script), diff.c (main), diff.h (enum
+       output_style): Tread unidiff as an output style in its own
+       right.  This also generates an error when both -u and -c are
+       given.
+
+       * diff.c (main): Better error messages when regexps are bad.
+
+       * diff.c (compare_files): Don't assume stdin is opened.
+
+       * diff3.c (read_diff): Don't assume things about the order of
+       descriptor assignment and closes.
+
+       * util.c (setup_output): Don't assume things about the order
+       of descriptor assignment and closes. 
+
+       * diff.c (compare_files): Set a flag so that closes don't
+       happen more than once.
+
+       * diff.c (main): Don't just flush stdout, do a close.  That
+       way on broken systems we can still get errors. 
+
+Mon Dec 24 16:24:17 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.c (usage): Use = for args of long options.
+
+Mon Dec 17 18:19:20 1990  Michael I Bushnell  (mib at geech.ai.mit.edu)
+
+       * context.c (print_context_label): Labels were interchanged badly.
+
+       * context.c (pr_unidiff_hunk): Changes to deal with files
+       ending in incomplete lines.
+       * util.c (print_1_line): Other half of the changes.
+
+Mon Dec  3 14:23:55 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.c (longopts, usage): unidiff => unified.
+
+Wed Nov  7 17:13:08 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * analyze.c (diff_2_files): No warnings about newlines for -D.
+
+       * diff.c (pr_unidiff_hunk): Remove ref to output_patch_flag.
+
+Tue Oct 23 23:19:18 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.c (compare_files): For -D, compare even args are same file.
+       * analyze.c (diff_2_files): Likewise.
+       Also, output even if files have no differences.
+
+       * analyze.c (diff_2_files): Print missing newline messages last.
+       Return 2 if a newline is missing.
+       Print them even if files end with identical text.
+
+Mon Oct 22 19:40:09 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.c (usage): Return 2.
+
+Wed Oct 10 20:54:04 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.c (longopts): Add +new-files.
+
+Sun Sep 23 22:49:29 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * context.c (print_context_script): Handle unidiff_flag.
+       (print_context_header): Likewise.
+       (print_unidiff_number_range, pr_unidiff_hunk): New functions.
+       * diff.c (longopts): Add element for +unidiff.
+       (main): Handle +unidiff and -u.
+       (usage): Mention them.
+
+Wed Sep  5 16:33:22 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * io.c (find_and_hash_each_line): Deal with missing final newline
+       after buffering necessary context lines.
+
+Sat Sep  1 16:32:32 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * io.c (find_identical_ends): ROBUST_OUTPUT_FORMAT test was backward.
+
+Thu Aug 23 17:17:20 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff3.c (WIFEXITED): Undef it if WEXITSTATUS is not defined.
+       * context.c (find_function): Don't try to return values.
+
+Wed Aug 22 11:54:39 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * diff.h (O_RDONLY): Define if not defined.
+
+Tue Aug 21 13:49:26 1990  Richard Stallman  (rms at mole.ai.mit.edu)
+
+       * Handle -L option.
+       * context.c (print_context_label): New function.
+       (print_context_header): Use that.
+       * diff.c (main): Recognize the option.
+       (usage): Updated.
+       * diff.h (file_label): New variable.
+       * diff3.c (main): Recognize -L instead of -t.
+
+       * diff3.c (main): Support -m without other option.
+
+       * diff3.c (WEXITSTATUS, WIFEXITED): Define whenever not defined.
+
+       * diff3.c (bcopy, index, rindex): Delete definitions; not used.
+       (D_LINENUM, D_LINELEN): Likewise.
+       (struct diff_block): lengths includes newlines.
+       (struct diff3_block): Likewise.
+       (always_text, merge): New variables.
+       (read_diff): Return address of end, not size read.  Calls changed.
+       Pass -a to diff if given to diff3.
+       current_chunk_size now an int.  Detect error in `pipe'.
+       Check for incomplete line of output here.
+       (scan_diff_line): Don't make scan_ptr + 2 before knowing it is valid.
+       No need to check validity of diff output here.
+       Include newline in length of line.
+       (main): Compute rev_mapping here.  Handle -a and -m.
+       Error message if excess -t operands.  Error for incompatible options.
+       Error if `-' given more than once.
+       Fix error storing in tag_strings.
+       (output_diff3): REV_MAPPING is now an arg.  Call changed.
+       Change syntax of "missing newline" message.
+       Expect length of line to include newline.
+       (output_diff3_edscript): Return just 0 or 1.
+       REV_MAPPING is now an arg.  Call changed.
+       (output_diff3_merge): New function.
+       (process_diff): Better error message for bad diff format.
+       (fatal, perror_with_exit): Return status 2.
+
+       * analyze.c (diff_2_files): Report missing newline in either
+       or both files, if not robust output style.
+
+       * util.c (setup_output): Detect error from pipe.
+       No need to close stdin.
+
+       * util.c (print_1_line): Change format of missing-newline msg.
+       Change if statements to switch.
+
+       * io.c (slurp): Don't mention differences in final newline if -B.
+
+       * io.c (binary_file_p): Use ISO char set as criterion, not ASCII.
+
+       * io.c (find_identical_ends): Increase value of BEG0 by 1.
+       Other changes in backwards scan to avoid decrementing pointers
+       before start of array, and set LINES properly.
+
+       * diff.h (ROBUST_OUTPUT_STYLE): New macro.
+       * io.c (find_identical_ends, find_and_hash_each_line): Use that macro.
+
+       * diff.h (dup2): Don't define if XENIX.
+
+       * diff.c (main): Check for write error at end.
+
+       * context.c (find_function): Don't return a value.
+       Use argument FILE rather than global files.
+
+       * analyze.c: Add external function declarations.
+       * analyze.c (build_script): Turn off explicit check for final newline.
+
+       * analyze.c (discard_confusing_lines): Make integers unsigned.
+
+Tue Jul 31 21:37:16 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * io.c (find_and_hash_each_line): Correct the criterion 
+       for leaving out the newline from the end of the line.
+
+Tue May 29 21:28:16 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * dir.c (diff_dirs): Free things only if nonzero.
+
+Mon Apr 16 18:31:05 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.h (NDIR_IN_SYS): New macro controls location of ndir.h.
+
+       * diff3.c (xmalloc, xrealloc): Don't die if size == 0 returns 0.
+
+Sun Mar 25 15:58:42 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * analyze.c (discard_confusing_lines):
+       `many' wasn't being used; use it.
+       Cancelling provisionals near start of run must handle already
+       cancelled provisionals.
+       Cancelling subruns of provisionals was cancelling last nonprovisional.
+
+Sat Mar 24 14:02:51 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * analyze.c (discard_confusing_lines):
+       Threshold for line occurring many times scales by square root
+       of total lines.
+       Within each run, cancel any long subrun of provisionals.
+       Don't update `provisional' while cancelling provisionals.
+       In big outer loop, handle provisional and nonprovisional separately.
+
+Thu Mar 22 16:35:33 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * analyze.c (discard_confusing_lines):
+       The first loops to discard provisionals from ends failed to step.
+       In second such loops, keep discarding all consecutive provisionals.
+       Increase threshold for stopping discarding, and also check for
+       consecutive nondiscardables as separate threshold.
+
+Fri Mar 16 00:33:08 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff3.c (read_diff): Pass -- as first arg to diff.
+
+       * diff3.c: Include wait.h or define equivalent macros.
+       (read_diff): Don't use stdio printing error in the inferior.
+       Remember the pid and wait for it.  Report failing status.
+       Report failure of vfork.
+
+Sun Mar 11 17:10:32 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff3.c (main): Accept -t options and pass to output_diff3_edscript.
+       (usage): Mention -t.
+       (read_diff): Use vfork.
+       (vfork): Don't use it on Sparc.
+
+       * diff.h (vfork): Don't use it on Sparc.
+
+Tue Mar  6 22:37:20 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff3.c (dup2): Don't define on Xenix.
+
+       * Makefile: Comments for Xenix.
+
+Thu Mar  1 17:19:23 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * analyze.c (diff_2_files): `message' requires three args.
+
+Fri Feb 23 10:56:50 1990  David J. MacKenzie  (djm at albert.ai.mit.edu)
+
+       * diff.h, util.c, diff3.c: Change 'void *' to 'VOID *', with
+       VOID defined as void if __STDC__, char if not.
+
+Sun Feb 18 20:31:58 1990  David J. MacKenzie  (djm at albert.ai.mit.edu)
+
+       * Makefile: Add rules for getopt.c, getopt1.c, getopt.h.
+
+       * getopt.c, getopt.h, getopt1.c: New files.
+
+       * main.c (main, usage): Add long options.
+
+       * analyze.c (shift_boundaries): Remove unused var 'j_end'.
+
+Thu Feb  8 02:43:16 1990  Jim Kingdon  (kingdon at pogo.ai.mit.edu)
+
+       * GNUmakefile: include ../Makerules before Makefile.
+
+Fri Feb  2 23:21:38 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * analyze.c (diif_2_files): If -B or -I, don't return 1
+       if all changes were ignored.
+
+Wed Jan 24 20:43:57 1990  Richard Stallman  (rms at albert.ai.mit.edu)
+
+       * diff3.c (fatal): Output to stderr.
+
+Thu Jan 11 00:25:56 1990  David J. MacKenzie  (djm at hobbes.ai.mit.edu)
+
+       * diff.c (usage): Mention -v.
+
+Wed Jan 10 16:06:38 1990  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff3.c (output_diff3_edscript): Return number of overlaps.
+       (main): If have overlaps, exit with status 1.
+
+Sun Dec 24 10:29:20 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * io.c (find_equiv_class): Fix typo that came from changing init of B
+       to an assigment.
+
+       * version.c: New file.
+       * diff.c (main): -v prints version number.
+
+       * io.c (binary_file_p): Null char implies binary file.
+
+Fri Nov 17 23:44:55 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * util.c (print_1_line): Fix off by 1 error.
+
+Thu Nov 16 13:51:10 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * util.c (xcalloc): Function deleted.
+
+       * io.c (slurp): Null-terminate the buffer.
+
+       * io.c (read_files): Delete unused vars.
+
+       * io.c (find_equiv_class): Don't index by N if too low.
+
+       * dir.c (dir_sort): Delete the extra declaration of compare_names.
+
+       * diff.h: Don't declare xcalloc.  Declare some other functions.
+
+       * analyze.c (shift_boundaries):
+       Test for END at end of range before indexing by it.
+       Fix typo `preceeding' in var names.
+
+Sat Nov 11 14:04:16 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff3.c (using_to_diff3_block): Delete unused vars.
+       (make_3way_diff, process_diff_control, read_diff, output_diff3): Likewise.
+
+Mon Nov  6 18:15:50 EST 1989 Jay Fenlason (hack@ai.mit.edu)
+
+       * README Fix typo.
+
+Fri Nov  3 15:27:47 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.c (usage): Mention -D. 
+
+       * ifdef.c (print_ifdef_hunk): Write comments on #else and #endif.
+
+Sun Oct 29 16:41:07 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.c (compare_files): Don't fflush for identical files.
+
+Wed Oct 25 17:57:12 1989  Randy Smith  (randy at apple-gunkies.ai.mit.edu)
+
+       * diff3.c (using_to_diff3_block): When defaulting lines from
+       FILE0, only copy up to just under the *lowest* line mentioned
+       in the next diff.
+
+       * diff3.c (fatal): Add \n to error messages.
+
+Wed Oct 25 15:05:49 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * Makefile (tapefiles): Add ChangeLog.
+
+Tue Oct  3 00:51:17 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff3.c (process_diff, create_diff3_block): Init ->next field.
+
+Fri Sep 29 08:16:45 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * util.c (line_cmp): Alter end char of line 2, not line 1.
+
+Wed Sep 20 00:12:37 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * Makefile (diff.tar): Expect ln to fail on some files;
+       copy them with cp.
+
+Mon Sep 18 02:54:29 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * Handle -D option:
+       * io.c (find_and_hash_each_line): Keep all lines of 1st file.
+       * diff.c (main): Handle -D option.
+       (compare_files): Reject -D if files spec'd are directories.
+       * analyze.c (diff_2_files): Handle OUTPUT_IFDEF case.
+
+Fri Sep  1 20:15:50 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.c (option_list): Rename arg VECTOR as OPTIONVEC.
+
+Mon Aug 28 17:58:27 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.c (compare_files): Clear entire inf[i].stat.
+
+Wed Aug 23 17:48:47 1989  Richard Stallman  (rms at apple-gunkies.ai.mit.edu)
+
+       * io.c (find_identical_ends): Sign was backward
+       determining where to bound the scan for the suffix.
+
+Wed Aug 16 12:49:16 1989  Richard Stallman  (rms at hobbes.ai.mit.edu)
+
+       * analyze.c (diff_2_files): If -q, treat all files as binary.
+       * diff.c (main): Detect -q, record in no_details_flag.
+
+Sun Jul 30 23:12:00 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.c (usage): New function.
+       (main): Call it.
+
+Wed Jul 26 02:02:19 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.c (main): Make -C imply -c.
+
+Thu Jul 20 17:57:51 1989  Chris Hanson  (cph at kleph)
+
+       * io.c (find_and_hash_each_line): Bug fix in context handling,
+       introduced by last change.
+
+Fri Jul 14 17:39:20 1989  Chris Hanson  (cph at kleph)
+
+       * analyze.c: To make RCS work correctly on files that don't
+       necessarily end in newline, introduce some changes that cause
+       diffs to be sensitive to missing final newline.  Because
+       non-RCS modes don't want to be affected by these changes, they
+       are conditional on `output_style == OUTPUT_RCS'.
+       (diff_2_files) [OUTPUT_RCS]: Suppress the "File X missing
+       newline" message.
+       (build_script) [OUTPUT_RCS]: Cause the last line to compare as
+       different if exactly one of the files is missing its final
+       newline.
+
+       * io.c (find_and_hash_each_line): Bug fix in
+       ignore_space_change mode.  Change line's length to include the
+       newline.  For OUTPUT_RCS, decrement last line's length if
+       there is no final newline.
+       (find_identical_ends) [OUTPUT_RCS]: If one of the files is
+       missing a final newline, make sure it's not included in either
+       the prefix or suffix.
+
+       * util.c (print_1_line): Change line output routine to account
+       for line length including the newline.
+
+Tue Jun 27 02:35:28 1989  Roland McGrath  (roland at hobbes.ai.mit.edu)
+
+       * Makefile: Inserted $(archpfx) where appropriate.
+
+Wed May 17 20:18:43 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff3.c [USG]: Include fcntl.h.
+
+       * diff.h [USG]: New compilation flags HAVE_NDIR, HAVE_DIRECT.
+
+Wed Apr 26 15:35:57 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * dir.c (diff_dirs): Two new args, NONEX1 and NONEX2, say to pretend
+       nonex dirs are empty.
+       (dir_sort): New arg NONEX, likewise.
+       * diff.c (compare_files): Pass those args.
+       Sometimes call diff_dirs if subdir exists in just one place.
+
+Wed Apr 12 01:10:27 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * io.c (find_identical_ends): Set END0 *after* last char
+       during backward scan for suffix.
+
+Sat Apr  8 15:49:49 1989  Randall Smith  (randy at apple-gunkies.ai.mit.edu)
+
+       * diff3.c (using_to_diff3_block): Now find high marks in files 1
+       and 2 through mapping off of the last difference instead of the
+       first.
+
+       * diff3.c: Many trivial changes to spelling inside comments. 
+
+Fri Feb 24 12:38:03 1989  Randall Smith  (randy at gluteus.ai.mit.edu)
+
+       * util.c, normal.c, io.c, ed.c, dir.c, diff.h, diff.c, context.c,
+       analyze.c, Makefile: Changed copyright header to conform with new
+       GNU General Public license.
+       * diff3.c: Changed copyright header to conform with new GNU
+       General Public license.
+       * COPYING: Made a hard link to /gp/rms/COPYING.
+
+Fri Feb 24 10:01:58 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * io.c (slurp): Leave 2 chars space at end of buffer, not one.
+       (find_identical_ends): Special case if either file is empty;
+       don't try to make a sentinel since could crash.
+
+Wed Feb 15 14:24:48 1989  Jay Fenlason  (hack at apple-gunkies.ai.mit.edu)
+
+       * diff3.c (message)  Re-wrote routine to avoid using alloca()
+
+Wed Feb 15 06:19:14 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * io.c (find_identical_ends): Delete the variable `bytes'.
+
+Sun Feb 12 11:50:36 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * io.c (slurp): ->bufsize is nominal amount we have room for;
+       add room for sentinel when calling xmalloc or xrealloc.
+
+       * io.c (find_identical_ends): Do need overrun check in finding suffix.
+
+Fri Feb 10 01:28:15 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.c (main): -C now takes arg to specify context length.
+       Now -p to show C function name--Damned IEEE!
+       Fatal error if context length spec'd twice.
+
+       * ed.c (print_ed_hunk): Now special treatment only for lines containing
+       precisely a dot and nothing else.  Output `..', end the insert,
+       substitute that one line, then resume the insert if nec.
+
+       * io.c (find_and_hash_lines): When backing up over starting context,
+       don't move past buffer-beg.
+
+       * io.c (find_identical_ends): Use sentinels to make the loops faster.
+       If files are identical, skip the 2nd loop and return quickly.
+       (slurp): Leave 1 char extra space after each buffer.
+
+       * analyze.c (diff_2_files): Mention difference in final newlines.
+
+Wed Jan 25 22:44:44 1989  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * dir.c (diff_dirs): Use * when calling fcn ptr variable.
+
+Sat Dec 17 14:12:06 1988  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * Makefile: New vars INSTALL and LIBS used in some rules;
+       provide default defns plus commented-put defns for sysV.
+
+Thu Nov 17 16:42:53 1988  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * dir.c (dir_sort): Open-trouble not fatal; just say # files is -1.
+       (diff_dirs): If dir_sort does that, give up and return 2.
+
+       * diff.c (compare_files): Don't open directories.
+       Don't close them specially either.
+       Cross-propagate inf[i].dir_p sooner.
+
+Sun Nov 13 11:19:36 1988  Richard Stallman  (rms at sugar-bombs.ai.mit.edu)
+
+       * diff.h: Declare index, rindex.
+
+       * diff.c (compare_files): If comparing foodir with b/f,
+       use foodir/f, not foodir/b/f.
+
+       * diff.c (compare_files): Don't print "are identical" msg for 2 dirs.
+       Status now 1 if one file is a dir and the other isn't, etc.
+
+Thu Nov  3 16:30:24 1988  Randall Smith  (randy at gluteus.ai.mit.edu)
+
+       * Makefile: Added a define for diff3 to define DIFF_PROGRAM.
+
+       * util.c: Added hack to make sure that perror was not called with
+       a null pointer.
+
+       * diff.c: Changed S_IFDIR to S_IFMT in masking type of file bits
+       out. 
+
+       * diff3.c: Included USG compatibility defines.
+
+       * diff.h: Moved sys/file.h into #else USG section (not needed or
+       wanted on System V).
+
+       * ed.c, analyze.c, context.c: Shortened names to 12 characters for
+       the sake of System V (too simple not to do).
+\f
+Local Variables:
+mode: indented-text
+left-margin: 8
+version-control: never
+End:
diff --git a/Src/COPYING b/Src/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       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/Src/ChildFrm.cpp b/Src/ChildFrm.cpp
new file mode 100644 (file)
index 0000000..cfe2953
--- /dev/null
@@ -0,0 +1,206 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// ChildFrm.cpp : implementation of the CChildFrame class
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+
+#include "ChildFrm.h"
+#include "DiffView.h"
+#include "MainFrm.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CChildFrame
+
+IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)
+
+BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
+       //{{AFX_MSG_MAP(CChildFrame)
+       ON_WM_CREATE()
+       ON_WM_CLOSE()
+       ON_WM_SIZE()
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+static UINT indicators[] =
+{
+       ID_SEPARATOR
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CChildFrame construction/destruction
+
+CChildFrame::CChildFrame()
+{
+       m_bActivated = FALSE;
+}
+
+CChildFrame::~CChildFrame()
+{
+}
+
+BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
+       CCreateContext* pContext)
+{
+       //lpcs->style |= WS_MAXIMIZE;
+       // create a splitter with 1 row, 2 columns
+       if (!m_wndSplitter.CreateStatic(this, 1, 2)) //, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL))
+       {
+               TRACE0("Failed to CreateStaticSplitter\n");
+               return FALSE;
+       }
+       
+       // add the first splitter pane - the default view in column 0
+       
+       // add the first splitter pane - the default view in column 0
+       //int width=theApp.GetProfileInt(_T("Settings"), _T("WLeft"), 0);
+       //if (width<=0)
+       //      width = rc.Width()/2;
+
+       if (!m_wndSplitter.CreateView(0, 0,
+               RUNTIME_CLASS(CDiffView), CSize(-1, 200), pContext))
+       {
+               TRACE0("Failed to create first pane\n");
+               return FALSE;
+       }
+       
+       // add the second splitter pane - an input view in column 1
+       if (!m_wndSplitter.CreateView(0, 1,
+               RUNTIME_CLASS(CDiffView), CSize(-1, 200), pContext))
+       {
+               TRACE0("Failed to create second pane\n");
+               return FALSE;
+       }
+       
+       mf->m_pLeft = (CDiffView *)m_wndSplitter.GetPane(0,0);
+       //mf->m_pLeft->OnInitialUpdate();
+       mf->m_pLeft->m_bIsLeft=TRUE;
+       mf->m_pRight = (CDiffView *)m_wndSplitter.GetPane(0,1);
+       //mf->m_pRight->OnInitialUpdate();
+       mf->m_pRight->m_bIsLeft=FALSE;
+       mf->m_pLeft->UpdateWindow();
+
+       CRect rc;
+       GetClientRect(&rc);
+       m_wndSplitter.SetColumnInfo(0, rc.Width()/2, 10);
+       m_wndSplitter.RecalcLayout();
+       return TRUE;
+}
+
+BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
+{
+
+       return CMDIChildWnd::PreCreateWindow(cs);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CChildFrame diagnostics
+
+#ifdef _DEBUG
+void CChildFrame::AssertValid() const
+{
+       CMDIChildWnd::AssertValid();
+}
+
+void CChildFrame::Dump(CDumpContext& dc) const
+{
+       CMDIChildWnd::Dump(dc);
+}
+
+#endif //_DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+// CChildFrame message handlers
+
+int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
+{
+       if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
+               return -1;
+       
+       //MDIMaximize();
+       return 0;
+}
+
+void CChildFrame::ActivateFrame(int nCmdShow) 
+{
+    if (!m_bActivated) 
+    {
+        m_bActivated = TRUE;
+               if(theApp.GetProfileInt(_T("Settings"), _T("LeftMax"), TRUE))
+                       nCmdShow = SW_SHOWMAXIMIZED;
+               else
+                       nCmdShow = SW_SHOWNORMAL;
+       CRect rc;
+       GetClientRect(&rc);
+       m_wndSplitter.SetColumnInfo(0, rc.Width()/2, 10);
+       m_wndSplitter.RecalcLayout();
+    }
+       CMDIChildWnd::ActivateFrame(nCmdShow);
+}
+
+BOOL CChildFrame::DestroyWindow() 
+{
+       
+       return CMDIChildWnd::DestroyWindow();
+}
+
+void CChildFrame::SavePosition()
+{
+       CRect rc;
+       CWnd* pLeft = m_wndSplitter.GetPane(0,0);
+       if (pLeft != NULL)
+       {
+               pLeft->GetWindowRect(&rc);
+               theApp.WriteProfileInt(_T("Settings"), _T("WLeft"), rc.Width());
+       }
+       WINDOWPLACEMENT wp;
+       GetWindowPlacement(&wp);
+       theApp.WriteProfileInt(_T("Settings"), _T("LeftMax"), (wp.showCmd == SW_MAXIMIZE));
+}
+
+void CChildFrame::OnClose() 
+{
+       SavePosition(); 
+       CMDIChildWnd::OnClose();
+}
+
+void CChildFrame::OnSize(UINT nType, int cx, int cy) 
+{
+       CMDIChildWnd::OnSize(nType, cx, cy);
+       
+       CRect rc;
+       GetClientRect(&rc);
+#ifndef _DEBUG
+       if(IsWindow(m_wndSplitter)
+                       && m_wndSplitter.GetPane(0,0)!=NULL) 
+               {
+                       m_wndSplitter.SetColumnInfo(0, rc.Width()/2, 10);
+                       m_wndSplitter.RecalcLayout();
+               }
+#endif         
+}
diff --git a/Src/ChildFrm.h b/Src/ChildFrm.h
new file mode 100644 (file)
index 0000000..cafcd1d
--- /dev/null
@@ -0,0 +1,80 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// ChildFrm.h : interface of the CChildFrame class
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_CHILDFRM_H__BBCD4F8E_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
+#define AFX_CHILDFRM_H__BBCD4F8E_34E4_11D1_BAA6_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+class CChildFrame : public CMDIChildWnd
+{
+       DECLARE_DYNCREATE(CChildFrame)
+public:
+       CChildFrame();
+
+// Attributes
+protected:
+       CSplitterWnd m_wndSplitter;
+public:
+
+// Operations
+public:
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CChildFrame)
+       public:
+       virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
+       virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
+       virtual void ActivateFrame(int nCmdShow = -1);
+       virtual BOOL DestroyWindow();
+       //}}AFX_VIRTUAL
+
+// Implementation
+public:
+       void SavePosition();
+       virtual ~CChildFrame();
+#ifdef _DEBUG
+       virtual void AssertValid() const;
+       virtual void Dump(CDumpContext& dc) const;
+#endif
+
+// Generated message map functions
+protected:
+       BOOL m_bActivated;
+       //{{AFX_MSG(CChildFrame)
+       afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+       afx_msg void OnClose();
+       afx_msg void OnSize(UINT nType, int cx, int cy);
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_CHILDFRM_H__BBCD4F8E_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
diff --git a/Src/Diff.cpp b/Src/Diff.cpp
new file mode 100644 (file)
index 0000000..5e4f6c1
--- /dev/null
@@ -0,0 +1,474 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+
+/* GNU DIFF main routine.
+   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.  */
+
+/* GNU DIFF was written by Mike Haertel, David Hayes,
+   Richard Stallman, Len Tower, and Paul Eggert.  */
+
+#include "stdafx.h"
+#define GDIFF_MAIN
+#include "diff.h" 
+#include "getopt.h"
+#include "fnmatch.h"
+#include "io.h"
+#include "DirDoc.h"
+#include "logfile.h"
+
+extern int diff_dirs (CDiffContext*, int);
+
+
+#ifndef DEFAULT_WIDTH
+#define DEFAULT_WIDTH 130
+#endif
+
+#ifndef GUTTER_WIDTH_MINIMUM
+#define GUTTER_WIDTH_MINIMUM 3
+#endif
+
+
+extern CLogFile gLog;
+extern bool gWriteLog;
+
+int compare_files (LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, CDiffContext*, int);
+int excluded_filename (char const *f);
+
+/* Nonzero for -r: if comparing two directories,
+   compare their common subdirectories recursively.  */
+
+int recursive;
+
+/* For debugging: don't do discard_confusing_lines.  */
+
+static char const **exclude;
+static int exclude_alloc, exclude_count;
+
+int
+excluded_filename (char const *f)
+{
+  int i;
+  for (i = 0;  i < exclude_count;  i++)
+    if (fnmatch (exclude[i], f, 0) == 0)
+      return 1;
+  return 0;
+}
+
+static char const *
+filetype (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 popular file types */
+  /* S_ISLNK is impossible with `stat'.  */
+#ifdef S_ISSOCK
+  if (S_ISSOCK (st->st_mode)) return "socket";
+#endif
+
+  return "weird file";
+}
+
+
+
+int FileIsBinary(int fd)
+{
+    int bResult=0;
+    int cnt;
+    char buf[40];
+    long prevpos = tell(fd);
+       lseek(fd, 0L, SEEK_SET);
+    if (cnt=read(fd, buf, 40))
+    {
+               for (register int i=0; i < cnt; i++)
+               {
+                       if (!isprint(buf[i])
+                       && !isspace(buf[i])
+                       && buf[i] != NULL)
+                       {
+                       bResult=1;
+                       break;
+                       }
+               }
+    }
+    lseek(fd, prevpos, SEEK_SET);
+    return bResult;
+}
+
+
+/* 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.  */
+
+int
+compare_files (LPCTSTR dir0, LPCTSTR name0, 
+              LPCTSTR dir1, LPCTSTR name1, 
+              CDiffContext *pCtx, int depth)
+{
+  struct file_data inf[2];
+  register int i;
+  int val;
+  int same_files;
+  int failed = 0;
+  LPTSTR 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 (gWriteLog) gLog.Write(_T("Comparing: n0=%s, n1=%s, d0=%s, d1=%s"), name0, name1, dir0, dir1);
+
+  if (! ((name0 != 0 && name1 != 0)
+        || (unidirectional_new_file_flag && name1 != 0)
+        || entire_new_file_flag))
+    {
+      LPCTSTR name = name0 == 0 ? name1 : name0;
+      LPCTSTR 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").  */
+      pCtx->AddDiff(name, dir0, dir1, name0 == 0 ? FILE_RUNIQUE : FILE_LUNIQUE);
+      if (gWriteLog) gLog.Write(_T("\tUnique\r\n"));
+      return 1;
+    }
+
+  /* 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++)
+    {
+      bzero (&inf[i].stat, sizeof (struct stat));
+      inf[i].dir_p = 0;
+
+      if (inf[i].desc != -1)
+       {
+         int stat_result;
+
+         if (i && stricmp (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 = strrchr (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;
+      pCtx->AddDiff(name0, dir0, dir1, FILE_ERROR);
+    }
+#if defined(__MSDOS__) || defined(__NT__) || defined(WIN32)
+  else if (same_files = 0) /* yes, only ONE equal sign intended! hmo11apr93 */
+    ;
+#else
+  else if ((same_files =    inf[0].stat.st_ino == inf[1].stat.st_ino
+                        && inf[0].stat.st_dev == inf[1].stat.st_dev
+                        && inf[0].stat.st_size == inf[1].stat.st_size
+                        && inf[0].desc != -1
+                        && inf[1].desc != -1)
+          && 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;
+      pCtx->AddDiff(name0, dir0, dir0, FILE_SAME);
+    }
+#endif /*__MSDOS__||__NT__*/
+  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
+       {
+         if (gWriteLog) gLog.Write(_T("\tDirs found.\r\n"));
+
+         CDiffContext ctx(inf[0].name, inf[1].name, *pCtx);
+         val = diff_dirs (&ctx, 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)))
+         {
+                 if (gWriteLog) gLog.Write(_T("\tDirs found.\r\n"));
+                 CDiffContext ctx(inf[0].name, inf[1].name, *pCtx);
+                 val = diff_dirs (&ctx, 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;
+         pCtx->AddDiff(name0, dir0, dir1, FILE_DIFF);
+       }
+    }
+  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;
+      pCtx->AddDiff(name0, dir0, dir1, FILE_DIFF);
+    }
+  else
+    {
+      /* Both exist and neither is a directory.  */
+      int o_binary = always_text_flag ? O_BINARY : 0;
+      /* Open the files and record their descriptors.  */
+
+      if (inf[0].desc == -2)
+       if ((inf[0].desc = open (inf[0].name, O_RDONLY|o_binary, 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|o_binary, 0)) < 0)
+         {
+           perror_with_name (inf[1].name);
+           failed = 1;
+       }
+       
+       bool bBinary = (FileIsBinary(inf[0].desc)
+               || FileIsBinary(inf[1].desc));
+       if (bBinary)
+       {
+           if (inf[0].stat.st_size != inf[1].stat.st_size
+               || inf[0].stat.st_mtime != inf[1].stat.st_mtime)
+           {
+               val = 1;
+               pCtx->AddDiff(name0, dir0, dir1, FILE_BINDIFF);
+                   if (gWriteLog) gLog.Write(_T("\tbinary.\r\n"));
+           }
+           else
+           {
+               pCtx->AddDiff(name0, dir0, dir1, FILE_ERROR);
+               if (gWriteLog) gLog.Write(_T("\t%s.\r\n"), val==2? "error":"different");
+           }
+       }
+       else
+       {
+           /* Compare the files, if no error was found.  */
+           struct change *e, *p;
+           struct change *script=NULL;
+           
+           script = diff_2_files (inf, depth);
+           val = script? 1 : 0;
+           /* Close the file descriptors.  */
+           // cleanup the script
+           for (e = script; e; e = p)
+           {
+               p = e->link;
+               free (e);
+           }
+               cleanup_file_buffers(inf);
+           
+           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;
+           }
+           if (val==2 || val == 1)
+           {
+               pCtx->AddDiff(name0, dir0, dir1, val==2? FILE_ERROR:FILE_DIFF);
+               if (gWriteLog) gLog.Write(_T("\t%s.\r\n"), val==2? "error":"different");
+           }
+       }
+    }
+
+  /* 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);
+      pCtx->AddDiff(name0, dir0, dir1, FILE_SAME);
+      if (gWriteLog) gLog.Write(_T("\tidentical.\r\n"));
+   }
+  else
+  {
+    fflush (stdout);
+  }
+
+  if (free0)
+    free (free0);
+  if (free1)
+    free (free1);
+
+
+  return val;
+}
diff --git a/Src/DiffContext.cpp b/Src/DiffContext.cpp
new file mode 100644 (file)
index 0000000..a4840db
--- /dev/null
@@ -0,0 +1,85 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// DiffContext.cpp: implementation of the CDiffContext class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "merge.h"
+#include "DiffContext.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CDiffContext::CDiffContext(LPCTSTR pszLeft /*=NULL*/, LPCTSTR pszRight /*=NULL*/)
+{
+       m_bRecurse=FALSE;
+       m_strLeft = pszLeft;
+       m_strRight = pszRight;
+       m_pList = &m_dirlist;
+
+       pNamesLeft = NULL;
+       pNamesRight = NULL;
+}
+
+CDiffContext::CDiffContext(LPCTSTR pszLeft, LPCTSTR pszRight, CDiffContext& src)
+{
+       m_bRecurse=src.m_bRecurse;
+       m_strLeft = pszLeft;
+       m_strRight = pszRight;
+       m_pList = src.m_pList;
+       SetRegExp(src.m_strRegExp);
+
+       pNamesLeft = NULL;
+       pNamesRight = NULL;
+}
+
+CDiffContext::~CDiffContext()
+{
+       if (pNamesLeft != NULL)
+               free(pNamesLeft);
+       if (pNamesRight != NULL)
+               free(pNamesRight);
+}
+       
+
+void CDiffContext::AddDiff(LPCTSTR pszFilename, LPCTSTR pszLeftDir, LPCTSTR pszRightDir, BYTE code)
+{
+       DIFFITEM di;
+       strcpy(di.filename,pszFilename);
+       strcpy(di.lpath, pszLeftDir);
+       strcpy(di.rpath, pszRightDir);
+       di.code = code;
+       m_pList->AddTail(di);
+}
+
+void CDiffContext::SetRegExp(LPCTSTR pszExp)
+{
+       m_strRegExp = pszExp;
+       m_rgx.RegComp( pszExp );
+}
diff --git a/Src/DiffContext.h b/Src/DiffContext.h
new file mode 100644 (file)
index 0000000..990b30b
--- /dev/null
@@ -0,0 +1,55 @@
+// DiffContext.h: interface for the CDiffContext class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_DIFFCONTEXT_H__D3CC86BE_F11E_11D2_826C_00A024706EDC__INCLUDED_)
+#define AFX_DIFFCONTEXT_H__D3CC86BE_F11E_11D2_826C_00A024706EDC__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "RegExp.h"
+
+struct dirdata
+{
+  char const **names;  /* Sorted names of files in dir, 0-terminated.  */
+  char *data;  /* Allocated storage for file names.  */
+};
+
+typedef struct tagDIFFITEM {
+       TCHAR filename[_MAX_FNAME+_MAX_EXT];
+       TCHAR lpath[MAX_PATH], rpath[MAX_PATH];
+       BYTE code;
+}DIFFITEM;
+
+#define FILE_LUNIQUE           0
+#define FILE_RUNIQUE           1
+#define FILE_DIFF              2
+#define FILE_SAME              3
+#define FILE_ERROR             4
+#define FILE_BINDIFF   5
+
+
+class CDiffContext  
+{
+public:
+       void SetRegExp(LPCTSTR pszExp);
+       void AddDiff(LPCTSTR pszFilename, LPCTSTR pszLeftDir, LPCTSTR pszRightDir, BYTE code);
+       CDiffContext(LPCTSTR pszLeft = NULL, LPCTSTR pszRight = NULL);
+       CDiffContext(LPCTSTR pszLeft, LPCTSTR pszRight, CDiffContext& src);
+       virtual ~CDiffContext();
+
+       CList<DIFFITEM,DIFFITEM> m_dirlist, *m_pList;
+       BOOL m_bRecurse;
+       CString m_strLeft;
+       CString m_strRight;
+       CRegExp m_rgx;
+       CString m_strRegExp;
+
+       struct dirdata ddLeft, ddRight;
+       char *pNamesLeft;
+       char *pNamesRight;
+};
+
+#endif // !defined(AFX_DIFFCONTEXT_H__D3CC86BE_F11E_11D2_826C_00A024706EDC__INCLUDED_)
diff --git a/Src/Dir.cpp b/Src/Dir.cpp
new file mode 100644 (file)
index 0000000..caa8c4c
--- /dev/null
@@ -0,0 +1,207 @@
+/* Read, sort and compare two directories.  Used 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.  */
+
+#include "stdafx.h"
+#include "diff.h"
+#include "RegExp.h"
+#include "direct.h"
+#include "DiffContext.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.  */
+
+int compare_files (LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, CDiffContext*, int);
+
+static int compare_names (void const *, void const *);
+static int dir_sort (LPCTSTR pszDir, struct dirdata *dirdata, CRegExp& rgx);
+
+
+static int
+dir_sort (LPCTSTR pszDir, struct dirdata *dirdata, CRegExp& rgx)
+{
+       register int i;
+       WIN32_FIND_DATA fd;
+       HANDLE hff;
+       CString s;
+       
+       /* 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;
+       
+       /* Open the directory and check for errors.  */
+       //register DIR *reading = opendir (pszDir);
+       //if (!reading)
+       //      return -1;
+       s.Format(_T("%s\\*.*"), pszDir);
+       if ((hff=FindFirstFile(s, &fd)) == INVALID_HANDLE_VALUE)
+               return -1;
+       
+       /* Initialize the table of filenames.  */
+       struct stat sb;
+       stat(pszDir, &sb);
+       
+       data_alloc = max (1, (size_t) sb.st_size);
+       data_used = 0;
+       dirdata->data = data = (char*)xmalloc (data_alloc);
+       
+       /* Read the directory entries, and insert the subfiles
+       into the `data' table.  */
+       
+       do
+       {
+               char *d_name = fd.cFileName;
+               size_t d_size;
+               
+               /* 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)
+                       || (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && rgx.RegFind(d_name)==-1))
+                       continue;
+               
+               d_size = strlen (d_name) + 1;
+               while (data_alloc < data_used + d_size)
+                       dirdata->data = data = (char*)xrealloc (data, data_alloc *= 2);
+               memcpy (data + data_used, d_name, d_size);
+               data_used += d_size;
+               nnames++;
+
+       } while (FindNextFile(hff, &fd));
+
+       FindClose(hff);
+
+       
+       /* 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 (void const *file1, void const *file2)
+{
+       return stricmp (* (char const *const *) file1, * (char const *const *) file2);
+}
+\f
+/* 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 (CDiffContext *pCtx, int depth)
+{
+       int val = 0;                    /* Return value.  */
+
+       /* Get sorted contents of both dirs.  */
+       if (dir_sort (pCtx->m_strLeft, &pCtx->ddLeft, pCtx->m_rgx) != 0)
+       {
+               val = 2;
+       }
+       if (dir_sort (pCtx->m_strRight, &pCtx->ddRight, pCtx->m_rgx) != 0)
+       {
+               val = 2;
+       }
+               
+       if (val == 0)
+       {
+               char const * const *names0 = pCtx->ddLeft.names;
+               char const * const *names1 = pCtx->ddRight.names;
+               char const *name0 = pCtx->m_strLeft;
+               char const *name1 = pCtx->m_strRight;
+               
+               /* 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 && stricmp (*names0, dir_start_file) < 0)
+                               names0++;
+                       while (*names1 && stricmp (*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
+                               : stricmp (*names0, *names1));
+                       int v1 = compare_files(name0, 0 < nameorder ? 0 : *names0++,
+                               name1, nameorder < 0 ? 0 : *names1++,
+                               pCtx, depth+1);
+                       if (v1 > val)
+                               val = v1;
+               }
+       }
+       
+       if (pCtx->ddLeft.names)
+               free (pCtx->ddLeft.names);
+       if (pCtx->ddLeft.data)
+               free (pCtx->ddLeft.data);
+       if (pCtx->ddRight.names)
+               free (pCtx->ddRight.names);
+       if (pCtx->ddRight.data)
+               free (pCtx->ddRight.data);
+
+       return val;
+}
diff --git a/Src/DirDoc.cpp b/Src/DirDoc.cpp
new file mode 100644 (file)
index 0000000..2ddbda8
--- /dev/null
@@ -0,0 +1,390 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// DirDoc.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+#include "DirDoc.h"
+#include "DirFrame.h"
+#include "diff.h"
+#include "getopt.h"
+#include "fnmatch.h"
+#include "MainFrm.h"
+#include "coretools.h"
+#include "logfile.h"
+
+extern int recursive;
+
+int compare_files (LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, CDiffContext*, int);
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+CLogFile gLog(_T("WinMerge.log"), NULL, TRUE);
+bool gWriteLog = true;
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirDoc
+
+IMPLEMENT_DYNCREATE(CDirDoc, CDocument)
+
+CDirDoc::CDirDoc()
+{
+       m_pView = NULL;
+       m_pCtxt=NULL;
+}
+
+CDirDoc::~CDirDoc()
+{
+       mf->m_pDirDoc = NULL;
+       if (m_pCtxt != NULL)
+               delete m_pCtxt;
+}
+
+BOOL CDirDoc::OnNewDocument()
+{
+       if (!CDocument::OnNewDocument())
+               return FALSE;
+
+       CString s;
+       VERIFY(s.LoadString(IDS_DIRECTORY_WINDOW_TITLE));
+       SetTitle(s);
+
+       return TRUE;
+}
+
+
+BEGIN_MESSAGE_MAP(CDirDoc, CDocument)
+       //{{AFX_MSG_MAP(CDirDoc)
+               // NOTE - the ClassWizard will add and remove mapping macros here.
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirDoc diagnostics
+
+#ifdef _DEBUG
+void CDirDoc::AssertValid() const
+{
+       CDocument::AssertValid();
+}
+
+void CDirDoc::Dump(CDumpContext& dc) const
+{
+       CDocument::Dump(dc);
+}
+#endif //_DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirDoc serialization
+
+void CDirDoc::Serialize(CArchive& ar)
+{
+       if (ar.IsStoring())
+       {
+               // TODO: add storing code here
+       }
+       else
+       {
+               // TODO: add loading code here
+       }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirDoc commands
+
+/*int
+diff_dirs2 (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;         
+  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)
+    {
+      char const * const *names0 = dirdata[0].names;
+      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 && stricmp (*names0, dir_start_file) < 0)
+           names0++;
+         while (*names1 && stricmp (*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
+                          : stricmp (*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;
+}*/
+
+
+void CDirDoc::Rescan()
+{
+       ASSERT(m_pCtxt != NULL);
+       BeginWaitCursor();
+
+       if (gWriteLog) gLog.Write(_T("Starting directory scan:\r\n\tLeft: %s\r\n\tRight: %s\r\n"),
+                       m_pCtxt->m_strLeft, m_pCtxt->m_strRight);
+       m_pCtxt->m_dirlist.RemoveAll();
+
+       compare_files (0, (char const *)(LPCTSTR)m_pCtxt->m_strLeft, 
+                              0, (char const *)(LPCTSTR)m_pCtxt->m_strRight, m_pCtxt, 0);
+       
+       if (gWriteLog) gLog.Write(_T("Directory scan complete\r\n"));
+
+       CString s;
+       AfxFormatString2(s, IDS_DIRECTORY_WINDOW_STATUS_FMT, m_pCtxt->m_strLeft, m_pCtxt->m_strRight);
+       ((CDirFrame*)(m_pView->GetParent()))->SetStatus(s);
+       Redisplay();
+
+       EndWaitCursor();
+}
+
+void CDirDoc::Redisplay()
+{
+       if (m_pCtxt == NULL)
+               return;
+
+       CString s,s2;
+       UINT cnt=0;
+       LPCTSTR p;
+       int llen = m_pCtxt->m_strLeft.GetLength();
+       int rlen = m_pCtxt->m_strRight.GetLength();
+
+       m_pView->m_pList->DeleteAllItems();
+       POSITION pos = m_pCtxt->m_dirlist.GetHeadPosition(), curpos;
+       while (pos != NULL)
+       {
+               curpos = pos;
+               DIFFITEM di = m_pCtxt->m_dirlist.GetNext(pos);
+               switch (di.code)
+               {
+               case FILE_DIFF:
+                       if (mf->m_bShowDiff
+                               && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT)))
+                       {
+                               m_pView->AddItem(cnt, DV_NAME, di.filename);
+
+                               p = _tcsninc(di.lpath, llen);
+                               s = _T(".");
+                               s += p;
+                               m_pView->AddItem(cnt, DV_PATH, s);
+                               
+                               m_pView->m_pList->SetItemData(cnt, (DWORD)curpos);
+                               UpdateItemStatus(cnt);
+                               cnt++;
+                       }
+                       break;
+               case FILE_BINDIFF:
+                       if (mf->m_bShowDiff
+                               && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT)))
+                       {
+                               m_pView->AddItem(cnt, DV_NAME, di.filename);
+                               p = _tcsninc(di.lpath, llen);
+                               s = _T(".");
+                               s += p;
+                               m_pView->AddItem(cnt, DV_PATH, s);
+                               m_pView->m_pList->SetItemData(cnt, (DWORD)curpos);
+                               UpdateItemStatus(cnt);
+                               cnt++;
+                       }
+                       break;
+               case FILE_LUNIQUE:
+               case FILE_RUNIQUE:
+                       if (mf->m_bShowUnique
+                               && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT)))
+                       {
+                               m_pView->AddItem(cnt, DV_NAME, di.filename);
+                               
+                               s2 = _T(".");
+                               s2 += p;
+                               m_pView->AddItem(cnt, DV_PATH, s2);
+                               m_pView->m_pList->SetItemData(cnt, (DWORD)curpos);
+                               UpdateItemStatus(cnt);
+                               cnt++;
+                       }
+                       break;
+               case FILE_SAME:
+                       if (mf->m_bShowIdent
+                               && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT)))
+                       {
+                               m_pView->AddItem(cnt, DV_NAME, di.filename);
+                               p = _tcsninc(di.lpath, llen);
+                               s = _T(".");
+                               s += p;
+                               m_pView->AddItem(cnt, DV_PATH, s);
+                               m_pView->m_pList->SetItemData(cnt, (DWORD)curpos);
+                               UpdateItemStatus(cnt);
+                               cnt++;
+                       }
+                       break;
+               default: // error
+                       m_pView->AddItem(cnt, DV_NAME, di.filename);
+                       p = _tcsninc(di.lpath, llen);
+                       s = _T(".");
+                       s += p;
+                       m_pView->AddItem(cnt, DV_PATH, s);
+                       m_pView->m_pList->SetItemData(cnt, (DWORD)curpos);
+                       UpdateItemStatus(cnt);
+                       cnt++;
+                       break;
+               }
+       }
+       
+}
+
+CDirView * CDirDoc::GetMainView()
+{
+       POSITION pos = GetFirstViewPosition(), ps2=pos;
+       
+       while (pos != NULL)
+       {
+               CDirView* pView = (CDirView*)GetNextView(pos);
+               if (pView->IsKindOf( RUNTIME_CLASS(CDirView)))
+                       return pView;
+       }   
+       return (CDirView*)GetNextView(ps2);
+}
+
+void CDirDoc::UpdateItemStatus(UINT nIdx)
+{
+       CString s,s2;
+       UINT cnt=0;
+       LPCTSTR p;
+       int llen = m_pCtxt->m_strLeft.GetLength();
+       int rlen = m_pCtxt->m_strRight.GetLength();
+       POSITION pos = (POSITION)m_pView->m_pList->GetItemData(nIdx);
+       DIFFITEM di = m_pCtxt->m_dirlist.GetAt(pos);
+       switch (di.code)
+       {
+       case FILE_DIFF:
+               VERIFY(s.LoadString(IDS_FILES_ARE_DIFFERENT));
+               m_pView->AddItem(nIdx, DV_STATUS, s);
+               m_pView->SetImage(nIdx, FILE_DIFF);
+               break;
+       case FILE_BINDIFF:
+               VERIFY(s.LoadString(IDS_BIN_FILES_DIFF));
+               m_pView->AddItem(nIdx, DV_STATUS, s);
+               m_pView->SetImage(nIdx, FILE_BINDIFF);
+               break;
+       case FILE_LUNIQUE:
+       case FILE_RUNIQUE:
+               {
+                       int img;
+                       if (di.code == FILE_LUNIQUE)
+                       {
+                               p = _tcsninc(di.lpath, llen);
+                               AfxFormatString1(s, IDS_ONLY_IN_FMT, di.lpath);
+                               img = FILE_LUNIQUE;
+                       }
+                       else
+                       {
+                               p = _tcsninc(di.rpath, rlen);
+                               AfxFormatString1(s, IDS_ONLY_IN_FMT, di.rpath);
+                               img = FILE_RUNIQUE;
+                       }
+                       m_pView->AddItem(nIdx, DV_STATUS, s);
+                       m_pView->SetImage(nIdx, img);
+               }
+               break;
+       case FILE_SAME:
+               VERIFY(s.LoadString(IDS_IDENTICAL));
+               m_pView->AddItem(nIdx, DV_STATUS,s);
+               m_pView->SetImage(nIdx, FILE_SAME);
+               break;
+       default: // error
+               VERIFY(s.LoadString(IDS_CANT_COMPARE_FILES));
+               m_pView->AddItem(nIdx, DV_STATUS, s);
+               m_pView->SetImage(nIdx, FILE_ERROR);
+               break;
+       }
+}
+
+void CDirDoc::InitStatusStrings()
+{
+       
+}
+
+void CDirDoc::UpdateResources()
+{
+       m_pView->UpdateResources();
+
+       CString s;
+       VERIFY(s.LoadString(IDS_DIRECTORY_WINDOW_TITLE));
+       SetTitle(s);
+
+       Redisplay();
+}
+
+void CDirDoc::SetDiffContext(CDiffContext *pCtxt)
+{
+       if (m_pCtxt != NULL)
+               delete m_pCtxt;
+
+       m_pCtxt = pCtxt;
+}
diff --git a/Src/DirDoc.h b/Src/DirDoc.h
new file mode 100644 (file)
index 0000000..a12ac11
--- /dev/null
@@ -0,0 +1,83 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+#if !defined(AFX_DIRDOC_H__0B17B4C1_356F_11D1_95CD_444553540000__INCLUDED_)
+#define AFX_DIRDOC_H__0B17B4C1_356F_11D1_95CD_444553540000__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// DirDoc.h : header file
+//
+#include "DirView.h"
+#include "DiffContext.h"
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirDoc document
+
+class CDirDoc : public CDocument
+{
+protected:
+       CDirDoc();           // protected constructor used by dynamic creation
+       DECLARE_DYNCREATE(CDirDoc)
+
+// Attributes
+public:
+// Operations
+public:
+       CDirView *m_pView;
+       CDirView * GetMainView();
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CDirDoc)
+       public:
+       virtual void Serialize(CArchive& ar);   // overridden for document i/o
+       protected:
+       virtual BOOL OnNewDocument();
+       //}}AFX_VIRTUAL
+
+// Implementation
+public:
+       void SetDiffContext(CDiffContext *pCtxt);
+       void UpdateResources();
+       void InitStatusStrings();
+       void UpdateItemStatus(UINT nIdx);
+       void Redisplay();
+       void Rescan();
+       CDiffContext *m_pCtxt;
+       virtual ~CDirDoc();
+#ifdef _DEBUG
+       virtual void AssertValid() const;
+       virtual void Dump(CDumpContext& dc) const;
+#endif
+
+       // Generated message map functions
+protected:
+       //{{AFX_MSG(CDirDoc)
+               // NOTE - the ClassWizard will add and remove member functions here.
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_DIRDOC_H__0B17B4C1_356F_11D1_95CD_444553540000__INCLUDED_)
diff --git a/Src/DirFrame.cpp b/Src/DirFrame.cpp
new file mode 100644 (file)
index 0000000..3fbed37
--- /dev/null
@@ -0,0 +1,95 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// DirFrame.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+#include "DirFrame.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+static UINT indicators[] =
+{
+       ID_SEPARATOR,           // status line indicator
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirFrame
+
+IMPLEMENT_DYNCREATE(CDirFrame, CMDIChildWnd)
+
+CDirFrame::CDirFrame()
+{
+}
+
+CDirFrame::~CDirFrame()
+{
+}
+
+
+BEGIN_MESSAGE_MAP(CDirFrame, CMDIChildWnd)
+       //{{AFX_MSG_MAP(CDirFrame)
+       ON_WM_CREATE()
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirFrame message handlers
+
+#ifdef _DEBUG
+void CDirFrame::AssertValid() const
+{
+       CMDIChildWnd::AssertValid();
+}
+
+void CDirFrame::Dump(CDumpContext& dc) const
+{
+       CMDIChildWnd::Dump(dc);
+}
+
+#endif //_DEBUG
+
+int CDirFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
+{
+       if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
+               return -1;
+
+       if (!m_wndStatusBar.Create(this) ||
+               !m_wndStatusBar.SetIndicators(indicators,
+                 sizeof(indicators)/sizeof(UINT)))
+       {
+               TRACE0("Failed to create status bar\n");
+               return -1;      // fail to create
+       }       
+       
+       return 0;
+}
+
+void CDirFrame::SetStatus(LPCTSTR szStatus)
+{
+       m_wndStatusBar.SetPaneText(0, szStatus);
+}
+
diff --git a/Src/DirFrame.h b/Src/DirFrame.h
new file mode 100644 (file)
index 0000000..8960ba5
--- /dev/null
@@ -0,0 +1,72 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+#if !defined(AFX_DIRFRAME_H__95565903_35C4_11D1_BAA7_00A024706EDC__INCLUDED_)
+#define AFX_DIRFRAME_H__95565903_35C4_11D1_BAA7_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// DirFrame.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirFrame frame
+
+class CDirFrame : public CMDIChildWnd
+{
+       DECLARE_DYNCREATE(CDirFrame)
+protected:
+       CDirFrame();           // protected constructor used by dynamic creation
+
+// Attributes
+public:
+
+// Operations
+public:
+       void SetStatus(LPCTSTR szStatus);
+       CStatusBar  m_wndStatusBar;
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CDirFrame)
+       //}}AFX_VIRTUAL
+
+// Implementation
+#ifdef _DEBUG
+       virtual void AssertValid() const;
+       virtual void Dump(CDumpContext& dc) const;
+#endif
+
+protected:
+       virtual ~CDirFrame();
+
+       // Generated message map functions
+       //{{AFX_MSG(CDirFrame)
+       afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_DIRFRAME_H__95565903_35C4_11D1_BAA7_00A024706EDC__INCLUDED_)
diff --git a/Src/DirView.cpp b/Src/DirView.cpp
new file mode 100644 (file)
index 0000000..e2a93aa
--- /dev/null
@@ -0,0 +1,392 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// DirView.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+#include "DirView.h"
+#include "DirDoc.h"
+#include "MainFrm.h"
+#include "resource.h"
+#include "coretools.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirView
+
+
+
+IMPLEMENT_DYNCREATE(CDirView, CListViewEx)
+
+CDirView::CDirView()
+{
+       m_pList=NULL;
+}
+
+CDirView::~CDirView()
+{
+}
+
+
+BEGIN_MESSAGE_MAP(CDirView, CListViewEx)
+       ON_WM_CONTEXTMENU()
+       //{{AFX_MSG_MAP(CDirView)
+       ON_WM_LBUTTONDBLCLK()
+       ON_COMMAND(ID_DIR_COPY_FILE_TO_LEFT, OnDirCopyFileToLeft)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_FILE_TO_LEFT, OnUpdateDirCopyFileToLeft)
+       ON_COMMAND(ID_DIR_COPY_FILE_TO_RIGHT, OnDirCopyFileToRight)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_FILE_TO_RIGHT, OnUpdateDirCopyFileToRight)
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirView drawing
+
+void CDirView::OnDraw(CDC* /*pDC*/)
+{
+       CDocument* pDoc = GetDocument();
+       // TODO: add draw code here
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirView diagnostics
+
+#ifdef _DEBUG
+void CDirView::AssertValid() const
+{
+       CListViewEx::AssertValid();
+}
+
+void CDirView::Dump(CDumpContext& dc) const
+{
+       CListViewEx::Dump(dc);
+}
+
+CDirDoc* CDirView::GetDocument() // non-debug version is inline
+{
+       ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDirDoc)));
+       return (CDirDoc*)m_pDocument;
+}
+#endif //_DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirView message handlers
+
+void CDirView::OnInitialUpdate() 
+{
+       CListViewEx::OnInitialUpdate();
+       m_pList = &GetListCtrl();
+       GetDocument()->m_pView = this;
+       
+
+       m_pList->InsertColumn(DV_NAME, _T("Filename"), LVCFMT_LEFT, 150);
+       m_pList->InsertColumn(DV_PATH, _T("Directory"), LVCFMT_LEFT, 200);
+       m_pList->InsertColumn(DV_STATUS, _T("Comparison result"), LVCFMT_LEFT, 250);
+
+       CBitmap eq,ne,fldl,fldr,unk,bin;
+       VERIFY (m_imageList.Create (16, 16, ILC_MASK, 0, 1));
+       VERIFY (eq.LoadBitmap (IDB_EQUAL));
+       VERIFY (ne.LoadBitmap (IDB_NOTEQUAL));
+       VERIFY (fldl.LoadBitmap (IDB_LFOLDER));
+       VERIFY (fldr.LoadBitmap (IDB_RFOLDER));
+       VERIFY (unk.LoadBitmap (IDB_UNKNOWN));
+       VERIFY (bin.LoadBitmap (IDB_BINARY));
+       VERIFY (-1 != m_imageList.Add (&fldl, RGB (255, 255, 255)));
+       VERIFY (-1 != m_imageList.Add (&fldr, RGB (255, 255, 255)));
+       VERIFY (-1 != m_imageList.Add (&ne, RGB (255, 255, 255)));
+       VERIFY (-1 != m_imageList.Add (&eq, RGB (255, 255, 255)));
+       VERIFY (-1 != m_imageList.Add (&unk, RGB (255, 255, 255)));
+       VERIFY (-1 != m_imageList.Add (&bin, RGB (255, 255, 255)));
+       m_pList->SetImageList (&m_imageList, LVSIL_SMALL);
+       UpdateResources();
+}
+
+void CDirView::OnLButtonDblClk(UINT nFlags, CPoint point) 
+{
+       int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
+       if (sel != -1)
+       {
+               CDirDoc *pd = GetDocument();
+               POSITION pos = reinterpret_cast<POSITION>(m_pList->GetItemData(sel));
+               DIFFITEM di = pd->m_pCtxt->m_dirlist.GetAt(pos);
+               switch(di.code)
+               {
+               case FILE_DIFF:
+                       {
+                               CString left, right;
+                               if (GetSelectedFileNames(left, right))
+                                       mf->ShowMergeDoc(left, right);
+                       }
+                       break;
+               case FILE_SAME:
+                       {
+                               CString s;
+                               VERIFY(s.LoadString(IDS_FILESSAME));
+                               AfxMessageBox(s, MB_ICONINFORMATION);
+                       }
+                       break;
+               case FILE_LUNIQUE:
+               case FILE_RUNIQUE:
+                       {
+                               CString s;
+                               VERIFY(s.LoadString(IDS_FILEUNIQUE));
+                               AfxMessageBox(s, MB_ICONINFORMATION);
+                       }
+                       break;
+               case FILE_BINDIFF:
+                       {
+                               CString s;
+                               VERIFY(s.LoadString(IDS_FILEBINARY));
+                               AfxMessageBox(s, MB_ICONSTOP);
+                       }
+                       break;
+               default:
+                       {
+                               CString s;
+                               VERIFY(s.LoadString(IDS_FILEERROR));
+                               AfxMessageBox(s, MB_ICONSTOP);
+                       }
+                       break;
+               }
+       }
+       CListViewEx::OnLButtonDblClk(nFlags, point);
+}
+
+void CDirView::OnContextMenu(CWnd*, CPoint point)
+{
+
+       // CG: This block was added by the Pop-up Menu component
+       {
+               if (point.x == -1 && point.y == -1){
+                       //keystroke invocation
+                       CRect rect;
+                       GetClientRect(rect);
+                       ClientToScreen(rect);
+
+                       point = rect.TopLeft();
+                       point.Offset(5, 5);
+               }
+
+               CMenu menu;
+               VERIFY(menu.LoadMenu(IDR_POPUP_DIRVIEW));
+
+               CMenu* pPopup = menu.GetSubMenu(0);
+               ASSERT(pPopup != NULL);
+               CWnd* pWndPopupOwner = this;
+
+               // set the menu items with the proper directory names
+               CString s, sl, sr;
+               GetSelectedDirNames(sl, sr);
+               AfxFormatString1(s, IDS_COPY2DIR_FMT, sl);
+               pPopup->ModifyMenu(ID_DIR_COPY_FILE_TO_LEFT, MF_BYCOMMAND|MF_STRING, ID_DIR_COPY_FILE_TO_LEFT, s);
+               AfxFormatString1(s, IDS_COPY2DIR_FMT, sr);
+               pPopup->ModifyMenu(ID_DIR_COPY_FILE_TO_RIGHT, MF_BYCOMMAND|MF_STRING, ID_DIR_COPY_FILE_TO_RIGHT, s);
+
+               while (pWndPopupOwner->GetStyle() & WS_CHILD)
+                       pWndPopupOwner = pWndPopupOwner->GetParent();
+
+               pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
+                       pWndPopupOwner);
+       }
+}
+
+void CDirView::OnDirCopyFileToLeft() 
+{
+       int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
+       CString s, sl, sr, slFile, srFile;
+
+       if (sel != -1
+               && GetSelectedDirNames(sl, sr)
+               && GetSelectedFileNames(slFile, srFile))
+       {
+               CDirDoc *pd = GetDocument();
+               CString s;
+               POSITION pos = reinterpret_cast<POSITION>(m_pList->GetItemData(sel));
+               DIFFITEM di = pd->m_pCtxt->m_dirlist.GetAt(pos);
+               switch(di.code)
+               {
+               case FILE_LUNIQUE:
+               case FILE_SAME:
+                       //pCmdUI->Enable(FALSE);
+                       break;
+               case FILE_RUNIQUE:
+               case FILE_DIFF:
+               case FILE_BINDIFF:
+                       AfxFormatString1(s, IDS_CONFIRM_COPY2DIR, sl);
+                       if (AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION)==IDYES)
+                       {               
+                               if (mf->SyncFiles(srFile, slFile))
+                               {
+                                       mf->UpdateCurrentFileStatus(FILE_SAME);
+                               }
+                       }
+
+                       break;
+               }
+       }
+}
+
+void CDirView::OnUpdateDirCopyFileToLeft(CCmdUI* pCmdUI) 
+{
+       int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
+       if (sel != -1)
+       {
+               CDirDoc *pd = GetDocument();
+               POSITION pos = reinterpret_cast<POSITION>(m_pList->GetItemData(sel));
+               DIFFITEM di = pd->m_pCtxt->m_dirlist.GetAt(pos);
+               switch(di.code)
+               {
+               case FILE_SAME:
+               case FILE_LUNIQUE:
+                       pCmdUI->Enable(FALSE);
+                       break;
+               case FILE_RUNIQUE:
+               case FILE_DIFF:
+               case FILE_BINDIFF:
+                       pCmdUI->Enable(TRUE);
+                       break;
+               }
+       }
+}
+
+void CDirView::OnDirCopyFileToRight() 
+{
+       int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
+       CString s, sl, sr, slFile, srFile;
+
+       if (sel != -1
+               && GetSelectedDirNames(sl, sr)
+               && GetSelectedFileNames(slFile, srFile))
+       {
+               CDirDoc *pd = GetDocument();
+               CString s;
+               POSITION pos = reinterpret_cast<POSITION>(m_pList->GetItemData(sel));
+               DIFFITEM di = pd->m_pCtxt->m_dirlist.GetAt(pos);
+               switch(di.code)
+               {
+               case FILE_RUNIQUE:
+               case FILE_SAME:
+                       //pCmdUI->Enable(FALSE);
+                       break;
+               case FILE_LUNIQUE:
+               case FILE_DIFF:
+               case FILE_BINDIFF:
+                       AfxFormatString1(s, IDS_CONFIRM_COPY2DIR, sr);
+                       if (AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION)==IDYES)
+                       {               
+                               CString left, right;
+                               if (mf->SyncFiles(slFile, srFile))
+                               {
+                                       mf->UpdateCurrentFileStatus(FILE_SAME);
+                               }
+                       }
+
+                       break;
+               }
+       }
+}
+
+void CDirView::OnUpdateDirCopyFileToRight(CCmdUI* pCmdUI) 
+{
+       int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
+       if (sel != -1)
+       {
+               CDirDoc *pd = GetDocument();
+               POSITION pos = reinterpret_cast<POSITION>(m_pList->GetItemData(sel));
+               DIFFITEM di = pd->m_pCtxt->m_dirlist.GetAt(pos);
+               switch(di.code)
+               {
+               case FILE_RUNIQUE:
+               case FILE_SAME:
+                       pCmdUI->Enable(FALSE);
+                       break;
+               case FILE_LUNIQUE:
+               case FILE_DIFF:
+               case FILE_BINDIFF:
+                       pCmdUI->Enable(TRUE);
+                       break;
+               }
+       }
+}
+
+BOOL CDirView::GetSelectedFileNames(CString& strLeft, CString& strRight)
+{
+       int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
+       if (sel != -1)
+       {
+               CDirDoc *pd = GetDocument();
+               CString name, pathex;
+               name = m_pList->GetItemText(sel, DV_NAME);
+               pathex = m_pList->GetItemText(sel, DV_PATH);
+               if (pathex.Left(2) == _T(".\\") || pathex.Left(2) == _T("./"))
+               {
+                       strLeft.Format(_T("%s\\%s\\%s"), pd->m_pCtxt->m_strLeft, pathex.Right(pathex.GetLength()-2), name);
+                       strRight.Format(_T("%s\\%s\\%s"), pd->m_pCtxt->m_strRight, pathex.Right(pathex.GetLength()-2), name);
+               }
+               else
+               {
+                       strLeft.Format(_T("%s\\%s"), pd->m_pCtxt->m_strLeft, name);
+                       strRight.Format(_T("%s\\%s"), pd->m_pCtxt->m_strRight, name);
+               }
+               return TRUE;
+       }
+       return FALSE;
+}
+
+
+void CDirView::UpdateResources()
+{
+       CString s;
+       LV_COLUMN lvc;
+       lvc.mask = LVCF_TEXT;
+
+       VERIFY(s.LoadString(IDS_FILENAME_HEADER));
+       lvc.pszText = (LPTSTR)((LPCTSTR)s);
+       m_pList->SetColumn(DV_NAME, &lvc);
+       VERIFY(s.LoadString(IDS_DIR_HEADER));
+       lvc.pszText = (LPTSTR)((LPCTSTR)s);
+       m_pList->SetColumn(DV_PATH, &lvc);
+       VERIFY(s.LoadString(IDS_RESULT_HEADER));
+       lvc.pszText = (LPTSTR)((LPCTSTR)s);
+       m_pList->SetColumn(DV_STATUS, &lvc);
+}
+
+BOOL CDirView::GetSelectedDirNames(CString& strLeft, CString& strRight)
+{
+       BOOL bResult = GetSelectedFileNames(strLeft, strRight);
+
+       if (bResult)
+       {
+               TCHAR path[MAX_PATH];
+               split_filename(strLeft, path, NULL, NULL);
+               strLeft = path;
+
+               split_filename(strRight, path, NULL, NULL);
+               strRight = path;
+       }
+       return bResult;
+}
diff --git a/Src/DirView.h b/Src/DirView.h
new file mode 100644 (file)
index 0000000..08b9b9f
--- /dev/null
@@ -0,0 +1,98 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+#if !defined(AFX_DirView_H__16E7C721_351C_11D1_95CD_444553540000__INCLUDED_)
+#define AFX_DirView_H__16E7C721_351C_11D1_95CD_444553540000__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// DirView.h : header file
+//
+
+#define DV_NAME   0
+#define DV_PATH   1
+#define DV_STATUS 2
+
+/////////////////////////////////////////////////////////////////////////////
+// CDirView view
+#include <afxcview.h>
+#include "listvwex.h"
+
+class CDirDoc;
+
+class CDirView : public CListViewEx
+{
+protected:
+       CDirView();           // protected constructor used by dynamic creation
+       DECLARE_DYNCREATE(CDirView)
+
+// Attributes
+       CImageList m_imageList;
+public:
+       CDirDoc* GetDocument(); // non-debug version is inline
+
+// Operations
+public:
+       BOOL GetSelectedDirNames(CString& strLeft, CString& strRight);
+       void UpdateResources();
+       BOOL GetSelectedFileNames(CString& strLeft, CString& strRight);
+       CListCtrl * m_pList;
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CDirView)
+       public:
+       virtual void OnInitialUpdate();
+       protected:
+       virtual void OnDraw(CDC* pDC);      // overridden to draw this view
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+       virtual ~CDirView();
+#ifdef _DEBUG
+       virtual void AssertValid() const;
+       virtual void Dump(CDumpContext& dc) const;
+#endif
+
+       // Generated message map functions
+protected:
+       afx_msg void OnContextMenu(CWnd*, CPoint point);
+       //{{AFX_MSG(CDirView)
+       afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
+       afx_msg void OnDirCopyFileToLeft();
+       afx_msg void OnUpdateDirCopyFileToLeft(CCmdUI* pCmdUI);
+       afx_msg void OnDirCopyFileToRight();
+       afx_msg void OnUpdateDirCopyFileToRight(CCmdUI* pCmdUI);
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+#ifndef _DEBUG  // debug version in DirView.cpp
+inline CDirDoc* CDirView::GetDocument()
+   { return (CDirDoc*)m_pDocument; }
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_DirView_H__16E7C721_351C_11D1_95CD_444553540000__INCLUDED_)
diff --git a/Src/MainFrm.cpp b/Src/MainFrm.cpp
new file mode 100644 (file)
index 0000000..1b09dec
--- /dev/null
@@ -0,0 +1,773 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// MainFrm.cpp : implementation of the CMainFrame class
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+
+#include <direct.h>
+#include "MainFrm.h"
+#include "ChildFrm.h"
+#include "DiffView.h"
+#include "DirView.h"
+#include "DirDoc.h"
+#include "OpenDlg.h"
+
+#include "diff.h"
+#include "getopt.h"
+#include "fnmatch.h"
+#include "coretools.h"
+#include "Splash.h"
+#include "VssPrompt.h"
+#include "PropVss.h"
+#include "PropGeneral.h"
+#include "RegKey.h"
+#include "logfile.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+extern int recursive;
+CMainFrame *mf = NULL;
+extern CLogFile gLog;
+extern bool gWriteLog;
+
+/////////////////////////////////////////////////////////////////////////////
+// CMainFrame
+
+IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)
+
+BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
+       //{{AFX_MSG_MAP(CMainFrame)
+       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENT, OnOptionsShowDifferent)
+       ON_COMMAND(ID_OPTIONS_SHOWIDENTICAL, OnOptionsShowIdentical)
+       ON_COMMAND(ID_OPTIONS_SHOWUNIQUE, OnOptionsShowUnique)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENT, OnUpdateOptionsShowdifferent)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWIDENTICAL, OnUpdateOptionsShowidentical)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUE, OnUpdateOptionsShowunique)
+       ON_WM_CREATE()
+       ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
+       ON_UPDATE_COMMAND_UI(ID_HIDE_BACKUP_FILES, OnUpdateHideBackupFiles)
+       ON_COMMAND(ID_HELP_GNULICENSE, OnHelpGnulicense)
+       ON_COMMAND(ID_PROPERTIES, OnProperties)
+       ON_COMMAND(ID_HIDE_BACKUP_FILES, OnHideBackupFiles)
+       ON_COMMAND(ID_VIEW_SELECTFONT, OnViewSelectfont)
+       ON_COMMAND(ID_VIEW_USEDEFAULTFONT, OnViewUsedefaultfont)
+       ON_UPDATE_COMMAND_UI(ID_VIEW_USEDEFAULTFONT, OnUpdateViewUsedefaultfont)
+       ON_COMMAND(ID_HELP_CONTENTS, OnHelpContents)
+       ON_UPDATE_COMMAND_UI(ID_HELP_CONTENTS, OnUpdateHelpContents)
+       ON_WM_CLOSE()
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+static UINT indicators[] =
+{
+       ID_SEPARATOR,           // status line indicator
+       ID_INDICATOR_CAPS,
+       ID_INDICATOR_NUM,
+       ID_INDICATOR_SCRL,
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CMainFrame construction/destruction
+
+CMainFrame::CMainFrame()
+{
+       m_pLeft = m_pRight = NULL;
+       m_pMergeDoc=NULL;
+       m_pDirDoc=NULL;
+       m_bFontSpecified=FALSE;
+       m_strSaveAsPath = _T("");
+       m_bFirstTime = TRUE;
+
+       m_bIgnoreBlankLines = theApp.GetProfileInt(_T("Settings"), _T("IgnoreBlankLines"), FALSE)!=0;
+       m_bIgnoreCase = theApp.GetProfileInt(_T("Settings"), _T("IgnoreCase"), FALSE)!=0;
+       m_bShowUnique = theApp.GetProfileInt(_T("Settings"), _T("ShowUnique"), TRUE)!=0;
+       m_bShowDiff = theApp.GetProfileInt(_T("Settings"), _T("ShowDifferent"), TRUE)!=0;
+       m_bShowIdent = theApp.GetProfileInt(_T("Settings"), _T("ShowIdentical"), TRUE)!=0;
+       m_bBackup = theApp.GetProfileInt(_T("Settings"), _T("BackupFile"), TRUE)!=0;
+       m_bScrollToFirst = theApp.GetProfileInt(_T("Settings"), _T("ScrollToFirst"), FALSE)!=0;
+       m_bIgnoreWhitespace = theApp.GetProfileInt(_T("Settings"), _T("IgnoreSpace"), TRUE)!=0;
+       m_bHideBak = theApp.GetProfileInt(_T("Settings"), _T("HideBak"), TRUE)!=0;
+       m_bUseVss = theApp.GetProfileInt(_T("Settings"), _T("UseVss"), FALSE)!=0;
+       m_strVssProject = theApp.GetProfileString(_T("Settings"), _T("VssProject"), _T(""));
+       m_strVssPath = theApp.GetProfileString(_T("Settings"), _T("VssPath"), _T(""));
+       m_nTabSize = theApp.GetProfileInt(_T("Settings"), _T("TabSize"), 4);
+       if (m_strVssPath.IsEmpty())
+       {
+               CRegKeyEx reg;
+               if (reg.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\SourceSafe")) == ERROR_SUCCESS)
+               {
+                       TCHAR temp[MAX_PATH],path[MAX_PATH];
+                       reg.ReadChars(_T("SCCServerPath"), temp, MAX_PATH, _T(""));
+                       split_filename(temp, path, NULL, NULL);
+                       m_strVssPath.Format(_T("%s\\Ss.exe"), path);
+               }
+       }
+}
+
+CMainFrame::~CMainFrame()
+{
+}
+
+int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+       if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
+               return -1;
+
+       mf = this;
+       ignore_all_space_flag = m_bIgnoreWhitespace;
+       length_varies = m_bIgnoreWhitespace;
+       ignore_case_flag = m_bIgnoreCase;
+       ignore_blank_lines_flag = m_bIgnoreBlankLines;
+       ignore_some_changes = m_bIgnoreWhitespace || m_bIgnoreCase || m_bIgnoreBlankLines;
+
+       heuristic = 1;
+       output_style = OUTPUT_NORMAL;
+    context = -1;
+    line_end_char = '\n';
+       //ignore_blank_lines_flag = 1;
+       GetFontProperties();
+       
+       if (!m_wndToolBar.Create(this) ||
+               !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
+       {
+               TRACE0("Failed to create toolbar\n");
+               return -1;      // fail to create
+       }
+
+       if (!m_wndStatusBar.Create(this) ||
+               !m_wndStatusBar.SetIndicators(indicators,
+                 sizeof(indicators)/sizeof(UINT)))
+       {
+               TRACE0("Failed to create status bar\n");
+               return -1;      // fail to create
+       }
+
+       // TODO: Remove this if you don't want tool tips or a resizeable toolbar
+       m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
+               CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
+
+       // TODO: Delete these three lines if you don't want the toolbar to
+       //  be dockable
+       m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
+       EnableDocking(CBRS_ALIGN_ANY);
+       DockControlBar(&m_wndToolBar);
+
+       // CG: The following line was added by the Splash Screen component.
+       CSplashWnd::ShowSplashScreen(this);
+
+       return 0;
+}
+
+BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
+{
+       // TODO: Modify the Window class or styles here by modifying
+       //  the CREATESTRUCT cs
+
+       return CMDIFrameWnd::PreCreateWindow(cs);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CMainFrame diagnostics
+
+#ifdef _DEBUG
+void CMainFrame::AssertValid() const
+{
+       CMDIFrameWnd::AssertValid();
+}
+
+void CMainFrame::Dump(CDumpContext& dc) const
+{
+       CMDIFrameWnd::Dump(dc);
+}
+
+#endif //_DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+// CMainFrame message handlers
+
+void CMainFrame::OnFileOpen() 
+{
+       DoFileOpen();
+}
+
+
+void CMainFrame::ShowMergeDoc(LPCTSTR szLeft, LPCTSTR szRight)
+{
+       BOOL docNull = (m_pMergeDoc == NULL);
+       if (docNull)
+               m_pMergeDoc = (CMergeDoc*)theApp.m_pDiffTemplate->OpenDocumentFile(NULL);
+       else if (mf->m_pLeft)
+               mf->m_pLeft->SendMessage(WM_COMMAND, ID_FILE_SAVE);
+
+       if (m_pMergeDoc != NULL)
+       {
+               m_pMergeDoc->m_strLeftFile = szLeft;
+               m_pMergeDoc->m_strRightFile = szRight;
+               if (m_pMergeDoc->Rescan())
+               {
+                       if (docNull)
+                               MDIActivate(m_pMergeDoc->m_pView->GetParent());
+                       else
+                               MDINext();
+               }
+               else
+               {
+                       m_pMergeDoc->m_pView->GetParentFrame()->DestroyWindow();
+                       m_pMergeDoc=NULL;
+               }
+       }
+}
+
+
+
+void CMainFrame::OnOptionsShowDifferent() 
+{
+       m_bShowDiff = !m_bShowDiff;
+       theApp.WriteProfileInt(_T("Settings"), _T("ShowDifferent"), m_bShowDiff);
+       if (m_pDirDoc != NULL)
+               m_pDirDoc->Redisplay();
+}
+
+void CMainFrame::OnOptionsShowIdentical() 
+{
+       m_bShowIdent = !m_bShowIdent;
+       theApp.WriteProfileInt(_T("Settings"), _T("ShowIdentical"), m_bShowIdent);
+       if (m_pDirDoc != NULL)
+               m_pDirDoc->Redisplay();
+}
+
+void CMainFrame::OnOptionsShowUnique() 
+{
+       m_bShowUnique = !m_bShowUnique;
+       theApp.WriteProfileInt(_T("Settings"), _T("ShowUnique"), m_bShowUnique);
+       if (m_pDirDoc != NULL)
+               m_pDirDoc->Redisplay();
+}
+
+void CMainFrame::OnUpdateOptionsShowdifferent(CCmdUI* pCmdUI) 
+{
+       pCmdUI->SetCheck(m_bShowDiff);
+}
+
+void CMainFrame::OnUpdateOptionsShowidentical(CCmdUI* pCmdUI) 
+{
+       pCmdUI->SetCheck(m_bShowIdent);
+}
+
+void CMainFrame::OnUpdateOptionsShowunique(CCmdUI* pCmdUI) 
+{
+       pCmdUI->SetCheck(m_bShowUnique);
+}
+
+
+
+
+void CMainFrame::OnHideBackupFiles() 
+{
+       m_bHideBak = ! m_bHideBak;
+       theApp.WriteProfileInt(_T("Settings"), _T("HideBak"), m_bHideBak);
+       if (m_pDirDoc != NULL)
+               m_pDirDoc->Redisplay();
+}
+
+void CMainFrame::OnUpdateHideBackupFiles(CCmdUI* pCmdUI) 
+{
+       pCmdUI->SetCheck(m_bHideBak);
+}
+
+void CMainFrame::OnHelpGnulicense() 
+{
+       ShellExecute(m_hWnd, _T("open"), _T("notepad.exe"),_T("Copying"), NULL, SW_SHOWNORMAL);
+}
+
+
+
+
+
+BOOL CMainFrame::CheckSavePath(CString& strSavePath)
+{
+       BOOL needCheck;
+       CFileStatus status;
+       CString s;
+
+       // check if file is writeable
+       do
+       {
+               needCheck=FALSE;
+               if (CFile::GetStatus(strSavePath, status))
+               {
+                       if (status.m_attribute & CFile::Attribute::readOnly)
+                       {
+                               if (!m_bUseVss)
+                               {
+                                       CString title;
+                                       VERIFY(title.LoadString(IDS_SAVE_AS_TITLE));
+                                       AfxFormatString1(s, IDS_SAVEREADONLY_FMT, strSavePath);
+                                       if (AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION) == IDYES
+                                               && SelectFile(s, NULL, title, NULL, FALSE))
+                                       {
+                                               strSavePath = s;
+                                               needCheck=TRUE;
+                                       }
+                                       else
+                                               return FALSE;
+                               }
+                               else
+                               {
+                                       CVssPrompt dlg;
+                                       AfxFormatString1(dlg.m_strMessage, IDS_SAVEVSS_FMT, strSavePath);
+                                       dlg.m_strProject = m_strVssProject;
+                                       switch(dlg.DoModal())
+                                       {
+                                       case IDOK:
+                                               {
+                                                       BeginWaitCursor();
+                                                       m_strVssProject = dlg.m_strProject;
+                                                       theApp.WriteProfileString(_T("Settings"), _T("VssProject"), mf->m_strVssProject);
+                                                       TCHAR args[1024];
+                                                       TCHAR path[MAX_PATH],name[MAX_PATH];
+                                                       split_filename(strSavePath,path,name,NULL);
+                                                       _chdrive(toupper(path[0])-'A'+1);
+                                                       _chdir(path);
+                                                       DWORD code;
+                                                       _stprintf(args,_T("checkout %s/%s"), m_strVssProject,name);
+                                                       HANDLE hVss = RunIt(m_strVssPath, args, TRUE, FALSE);
+                                                       if (hVss)
+                                                       {
+                                                               while (!HasExited(hVss, &code))
+                                                                       Sleep(1000);
+                                                               
+                                                               if (code != 0)
+                                                               {
+                                                                       AfxMessageBox(IDS_VSSERROR, MB_ICONSTOP);
+                                                                       return FALSE;
+                                                               }
+                                                               needCheck=FALSE;
+                                                               //CloseHandle(hVss);
+                                                       }
+                                                       EndWaitCursor();
+                                               }
+                                               break;
+                                       case IDCANCEL:
+                                               return FALSE;
+                                       case IDSAVEAS:
+                                               CString title;
+                                               VERIFY(title.LoadString(IDS_SAVE_AS_TITLE));
+                                               if (SelectFile(s, NULL, title, NULL, FALSE))
+                                               {
+                                                       strSavePath = s;
+                                                       needCheck=TRUE;
+                                               }
+                                               else
+                                                       return FALSE;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       } while (needCheck);
+       return TRUE;
+}
+
+void CMainFrame::OnProperties() 
+{
+       CPropertySheet sht(IDS_PROPERTIES_TITLE);
+       CPropVss vss;
+       CPropGeneral gen;
+       sht.AddPage(&gen);
+       sht.AddPage(&vss);
+       
+       vss.m_bDoVss = m_bUseVss;
+       vss.m_strPath = m_strVssPath;
+
+       gen.m_bBackup = m_bBackup;
+       gen.m_bIgnoreWhite = m_bIgnoreWhitespace;
+       gen.m_bIgnoreCase = m_bIgnoreCase;
+       gen.m_bIgnoreBlankLines = m_bIgnoreBlankLines;
+       gen.m_bScroll = m_bScrollToFirst;
+       gen.m_nTabSize = m_nTabSize;
+       
+       if (sht.DoModal()==IDOK)
+       {
+               m_bUseVss = vss.m_bDoVss;
+               m_strVssPath = vss.m_strPath;
+               
+               m_bBackup = gen.m_bBackup;
+               m_bScrollToFirst = gen.m_bScroll;
+               m_nTabSize = gen.m_nTabSize;
+
+               ignore_all_space_flag = m_bIgnoreWhitespace = gen.m_bIgnoreWhite;
+               ignore_blank_lines_flag = m_bIgnoreBlankLines = gen.m_bIgnoreBlankLines;
+               ignore_case_flag = m_bIgnoreCase = gen.m_bIgnoreCase;
+               ignore_some_changes = m_bIgnoreWhitespace || m_bIgnoreCase || m_bIgnoreBlankLines;
+               length_varies = m_bIgnoreWhitespace;
+
+               theApp.WriteProfileInt(_T("Settings"), _T("UseVss"), m_bUseVss);        
+               theApp.WriteProfileInt(_T("Settings"), _T("IgnoreSpace"), m_bIgnoreWhitespace);
+               theApp.WriteProfileInt(_T("Settings"), _T("ScrollToFirst"), m_bScrollToFirst);
+               theApp.WriteProfileInt(_T("Settings"), _T("BackupFile"), m_bBackup);
+               theApp.WriteProfileString(_T("Settings"), _T("VssPath"), m_strVssPath);
+               theApp.WriteProfileInt(_T("Settings"), _T("TabSize"), m_nTabSize);
+               theApp.WriteProfileInt(_T("Settings"), _T("IgnoreBlankLines"), m_bIgnoreBlankLines);
+               theApp.WriteProfileInt(_T("Settings"), _T("IgnoreCase"), m_bIgnoreCase);
+       }
+}
+
+
+BOOL CMainFrame::DoFileOpen(LPCTSTR pszLeft /*=NULL*/, LPCTSTR pszRight /*=NULL*/, BOOL bRecurse /*= FALSE*/)
+{
+       CString strLeft(pszLeft);
+       CString strRight(pszRight);
+       CString strExt;
+       CFileStatus status;
+
+       // if files weren't specified, popup dialog
+       if ((pszLeft == NULL || !CFile::GetStatus(pszLeft, status))
+               || (pszRight == NULL || !CFile::GetStatus(pszRight, status)))
+               
+       {
+               COpenDlg dlg;
+               dlg.m_strLeft = strLeft;
+               dlg.m_strRight = strRight;
+               dlg.m_bRecurse = bRecurse;
+               if (dlg.DoModal() != IDOK)
+                       return FALSE;
+
+               strLeft = dlg.m_strLeft;
+               strRight = dlg.m_strRight;
+               bRecurse = dlg.m_bRecurse;
+               strExt = dlg.m_strParsedExt;
+       }
+
+
+       // check to make sure they are same type
+       TCHAR name[MAX_PATH];
+       BOOL bLeftIsDir=CFile::GetStatus(strLeft, status) && (status.m_attribute & CFile::Attribute::directory);
+       BOOL bRightIsDir = CFile::GetStatus(strRight, status) && (status.m_attribute & CFile::Attribute::directory);
+       if (bLeftIsDir && !bRightIsDir)
+       {
+               split_filename(strRight, NULL, name, NULL);
+               strLeft += _T("\\");
+               strLeft += name;
+               bLeftIsDir = FALSE;
+       }
+       else if (!bLeftIsDir && bRightIsDir)
+       {
+               split_filename(strLeft, NULL, name, NULL);
+               strRight += _T("\\");
+               strRight += name;
+               bRightIsDir = FALSE;
+       }
+
+       if (gWriteLog)
+       {
+               gLog.Write(_T("### Begin Comparison Parameters #############################\r\n")
+                                 _T("\tLeft: %s\r\n")
+                                  _T("\tRight: %s\r\n")
+                                 _T("\tRecurse: %d\r\n")
+                                 _T("\tShowUnique: %d\r\n")
+                                 _T("\tShowIdentical: %d\r\n")
+                                 _T("\tShowDiff: %d\r\n")
+                                 _T("\tHideBak: %d\r\n")
+                                 _T("\tUseVss: %d\r\n")
+                                 _T("\tVssPath: %s\r\n")
+                                 _T("\tBackups: %d\r\n")
+                                 _T("\tIgnoreWS: %d\r\n")
+                                 _T("\tScrollToFirst: %d\r\n")
+                                 _T("### End Comparison Parameters #############################\r\n"),
+                                 strLeft,
+                                 strRight,
+                                 bRecurse,
+                                 m_bShowUnique,
+                                 m_bShowIdent,
+                                 m_bShowDiff,
+                                 m_bHideBak,
+                                 m_bUseVss,
+                                 m_strVssPath,
+                                 m_bBackup,
+                                 m_bIgnoreWhitespace,
+                                 m_bScrollToFirst);
+       }
+
+       // open the diff
+       if (bLeftIsDir)
+       {
+               recursive = bRecurse;
+               if (m_pDirDoc == NULL)
+                       m_pDirDoc = (CDirDoc*)theApp.m_pDirTemplate->OpenDocumentFile(NULL);
+               if (m_pDirDoc != NULL)
+               {
+                       CDiffContext *pCtxt = new CDiffContext(strLeft, strRight);
+                       if (pCtxt != NULL)
+                       {
+                               m_pDirDoc->SetDiffContext(pCtxt);
+                               pCtxt->SetRegExp(strExt);
+                               m_pDirDoc->Rescan();
+                       }
+               }
+       }
+       else
+       {
+               recursive = FALSE;
+               ShowMergeDoc(strLeft, strRight);
+       }
+       return TRUE;
+}
+
+BOOL CMainFrame::CreateBackup(LPCTSTR pszPath)
+{
+       // first, make a backup copy of the original
+       CFileStatus status;
+
+       // create backup copy of file if destination file exists
+       if (m_bBackup
+               && CFile::GetStatus(pszPath, status))
+       {
+               TCHAR path[MAX_PATH], name[_MAX_FNAME], ext[_MAX_EXT];
+               CString s;
+
+               // build the backup filename
+               split_filename(pszPath, path, name, ext);
+               if (*ext != _T('\0'))
+                       s.Format(_T("%s\\%s.%s") BACKUP_FILE_EXT, path, name, ext);
+               else
+                       s.Format(_T("%s\\%s")  BACKUP_FILE_EXT, path, name);
+
+               // get rid of the dest file
+               DeleteFile(s);
+
+               // move the sucker
+               if (!MoveFile(pszPath, s)
+                       && AfxMessageBox(IDS_BACKUP_FAILED_PROMPT, MB_YESNO|MB_ICONQUESTION) != IDYES)
+               {
+                       return FALSE;
+               }
+
+               return TRUE;
+       }
+
+       // we got here because we're either not backing up of there was nothing to backup
+       return TRUE;
+}
+
+BOOL CMainFrame::SyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest)
+{
+       CString strSavePath(pszDest);
+
+       if (!CheckSavePath(strSavePath))
+               return FALSE;
+       
+       if (!CreateBackup(strSavePath))
+               return FALSE;
+       
+       // Now it's just a matter of copying the right file to the left
+       DeleteFile(strSavePath);
+       if (!CopyFile(pszSrc, strSavePath, FALSE))
+       {
+               
+               return FALSE;
+       }
+       
+       // tell the dir view to update itself
+       return TRUE;
+}
+
+void CMainFrame::UpdateCurrentFileStatus(UINT nStatus)
+{
+       if (NULL != m_pDirDoc)
+       {
+               CDirView *pv = m_pDirDoc->GetMainView();
+               if (NULL != pv)
+               {
+                       CListCtrl& lc = pv->GetListCtrl();
+                       int sel = lc.GetNextItem(-1, LVNI_SELECTED);
+                       if (sel != -1)
+                       {
+                               // first change it in the dirlist
+                               POSITION pos = reinterpret_cast<POSITION>(lc.GetItemData(sel));
+                               DIFFITEM di = m_pDirDoc->m_pCtxt->m_dirlist.GetAt(pos);
+                               di.code = (BYTE)nStatus;
+                               m_pDirDoc->m_pCtxt->m_dirlist.SetAt(pos, di);
+                               m_pDirDoc->UpdateItemStatus(sel);
+                               //m_pDirDoc->Redisplay();
+                       }
+               }
+       }
+}
+
+void CMainFrame::OnViewSelectfont() 
+{
+       CHOOSEFONT cf;
+       memset(&cf, 0, sizeof(CHOOSEFONT));
+       cf.lStructSize = sizeof(CHOOSEFONT);
+       cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_FORCEFONTEXIST|CF_SCREENFONTS|CF_SCRIPTSONLY;
+       cf.lpLogFont = &m_lfDiff;
+       if (ChooseFont(&cf))
+       {
+               m_bFontSpecified = TRUE;
+               theApp.WriteProfileInt(_T("Font"), _T("Specified"), m_bFontSpecified);
+               theApp.WriteProfileInt(_T("Font"), _T("Height"), m_lfDiff.lfHeight);
+               theApp.WriteProfileInt(_T("Font"), _T("Width"), m_lfDiff.lfWidth);
+               theApp.WriteProfileInt(_T("Font"), _T("Escapement"), m_lfDiff.lfEscapement);
+               theApp.WriteProfileInt(_T("Font"), _T("Orientation"), m_lfDiff.lfOrientation);
+               theApp.WriteProfileInt(_T("Font"), _T("Weight"), m_lfDiff.lfWeight);
+               theApp.WriteProfileInt(_T("Font"), _T("Italic"), m_lfDiff.lfItalic);
+               theApp.WriteProfileInt(_T("Font"), _T("Underline"), m_lfDiff.lfUnderline);
+               theApp.WriteProfileInt(_T("Font"), _T("StrikeOut"), m_lfDiff.lfStrikeOut);
+               theApp.WriteProfileInt(_T("Font"), _T("CharSet"), m_lfDiff.lfCharSet);
+               theApp.WriteProfileInt(_T("Font"), _T("OutPrecision"), m_lfDiff.lfOutPrecision);
+               theApp.WriteProfileInt(_T("Font"), _T("ClipPrecision"), m_lfDiff.lfClipPrecision);
+               theApp.WriteProfileInt(_T("Font"), _T("Quality"), m_lfDiff.lfQuality);
+               theApp.WriteProfileInt(_T("Font"), _T("PitchAndFamily"), m_lfDiff.lfPitchAndFamily);
+               theApp.WriteProfileString(_T("Font"), _T("FaceName"), m_lfDiff.lfFaceName);
+
+               AfxMessageBox(IDS_FONT_CHANGE, MB_ICONINFORMATION);
+       }
+}
+
+void CMainFrame::GetFontProperties()
+{
+       m_bFontSpecified = theApp.GetProfileInt(_T("Font"), _T("Specified"), FALSE)!=FALSE;
+       if (m_bFontSpecified)
+       {
+               m_lfDiff.lfHeight = theApp.GetProfileInt(_T("Font"), _T("Height"), 10);
+               m_lfDiff.lfWidth = theApp.GetProfileInt(_T("Font"), _T("Width"), 0);
+               m_lfDiff.lfEscapement = theApp.GetProfileInt(_T("Font"), _T("Escapement"), 0);
+               m_lfDiff.lfOrientation = theApp.GetProfileInt(_T("Font"), _T("Orientation"), 0);
+               m_lfDiff.lfWeight = theApp.GetProfileInt(_T("Font"), _T("Weight"), FW_NORMAL);
+               m_lfDiff.lfItalic = (BYTE)theApp.GetProfileInt(_T("Font"), _T("Italic"), FALSE);
+               m_lfDiff.lfUnderline = (BYTE)theApp.GetProfileInt(_T("Font"), _T("Underline"), FALSE);
+               m_lfDiff.lfStrikeOut = (BYTE)theApp.GetProfileInt(_T("Font"), _T("StrikeOut"), FALSE);
+               m_lfDiff.lfCharSet = (BYTE)theApp.GetProfileInt(_T("Font"), _T("CharSet"), ANSI_CHARSET);
+               m_lfDiff.lfOutPrecision = (BYTE)theApp.GetProfileInt(_T("Font"), _T("OutPrecision"), OUT_TT_PRECIS);
+               m_lfDiff.lfClipPrecision = (BYTE)theApp.GetProfileInt(_T("Font"), _T("ClipPrecision"), CLIP_TT_ALWAYS);
+               m_lfDiff.lfQuality = (BYTE)theApp.GetProfileInt(_T("Font"), _T("Quality"), DEFAULT_QUALITY);
+               m_lfDiff.lfPitchAndFamily = (BYTE)theApp.GetProfileInt(_T("Font"), _T("PitchAndFamily"), FF_SWISS | DEFAULT_PITCH);
+               _tcscpy(m_lfDiff.lfFaceName, theApp.GetProfileString(_T("Font"), _T("FaceName"), _T("MS Sans Serif")));
+       }
+       else
+               memset(&m_lfDiff, 0, sizeof(LOGFONT));
+}
+
+void CMainFrame::OnViewUsedefaultfont() 
+{
+       m_bFontSpecified=FALSE;
+       theApp.WriteProfileInt(_T("Font"), _T("Specified"), m_bFontSpecified);
+}
+
+void CMainFrame::OnUpdateViewUsedefaultfont(CCmdUI* pCmdUI) 
+{
+       pCmdUI->Enable(m_bFontSpecified);
+}
+
+void CMainFrame::UpdateResources()
+{
+       CString s;
+       VERIFY(s.LoadString(AFX_IDS_IDLEMESSAGE));
+       m_wndStatusBar.SetPaneText(0, s);
+
+       if (m_pDirDoc != NULL)
+               m_pDirDoc->UpdateResources();
+
+       if (m_pLeft != NULL)
+               m_pLeft->UpdateResources();
+
+       if (m_pRight != NULL)
+               m_pRight->UpdateResources();
+}
+
+
+void CMainFrame::OnHelpContents() 
+{
+       TCHAR path[MAX_PATH], temp[MAX_PATH];
+       GetModuleFileName(NULL, temp, MAX_PATH);
+       split_filename(temp, path, NULL, NULL);
+       _tcscat(path, _T("\\Docs\\index.html"));
+
+       CFileStatus status;
+       if (CFile::GetStatus(path, status))
+               ShellExecute(NULL, _T("open"), path, NULL, NULL, SW_SHOWNORMAL);
+       else
+               ShellExecute(NULL, _T("open"), _T("http://www.geocities.com/SiliconValley/Vista/8632/WinMerge/index.html"), NULL, NULL, SW_SHOWNORMAL);
+
+}
+
+void CMainFrame::OnUpdateHelpContents(CCmdUI* pCmdUI) 
+{
+       pCmdUI->Enable(TRUE);
+}
+
+void CMainFrame::ActivateFrame(int nCmdShow) 
+{
+       if (!m_bFirstTime)
+       {
+               CMDIFrameWnd::ActivateFrame(nCmdShow);
+               return;
+       }
+
+       m_bFirstTime = FALSE;
+
+       WINDOWPLACEMENT wp;
+       GetWindowPlacement(&wp);
+       wp.rcNormalPosition.left=theApp.GetProfileInt(_T("Settings"), _T("MainLeft"),0);
+       wp.rcNormalPosition.top=theApp.GetProfileInt(_T("Settings"), _T("MainTop"),0);
+       wp.rcNormalPosition.right=theApp.GetProfileInt(_T("Settings"), _T("MainRight"),0);
+       wp.rcNormalPosition.bottom=theApp.GetProfileInt(_T("Settings"), _T("MainBottom"),0);
+       wp.showCmd = nCmdShow;
+
+       CRect dsk_rc,rc(wp.rcNormalPosition);
+       GetDesktopWindow()->GetWindowRect(&dsk_rc);
+       if (theApp.GetProfileInt(_T("Settings"), _T("MainMax"), FALSE))
+       {
+               CMDIFrameWnd::ActivateFrame(SW_MAXIMIZE);       
+       }
+       else if (rc.Width() != 0
+               && rc.Height() != 0
+               && wp.rcNormalPosition.left >= dsk_rc.left  // only show in saved position if it fits on screen
+               && wp.rcNormalPosition.top >= dsk_rc.top
+               && wp.rcNormalPosition.right <= dsk_rc.right
+               && wp.rcNormalPosition.bottom <= dsk_rc.bottom)
+       {
+               SetWindowPlacement(&wp);
+       }
+       else
+               CMDIFrameWnd::ActivateFrame(nCmdShow);
+}
+
+void CMainFrame::OnClose() 
+{
+       WINDOWPLACEMENT wp;
+       GetWindowPlacement(&wp);
+       theApp.WriteProfileInt(_T("Settings"), _T("MainLeft"),wp.rcNormalPosition.left);
+       theApp.WriteProfileInt(_T("Settings"), _T("MainTop"),wp.rcNormalPosition.top);
+       theApp.WriteProfileInt(_T("Settings"), _T("MainRight"),wp.rcNormalPosition.right);
+       theApp.WriteProfileInt(_T("Settings"), _T("MainBottom"),wp.rcNormalPosition.bottom);
+       theApp.WriteProfileInt(_T("Settings"), _T("MainMax"), (wp.showCmd == SW_MAXIMIZE));
+       if(m_pMergeDoc != NULL && m_pLeft)
+               ((CChildFrame*)m_pLeft->GetParentFrame())->SavePosition();
+       CMDIFrameWnd::OnClose();
+}
diff --git a/Src/MainFrm.h b/Src/MainFrm.h
new file mode 100644 (file)
index 0000000..0f57855
--- /dev/null
@@ -0,0 +1,131 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// MainFrm.h : interface of the CMainFrame class
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_MAINFRM_H__BBCD4F8C_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
+#define AFX_MAINFRM_H__BBCD4F8C_34E4_11D1_BAA6_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#define BACKUP_FILE_EXT   _T(".bak")
+
+
+class CDiffView;
+class CDirView;
+class CDirDoc;
+
+class CMainFrame : public CMDIFrameWnd
+{
+       DECLARE_DYNAMIC(CMainFrame)
+public:
+       CMainFrame();
+
+// Attributes
+public:        
+       BOOL m_bShowUnique;
+       BOOL m_bShowDiff;
+       BOOL m_bShowIdent;
+       BOOL m_bBackup;
+       LOGFONT m_lfDiff;
+       BOOL m_bFontSpecified;
+
+// Operations
+public:
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CMainFrame)
+       public:
+       virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
+       virtual void ActivateFrame(int nCmdShow = -1);
+       //}}AFX_VIRTUAL
+
+// Implementation
+public:
+       BOOL m_bFirstTime;
+       CString m_strSaveAsPath;
+       void CleanupFileBufs();
+       BOOL m_bIgnoreBlankLines;
+       BOOL m_bIgnoreCase;
+       void UpdateResources();
+       void UpdateCurrentFileStatus(UINT nStatus);
+       BOOL SyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest);
+       BOOL CreateBackup(LPCTSTR pszPath);
+       UINT m_nTabSize;
+       BOOL DoFileOpen(LPCTSTR pszLeft = NULL, LPCTSTR pszRight = NULL, BOOL bRecurse = FALSE);
+       BOOL CheckSavePath(CString& strSavePath);
+       CString m_strVssPath;
+       CString m_strVssProject;
+       BOOL m_bUseVss;
+       BOOL m_bHideBak;
+       BOOL m_bIgnoreWhitespace;
+       BOOL m_bScrollToFirst;
+       void ShowMergeDoc(LPCTSTR szLeft, LPCTSTR szRight);
+       CDiffView *m_pLeft, *m_pRight;
+       CMergeDoc *m_pMergeDoc;
+       CDirDoc *m_pDirDoc;
+       virtual ~CMainFrame();
+#ifdef _DEBUG
+       virtual void AssertValid() const;
+       virtual void Dump(CDumpContext& dc) const;
+#endif
+
+protected:  // control bar embedded members
+       CStatusBar  m_wndStatusBar;
+       CToolBar    m_wndToolBar;
+
+// Generated message map functions
+protected:
+       void GetFontProperties();
+       //{{AFX_MSG(CMainFrame)
+       afx_msg void OnOptionsShowDifferent();
+       afx_msg void OnOptionsShowIdentical();
+       afx_msg void OnOptionsShowUnique();
+       afx_msg void OnUpdateOptionsShowdifferent(CCmdUI* pCmdUI);
+       afx_msg void OnUpdateOptionsShowidentical(CCmdUI* pCmdUI);
+       afx_msg void OnUpdateOptionsShowunique(CCmdUI* pCmdUI);
+       afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+       afx_msg void OnFileOpen();
+       afx_msg void OnUpdateHideBackupFiles(CCmdUI* pCmdUI);
+       afx_msg void OnHelpGnulicense();
+       afx_msg void OnProperties();
+       afx_msg void OnHideBackupFiles();
+       afx_msg void OnViewSelectfont();
+       afx_msg void OnViewUsedefaultfont();
+       afx_msg void OnUpdateViewUsedefaultfont(CCmdUI* pCmdUI);
+       afx_msg void OnHelpContents();
+       afx_msg void OnUpdateHelpContents(CCmdUI* pCmdUI);
+       afx_msg void OnClose();
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+extern CMainFrame *mf;
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_MAINFRM_H__BBCD4F8C_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
diff --git a/Src/Merge.cpp b/Src/Merge.cpp
new file mode 100644 (file)
index 0000000..b8e0bde
--- /dev/null
@@ -0,0 +1,482 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// Merge.cpp : Defines the class behaviors for the application.
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+
+#include "MainFrm.h"
+#include "ChildFrm.h"
+#include "DirFrame.h"
+#include "MergeDoc.h"
+#include "DirDoc.h"
+#include "DiffView.h"
+#include "DirView.h"
+#include "Splash.h"
+#include "version.h"
+#include "statlink.h"
+#include "logfile.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeApp
+
+BEGIN_MESSAGE_MAP(CMergeApp, CWinApp)
+       //{{AFX_MSG_MAP(CMergeApp)
+       ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
+       ON_COMMAND(ID_VIEW_LANGUAGE, OnViewLanguage)
+       //}}AFX_MSG_MAP
+       // Standard file based document commands
+       //ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
+       //ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
+       // Standard print setup command
+       ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
+END_MESSAGE_MAP()
+
+#ifdef _DEBUG
+void SillyTestCrap();
+#endif
+
+extern CLogFile gLog;
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeApp construction
+
+CMergeApp::CMergeApp()
+: m_lang(IDR_MAINFRAME, IDR_MAINFRAME)
+{
+       // TODO: add construction code here,
+       // Place all significant initialization in InitInstance
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// The one and only CMergeApp object
+
+CMergeApp theApp;
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeApp initialization
+
+
+
+BOOL CMergeApp::InitInstance()
+{
+#ifdef _DEBUG
+       SillyTestCrap();
+#endif
+
+       ::CoInitialize(NULL);
+
+       // CG: The following block was added by the Splash Screen component.
+       {
+               CCommandLineInfo cmdInfo;
+               ParseCommandLine(cmdInfo);
+               CSplashWnd::EnableSplashScreen(cmdInfo.m_bShowSplash);
+       }
+
+       // Standard initialization
+       // If you are not using these features and wish to reduce the size
+       //  of your final executable, you should remove from the following
+       //  the specific initialization routines you do not need.
+
+#ifdef _AFXDLL
+       Enable3dControls();                     // Call this when using MFC in a shared DLL
+#else
+       Enable3dControlsStatic();       // Call this when linking to MFC statically
+#endif
+
+       // Change the registry key under which our settings are stored.
+       // You should modify this string to be something appropriate
+       // such as the name of your company or organization.
+       SetRegistryKey(_T("Thingamahoochie"));
+
+       LoadStdProfileSettings();  // Load standard INI file options (including MRU)
+
+       // Register the application's document templates.  Document templates
+       //  serve as the connection between documents, frame windows and views.
+
+       m_lang.SetLogFile(&gLog);
+       m_lang.InitializeLanguage();
+
+       m_pDiffTemplate = new CMultiDocTemplate(
+               IDR_MERGETYPE,
+               RUNTIME_CLASS(CMergeDoc),
+               RUNTIME_CLASS(CChildFrame), // custom MDI child frame
+               RUNTIME_CLASS(CDiffView));
+       AddDocTemplate(m_pDiffTemplate);
+       m_pDirTemplate = new CMultiDocTemplate(
+               IDR_MERGETYPE,
+               RUNTIME_CLASS(CDirDoc),
+               RUNTIME_CLASS(CDirFrame), // custom MDI child frame
+               RUNTIME_CLASS(CDirView));
+       AddDocTemplate(m_pDirTemplate);
+
+       // create main MDI Frame window
+       CMainFrame* pMainFrame = new CMainFrame;
+       if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
+               return FALSE;
+       m_pMainWnd = pMainFrame;
+
+       // Parse command line for standard shell commands, DDE, file open
+       //CCommandLineInfo cmdInfo;
+       //ParseCommandLine(cmdInfo);
+
+       /* Dispatch commands specified on the command line
+       if( cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew )
+               cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
+       if (!ProcessShellCommand(cmdInfo))
+               return FALSE;*/
+
+       // The main window has been initialized, so show and update it.
+       //pMainFrame->ShowWindow(m_nCmdShow);
+       pMainFrame->ActivateFrame(m_nCmdShow);
+       pMainFrame->UpdateWindow();
+
+       CStringArray files;
+       UINT nFiles=0;
+       BOOL recurse=FALSE;
+       files.SetSize(2);
+       for (int i = 1; i < __argc; i++)
+       {
+               LPCTSTR pszParam = __targv[i];
+               if (pszParam[0] == '-' || pszParam[0] == '/')
+               {
+                       // remove flag specifier
+                       ++pszParam;
+
+                       if (!_tcsicmp(pszParam, _T("r")))
+                               recurse=TRUE;
+               }
+               else
+               {
+                       files.SetAtGrow(nFiles, pszParam);
+                       nFiles++;
+               }
+       }
+
+       if (nFiles>2)
+       {
+               pMainFrame->m_strSaveAsPath = files[2];
+               pMainFrame->DoFileOpen(files[0], files[1], recurse);
+       }
+       else if (nFiles>1)
+       {
+               pMainFrame->m_strSaveAsPath = _T("");
+               pMainFrame->DoFileOpen(files[0], files[1], recurse);
+       }
+       else if (nFiles>0)
+       {
+               pMainFrame->m_strSaveAsPath = _T("");
+               pMainFrame->DoFileOpen(files[0], "", recurse);
+       }
+
+       return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CAboutDlg dialog used for App About
+
+class CAboutDlg : public CDialog
+{
+public:
+       CAboutDlg();
+
+// Dialog Data
+       //{{AFX_DATA(CAboutDlg)
+       enum { IDD = IDD_ABOUTBOX };
+       CStaticLink     m_ctlCompany;
+       CStatic m_ctlEmail;
+       CStaticLink     m_ctlWWW;
+       CString m_strVersion;
+       //}}AFX_DATA
+
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CAboutDlg)
+       protected:
+       virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+       //{{AFX_MSG(CAboutDlg)
+       virtual BOOL OnInitDialog();
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
+{
+       //{{AFX_DATA_INIT(CAboutDlg)
+       m_strVersion = _T("");
+       //}}AFX_DATA_INIT
+}
+
+void CAboutDlg::DoDataExchange(CDataExchange* pDX)
+{
+       CDialog::DoDataExchange(pDX);
+       //{{AFX_DATA_MAP(CAboutDlg)
+       DDX_Control(pDX, IDC_COMPANY, m_ctlCompany);
+       DDX_Control(pDX, IDC_EMAIL, m_ctlEmail);
+       DDX_Control(pDX, IDC_WWW, m_ctlWWW);
+       DDX_Text(pDX, IDC_VERSION, m_strVersion);
+       //}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
+       //{{AFX_MSG_MAP(CAboutDlg)
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+// App command to run the dialog
+void CMergeApp::OnAppAbout()
+{
+       CAboutDlg aboutDlg;
+       aboutDlg.DoModal();
+}
+
+BOOL CAboutDlg::OnInitDialog() 
+{
+       CDialog::OnInitDialog();
+       
+       CVersionInfo version;
+       AfxFormatString1(m_strVersion, IDS_VERSION_FMT, version.GetProductVersion());
+
+       m_ctlCompany.m_link = _T("http://www.geocities.com/SiliconValley/Vista/8632");
+       m_ctlWWW.m_link = _T("http://www.geocities.com/SiliconValley/Vista/8632/ts_winmerge.html");
+
+       UpdateData(FALSE);
+       
+       return TRUE;  // return TRUE unless you set the focus to a control
+                     // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeApp commands
+
+BOOL SelectFile(CString& path, LPCTSTR root_path /*=NULL*/, 
+                        LPCTSTR title /*= _T("Open")*/, 
+                        UINT filter /*=0*/,
+                        BOOL is_open /*=TRUE*/) 
+{
+       CString s;           
+                   
+       if (filter != 0)
+               VERIFY(s.LoadString(filter)); 
+       else
+               VERIFY(s.LoadString(IDS_ALLFILES)); 
+       CFileDialog dlg(is_open, NULL, NULL, 
+                                   OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, s);
+       dlg.m_ofn.lpstrTitle = (LPCTSTR)title;
+       dlg.m_ofn.lpstrInitialDir = (LPTSTR)root_path;
+
+       TCHAR buf[MAX_PATH*2] = _T("Directory Selection");
+       dlg.m_ofn.lpstrFile = buf;
+       dlg.m_ofn.nMaxFile = MAX_PATH*2;
+       if (dlg.DoModal()==IDOK)
+       {
+               path = dlg.GetPathName(); 
+               return TRUE;
+       }
+       path.Empty();
+       return FALSE;      
+}
+
+BOOL CMergeApp::PreTranslateMessage(MSG* pMsg)
+{
+       // CG: The following lines were added by the Splash Screen component.
+       if (CSplashWnd::PreTranslateAppMessage(pMsg))
+               return TRUE;
+
+       return CWinApp::PreTranslateMessage(pMsg);
+}
+
+void CMergeApp::OnViewLanguage() 
+{
+       if (m_lang.DoModal()==IDOK)
+       {
+               m_lang.ReloadMenu();
+               //m_LangDlg.UpdateDocTitle();
+               mf->UpdateResources();
+       }
+}
+
+
+#ifdef _DEBUG
+#define __STDC__ 1
+#include "RegExp.h"
+#include "direct.h"
+
+typedef BOOL (*RecursiveFindCallback)(WIN32_FIND_DATA &fd, LPCTSTR pszPath, LPVOID pUserData);
+TCHAR recurse_dir_regex[MAX_PATH];
+
+BOOL MyRecursiveFindCallback(WIN32_FIND_DATA &fd, LPCTSTR pszPath, LPVOID pUserData)
+{
+       TRACE(_T("%s\\%s\n"), pszPath, fd.cFileName);
+       return TRUE;
+}
+
+BOOL recursive_find_regex(CRegExp& regex, 
+                                                 RecursiveFindCallback pCallback,
+                                                 LPVOID pUserData)
+{
+       WIN32_FIND_DATA fd;
+       HANDLE hff;
+       TCHAR *p;
+       
+       // open the directory for reading
+       if ((hff = FindFirstFile(_T("*.*"), &fd)) != INVALID_HANDLE_VALUE)
+       {
+               do {
+
+                       // if the current entry is a directory, recurse into it
+                       if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                       {
+                               if (_tcscmp(fd.cFileName,_T("."))
+                                       && _tcscmp(fd.cFileName,_T("..")))
+                               {
+                                       if (_tchdir(fd.cFileName)==0)
+                                       { 
+                                               _tcscat(recurse_dir_regex,_T("\\"));
+                                               _tcscat(recurse_dir_regex,fd.cFileName);
+
+                                               if (regex.RegFind(fd.cFileName) != -1)
+                                                       if (!pCallback(fd, recurse_dir_regex, pUserData))
+                                                               return FALSE;
+
+                                               if (!recursive_find_regex(regex, pCallback, pUserData))
+                                                       return FALSE;
+
+                                               _tchdir(_T(".."));
+                                               if ((p=_tcsrchr(recurse_dir_regex,_T('\\')))!=NULL)
+                                                       *p=_T('\0');
+                                       }
+                                       //else
+                                       //      add_err(recurse_dir_regex, fd.cFileName, _T("Couldn't read folder"));
+                                       
+                               }
+                       }
+                       // entry is a file, delete it
+                       else
+                       {
+                               if (regex.RegFind(fd.cFileName) != -1)
+                                       if (!pCallback(fd, recurse_dir_regex, pUserData))
+                                               return FALSE;
+                       }
+               } while (FindNextFile(hff,&fd));
+               FindClose(hff);
+       }
+       else
+       {
+               //add_err(recurse_dir, _T(""), _T("No permission to open folder"));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+
+BOOL RecursiveFindRegex(LPCTSTR szRegex, 
+                                               LPCTSTR szStartPath, 
+                                               RecursiveFindCallback pCallback,
+                                               LPVOID pUserData)
+{
+       CRegExp regex;
+       regex.RegComp( szRegex );
+
+       // change the current drive if drive mapped
+       if(szStartPath[1]==_T(':'))
+       {
+               CString s(szStartPath[0]);
+               s.MakeUpper();
+               int drive = s[0]-_T('A')+1;
+               if( _chdrive(drive) != 0)
+                       return FALSE;
+       }       
+       
+
+       // change to the folder we want to delete
+       CString s(szStartPath);
+       if (s.Right(1) == ":")
+               s += '\\';
+       if (_tchdir(s)!=0)
+               return FALSE;
+       
+       _tcscpy(recurse_dir_regex, szStartPath);
+       return recursive_find_regex(regex, pCallback, pUserData);
+}
+
+
+void SillyTestCrap()
+{
+       TCHAR teststring[][MAX_PATH] = {
+               "test.cpp",
+                       "test.c",
+                       "test.h",
+                       "test.x",
+                       "test.cpp2",
+                       ".cpp",
+                       "cpp",
+                       "acpp",
+                       ""
+       };
+       TCHAR ext[] = _T("*.cpp;*.h;*.c");
+       LPTSTR p;
+       CString strPattern(_T(".*\\.("));
+
+       // parse the extensions
+       p = _tcstok(ext, _T(";,|*. \n\r\n"));
+       if (p == NULL)
+               return;
+
+       while (p != NULL)
+       {
+               strPattern += p;                
+               p = _tcstok(NULL, _T(";,|*. \n\r\n"));
+               if (p != NULL)
+                       strPattern += _T('|');
+       }
+       strPattern += _T(")$");
+
+       RecursiveFindRegex(strPattern, _T("f:\\programs\\merge"), MyRecursiveFindCallback, (LPVOID)AfxGetApp());
+
+       /*CRegExp r;
+       r.RegComp( strPattern );
+       for (UINT i=0; *teststring[i] != NULL; i++)
+       {
+               if (r.RegFind((LPTSTR)teststring[i]) != -1)
+                       TRACE("%s: Match\n", teststring[i]);
+               else
+                       TRACE("%s: No match\n", teststring[i]);
+       }*/
+
+}
+#endif
+
+int CMergeApp::ExitInstance() 
+{
+       ::CoUninitialize();     
+       return CWinApp::ExitInstance();
+}
diff --git a/Src/Merge.dsp b/Src/Merge.dsp
new file mode 100644 (file)
index 0000000..054b783
--- /dev/null
@@ -0,0 +1,982 @@
+# Microsoft Developer Studio Project File - Name="Merge" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=Merge - Win32 Bound Check
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Merge.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Merge.mak" CFG="Merge - Win32 Bound Check"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Merge - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "Merge - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "Merge - Win32 Bound Check" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""$/WinMerge", FAAAAAAA"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# PROP BASE Use_MFC 6
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 6
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /Od /I "ms" /I "." /I "..\releases\current\common" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_AFXDLL" /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1 /Yu"stdafx.h" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
+# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386
+# ADD LINK32 version.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"Release/WinMerge.exe" /verbose:lib
+# SUBTRACT LINK32 /pdb:none
+# Begin Special Build Tool
+TargetPath=.\Release\WinMerge.exe
+SOURCE="$(InputPath)"
+PostBuild_Cmds=StampVer -vstampver.inf -i4 -j4 -o2 $(TargetPath)
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# PROP BASE Use_MFC 6
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 6
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "ms" /I "." /I "..\releases\current\common" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_AFXDLL" /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1 /FR /Yu"stdafx.h" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 version.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Debug/WinMerge.exe" /pdbtype:sept
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# PROP BASE Use_MFC 6
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Merge___"
+# PROP BASE Intermediate_Dir "Merge___"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 6
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Merge___"
+# PROP Intermediate_Dir "Merge___"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "ms" /I "." /I "..\releases\current\common" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_AFXDLL" /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1 /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I "ms" /I "." /I "..\releases\current\common" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_AFXDLL" /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1 /Yu"stdafx.h" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 version.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Debug/WinMerge.exe" /pdbtype:sept
+# ADD LINK32 version.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Debug/WinMerge.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Merge - Win32 Release"
+# Name "Merge - Win32 Debug"
+# Name "Merge - Win32 Bound Check"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\ANALYZE.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\ChildFrm.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMPBUF.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\CONTEXT.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\coretools.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Diff.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\DiffContext.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DiffView.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Dir.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DirDoc.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIRENT.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\DirFrame.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\DirView.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\ED.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\EditFile.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\FNMATCH.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\GnuVersion.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=.\IFDEF.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\IO.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\LanguageSelect.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\listvwex.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\LogFile.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\MainFrm.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Merge.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Merge.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\MergeDoc.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\NORMAL.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\OpenDlg.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\PropGeneral.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\PropVss.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\REGEX.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\RegExp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\RegKey.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\SIDE.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Splash.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\StatLink.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /Yc"stdafx.h"
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# ADD CPP /Yc"stdafx.h"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# ADD BASE CPP /Yc"stdafx.h"
+# ADD CPP /Yc"stdafx.h"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\StringEx.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\SuperComboBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\1.5\Common\textfile.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\UTIL.C
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+# SUBTRACT BASE CPP /YX /Yc /Yu
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\version.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\VssPrompt.cpp
+
+!IF  "$(CFG)" == "Merge - Win32 Release"
+
+# SUBTRACT CPP /D "HAVE_STDLIB_H" /D "STDC_HEADERS" /D HAVE_STRING_H=1 /D PR_FILE_NAME=\"pr\" /D DIFF_PROGRAM=\"diff\" /D "REGEX_MALLOC" /D "__MSC__" /D "__NT__" /D USG=1
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "Merge - Win32 Bound Check"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=D:\data\html\WinMergeChanges.html
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ChildFrm.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMPBUF.H
+# End Source File
+# Begin Source File
+
+SOURCE=.\CONFIG.H
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\Common\coretools.h
+# End Source File
+# Begin Source File
+
+SOURCE=".\DIFF-decl.H"
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIFF.H
+# End Source File
+# Begin Source File
+
+SOURCE=.\DiffContext.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DiffView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DirDoc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DIRENT.H
+# End Source File
+# Begin Source File
+
+SOURCE=.\DirFrame.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DirView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EditFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FNMATCH.H
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\LanguageSelect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\listvwex.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\LogFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MainFrm.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Merge.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MergeDoc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OpenDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PropGeneral.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PropVss.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\REGEX.H
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\RegExp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\Common\RegKey.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Splash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\StatLink.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\StringEx.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\SuperComboBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SYSTEM.H
+# End Source File
+# Begin Source File
+
+SOURCE=..\Common\textfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Releases\Current\common\version.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VssPrompt.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\binary.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\bmp00001.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\equal.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\folder1.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Merge.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Merge.rc2
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\MergeDoc.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\notequal.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\rfolder.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\splash1.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Splsh16.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Toolbar.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\unknown.bmp
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\winmerge.bmp
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\stampver.inf
+# End Source File
+# End Target
+# End Project
+# Section Merge : {6F747475-446E-6C62-436C-6B0000003100}
+#      1:10:IDB_SPLASH:103
+#      2:21:SplashScreenInsertKey:4.0
+# End Section
+# Section Merge : {00312E6C-0754-0055-90BD-550078075500}
+#      1:19:IDR_POPUP_ABOUT_DLG:104
+# End Section
diff --git a/Src/Merge.h b/Src/Merge.h
new file mode 100644 (file)
index 0000000..dcaa1bb
--- /dev/null
@@ -0,0 +1,80 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// Merge.h : main header file for the MERGE application
+//
+
+#if !defined(AFX_MERGE_H__BBCD4F88_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
+#define AFX_MERGE_H__BBCD4F88_34E4_11D1_BAA6_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#ifndef __AFXWIN_H__
+       #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h"       // main symbols
+#include "MergeDoc.h"
+#include "languageselect.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeApp:
+// See Merge.cpp for the implementation of this class
+//
+
+class CMergeApp : public CWinApp
+{
+public:
+       virtual BOOL PreTranslateMessage(MSG* pMsg);
+       CLanguageSelect m_lang;
+       CMergeApp();
+       CMultiDocTemplate* m_pDiffTemplate;
+       CMultiDocTemplate* m_pDirTemplate;
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CMergeApp)
+       public:
+       virtual BOOL InitInstance();
+       virtual int ExitInstance();
+       //}}AFX_VIRTUAL
+
+// Implementation
+
+       //{{AFX_MSG(CMergeApp)
+       afx_msg void OnAppAbout();
+       afx_msg void OnViewLanguage();
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+extern CMergeApp theApp;
+
+/////////////////////////////////////////////////////////////////////////////
+CMergeDoc *GetDoc();
+BOOL SelectFile(CString& path, LPCTSTR root_path = NULL, 
+                        LPCTSTR title = _T("Open"), 
+                        UINT filter =0,
+                        BOOL is_open =TRUE);
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_MERGE_H__BBCD4F88_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
diff --git a/Src/Merge.rc b/Src/Merge.rc
new file mode 100644 (file)
index 0000000..a2dd2ad
--- /dev/null
@@ -0,0 +1,822 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#define _AFX_NO_OLE_RESOURCES\r\n"
+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+    "\r\n"
+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+    "#ifdef _WIN32\r\n"
+    "LANGUAGE 9, 1\r\n"
+    "#pragma code_page(1252)\r\n"
+    "#endif\r\n"
+    "#include ""res\\Merge.rc2""  // non-Microsoft Visual C++ edited resources\r\n"
+    "#include ""afxres.rc""         // Standard components\r\n"
+    "#include ""afxprint.rc""       // printing/print preview resources\r\n"
+    "#endif\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME           ICON    DISCARDABLE     "res\\Merge.ico"
+IDR_MERGETYPE           ICON    DISCARDABLE     "res\\MergeDoc.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_POPUP_DIFFVIEW MENU DISCARDABLE 
+BEGIN
+    POPUP "_POPUP_"
+    BEGIN
+        MENUITEM "Copy to other side",          ID_POPUP_COPYTOOTHERSIDE
+        MENUITEM "Copy from other side",        ID_POPUP_COPYFROMOTHERSIDE
+        MENUITEM SEPARATOR
+        MENUITEM "Copy all diffs to other side...", 
+                                                ID_POPUP_COPYALLDIFFSTOOTHERSIDE
+
+        MENUITEM "Copy all diffs from other side...", 
+                                                ID_POPUP_COPYALLDIFFSFROMOTHERSIDE
+
+        MENUITEM SEPARATOR
+        MENUITEM "Undo",                        ID_UNDO
+        MENUITEM SEPARATOR
+        MENUITEM "Save Changes...",             ID_POPUP_SAVE
+        MENUITEM "Edit file",                   ID_POPUP_EDITFILE
+    END
+END
+
+IDR_MAINFRAME MENU PRELOAD DISCARDABLE 
+BEGIN
+    POPUP "&File"
+    BEGIN
+        MENUITEM "&Open...\tCtrl+O",            ID_FILE_OPEN
+        MENUITEM "&Save\tCtrl+S",               ID_FILE_SAVE
+        MENUITEM SEPARATOR
+        MENUITEM "E&xit",                       ID_APP_EXIT
+    END
+    POPUP "&Edit"
+    BEGIN
+        MENUITEM "&Undo\tCtrl+Z",               ID_UNDO
+        MENUITEM SEPARATOR
+        MENUITEM "&Next difference\tTab",       ID_NEXTDIFF
+        MENUITEM "&Previous difference\tShift+Tab", ID_PREVDIFF
+        MENUITEM SEPARATOR
+        MENUITEM "Copy to &Right\tArrow right", ID_L2R
+        MENUITEM "Copy to &Left\tArrow left",   ID_R2L
+        MENUITEM SEPARATOR
+        MENUITEM "Copy All to Right",           ID_ALL_RIGHT
+        MENUITEM "Copy All to Left",            ID_ALL_LEFT
+        MENUITEM SEPARATOR
+        MENUITEM "Pr&operties",                 ID_PROPERTIES
+    END
+    POPUP "&View"
+    BEGIN
+        MENUITEM "Show &Identical Files",       ID_OPTIONS_SHOWIDENTICAL
+        MENUITEM "Show &Different Files",       ID_OPTIONS_SHOWDIFFERENT
+        MENUITEM "Show &Unique Files",          ID_OPTIONS_SHOWUNIQUE
+        MENUITEM "Hide *.&BAK Files",           ID_HIDE_BACKUP_FILES
+        MENUITEM SEPARATOR
+        MENUITEM "Select &Font...",             ID_VIEW_SELECTFONT
+        MENUITEM "Use Default F&ont",           ID_VIEW_USEDEFAULTFONT
+        MENUITEM SEPARATOR
+        MENUITEM "&Toolbar",                    ID_VIEW_TOOLBAR
+        MENUITEM "&Status Bar",                 ID_VIEW_STATUS_BAR
+        MENUITEM SEPARATOR
+        MENUITEM "&Language...",                ID_VIEW_LANGUAGE
+    END
+    POPUP "&Window"
+    BEGIN
+        MENUITEM "Close",                       ID_FILE_CLOSE
+        MENUITEM SEPARATOR
+        MENUITEM "Tile &Horizontally",          ID_WINDOW_TILE_HORZ
+        MENUITEM "Tile &Vertically",            ID_WINDOW_TILE_VERT
+        MENUITEM "&Cascade",                    ID_WINDOW_CASCADE
+    END
+    POPUP "&Help"
+    BEGIN
+        MENUITEM "&Contents...",                ID_HELP_CONTENTS
+        MENUITEM SEPARATOR
+        MENUITEM "GNU Public License...",       ID_HELP_GNULICENSE
+        MENUITEM "&About WinMerge...",          ID_APP_ABOUT
+    END
+END
+
+IDR_POPUP_DIRVIEW MENU DISCARDABLE 
+BEGIN
+    POPUP "_POPUP_"
+    BEGIN
+        MENUITEM "Copy file to left",           ID_DIR_COPY_FILE_TO_LEFT
+        MENUITEM "Copy file to right",          ID_DIR_COPY_FILE_TO_RIGHT
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE 
+BEGIN
+    "C",            ID_EDIT_COPY,           VIRTKEY, CONTROL, NOINVERT
+    "N",            ID_FILE_NEW,            VIRTKEY, CONTROL, NOINVERT
+    "O",            ID_FILE_OPEN,           VIRTKEY, CONTROL, NOINVERT
+    "P",            ID_FILE_PRINT,          VIRTKEY, CONTROL, NOINVERT
+    "S",            ID_FILE_SAVE,           VIRTKEY, CONTROL, NOINVERT
+    "V",            ID_EDIT_PASTE,          VIRTKEY, CONTROL, NOINVERT
+    VK_BACK,        ID_EDIT_UNDO,           VIRTKEY, ALT, NOINVERT
+    VK_DELETE,      ID_EDIT_CUT,            VIRTKEY, SHIFT, NOINVERT
+    VK_DOWN,        ID_NEXTDIFF,            VIRTKEY, NOINVERT
+    VK_F6,          ID_NEXT_PANE,           VIRTKEY, NOINVERT
+    VK_F6,          ID_PREV_PANE,           VIRTKEY, SHIFT, NOINVERT
+    VK_INSERT,      ID_EDIT_COPY,           VIRTKEY, CONTROL, NOINVERT
+    VK_INSERT,      ID_EDIT_PASTE,          VIRTKEY, SHIFT, NOINVERT
+    VK_TAB,         ID_NEXTDIFF,            VIRTKEY, NOINVERT
+    VK_TAB,         ID_PREVDIFF,            VIRTKEY, SHIFT, NOINVERT
+    VK_UP,          ID_PREVDIFF,            VIRTKEY, NOINVERT
+    "X",            ID_EDIT_CUT,            VIRTKEY, CONTROL, NOINVERT
+    "Z",            ID_EDIT_UNDO,           VIRTKEY, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 198, 103
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About WinMerge"
+FONT 8, "MS Sans Serif"
+BEGIN
+    ICON            IDR_MAINFRAME,IDC_STATIC,24,14,21,20
+    LTEXT           "WinMerge",IDC_STATIC,63,13,63,8,SS_NOPREFIX
+    LTEXT           "© 1999 ",IDC_STATIC,7,49,27,8
+    DEFPUSHBUTTON   "OK",IDOK,159,7,32,14,WS_GROUP
+    LTEXT           "Version 1.0",IDC_VERSION,64,23,84,8,SS_NOPREFIX
+    LTEXT           "All rights reserved.",IDC_STATIC,7,60,170,8
+    LTEXT           "Visit the WinMerge HomePage!",IDC_WWW,7,76,184,8
+    LTEXT           "grimmd@geocities.com",IDC_EMAIL,7,88,170,8
+    CONTROL         "",IDC_STATIC,"Static",SS_ETCHEDHORZ,7,72,184,1
+    LTEXT           "Thingamahoochie Software",IDC_COMPANY,35,49,91,8
+END
+
+IDD_OPEN DIALOGEX 0, 0, 352, 98
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Files or Directories..."
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    LTEXT           "&Left:",IDC_STATIC,38,24,15,8
+    COMBOBOX        IDC_LEFT_COMBO,56,22,182,94,CBS_DROPDOWN | 
+                    CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP,
+                    WS_EX_ACCEPTFILES
+    PUSHBUTTON      "&Browse...",IDC_LEFT_BUTTON,241,22,38,12
+    LTEXT           "&Right:",IDC_STATIC,33,40,20,8
+    COMBOBOX        IDC_RIGHT_COMBO,56,38,182,95,CBS_DROPDOWN | 
+                    CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP,
+                    WS_EX_ACCEPTFILES
+    PUSHBUTTON      "Bro&wse...",IDC_RIGHT_BUTTON,241,38,38,12
+    LTEXT           "E&xtensions:",IDC_STATIC,16,56,37,8
+    COMBOBOX        IDC_EXT_COMBO,56,54,151,95,CBS_DROPDOWN | 
+                    CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Re&cursive",IDC_RECURS_CHECK,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,35,77,48,10
+    DEFPUSHBUTTON   "OK",IDOK,295,11,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,295,28,50,14
+    GROUPBOX        "Files or Directories to Compare",IDC_STATIC,7,7,279,84
+END
+
+IDD_EDITFILE DIALOG DISCARDABLE  0, 0, 510, 350
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Sans Serif"
+BEGIN
+    PUSHBUTTON      "Save",IDOK,453,7,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,453,24,50,14
+    EDITTEXT        IDC_FILE_EDIT,7,7,437,336,ES_MULTILINE | ES_NOHIDESEL | 
+                    ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL
+    PUSHBUTTON      "Cu&t",IDC_CUT,453,64,50,14
+    PUSHBUTTON      "&Copy",IDC_COPY,453,82,50,14
+    PUSHBUTTON      "&Paste",IDC_PASTE,453,99,50,14
+    PUSHBUTTON      "&Undo",IDC_UNDO,453,117,50,14
+END
+
+IDD_VSS DIALOG DISCARDABLE  0, 0, 258, 105
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Visual SourceSafe"
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT           "",IDC_STATIC,7,7,244,47,SS_SUNKEN
+    LTEXT           "IDS_SAVEVSS_FMT",IDC_MESSAGE,14,13,231,34
+    DEFPUSHBUTTON   "Check Out",IDOK,33,83,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,173,83,50,14
+    PUSHBUTTON      "Save As...",IDSAVEAS,103,83,50,14
+    LTEXT           "Project:",IDC_STATIC,9,61,25,8
+    COMBOBOX        IDC_PROJECT_COMBO,36,58,215,114,CBS_DROPDOWN | 
+                    CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_PROP_VSS DIALOG DISCARDABLE  0, 0, 259, 157
+STYLE WS_CHILD | WS_DISABLED | WS_CAPTION
+CAPTION "SourceSafe"
+FONT 8, "MS Sans Serif"
+BEGIN
+    GROUPBOX        "Settings",IDC_STATIC,7,7,245,54
+    CONTROL         "Enable Visual &SourceSafe integration",IDC_DOVSS_CHECK,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,21,133,10
+    LTEXT           "&Path to SS.EXE:",IDC_VSS_L1,16,42,53,8
+    EDITTEXT        IDC_PATH_EDIT,72,40,131,12,ES_AUTOHSCROLL
+    PUSHBUTTON      "&Browse...",IDC_BROWSE_BUTTON,206,40,42,12
+END
+
+IDD_PROPPAGE_LARGE DIALOG DISCARDABLE  0, 0, 235, 156
+STYLE WS_CHILD | WS_DISABLED | WS_CAPTION
+CAPTION "General"
+FONT 8, "MS Sans Serif"
+BEGIN
+    GROUPBOX        "Settings",IDC_STATIC,7,7,221,78
+    CONTROL         "&Backup original file",IDC_BACKUP_CHECK,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,17,21,75,10
+    CONTROL         "Automatically &scroll to first difference",
+                    IDC_SCROLL_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    17,33,129,10
+    CONTROL         "Ignore &whitespace",IDC_WHITESPACE_CHECK,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,17,45,74,10
+    CONTROL         "Ignore blan&k lines",IDC_IGNBLANKS_CHECK,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,17,57,71,10
+    CONTROL         "Ignore &case",IDC_IGNCASE_CHECK,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,17,69,53,10
+    GROUPBOX        "Tabs",IDC_STATIC,7,92,221,30
+    LTEXT           "&Tab size:",IDC_STATIC,15,106,30,8
+    EDITTEXT        IDC_TAB_EDIT,47,105,27,12,ES_AUTOHSCROLL
+END
+
+IDD_LANGUAGE_SELECT DIALOG DISCARDABLE  0, 0, 213, 111
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Language"
+FONT 8, "MS Sans Serif"
+BEGIN
+    PUSHBUTTON      "OK",IDOK,154,16,52,14
+    PUSHBUTTON      "Cancel",IDCANCEL,154,33,52,14
+    LISTBOX         30001,5,16,141,89,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | 
+                    WS_TABSTOP
+    LTEXT           "Available languages:",IDC_STATIC,7,7,66,8
+END
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", "\0"
+            VALUE "FileDescription", "WinMerge Application\0"
+            VALUE "FileVersion", "999.999.999.999\0"
+            VALUE "InternalName", "WinMerge\0"
+            VALUE "LegalCopyright", "© 1999 Dean P. Grimm\0"
+            VALUE "LegalTrademarks", "\0"
+            VALUE "OriginalFilename", "WinMerge.EXE\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "WinMerge\0"
+            VALUE "ProductVersion", "999.999.999.999\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_ABOUTBOX, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 191
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 96
+    END
+
+    IDD_OPEN, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 345
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 91
+    END
+
+    IDD_EDITFILE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 503
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 343
+    END
+
+    IDD_VSS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 251
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 97
+    END
+
+    IDD_PROP_VSS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 252
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 150
+    END
+
+    IDD_PROPPAGE_LARGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 228
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 149
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Toolbar
+//
+
+IDR_MAINFRAME TOOLBAR DISCARDABLE  16, 15
+BEGIN
+    BUTTON      ID_FILE_OPEN
+    BUTTON      ID_FILE_SAVE
+    SEPARATOR
+    BUTTON      ID_UNDO
+    SEPARATOR
+    BUTTON      ID_NEXTDIFF
+    BUTTON      ID_PREVDIFF
+    SEPARATOR
+    BUTTON      ID_L2R
+    BUTTON      ID_R2L
+    SEPARATOR
+    BUTTON      ID_PROPERTIES
+    SEPARATOR
+    BUTTON      ID_ALL_RIGHT
+    BUTTON      ID_ALL_LEFT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDR_MAINFRAME           BITMAP  MOVEABLE PURE   "res\\Toolbar.bmp"
+IDB_SPLASH              BITMAP  DISCARDABLE     "res\\splash1.bmp"
+IDB_LFOLDER             BITMAP  DISCARDABLE     "res\\folder1.bmp"
+IDB_EQUAL               BITMAP  DISCARDABLE     "res\\equal.bmp"
+IDB_NOTEQUAL            BITMAP  DISCARDABLE     "res\\notequal.bmp"
+IDB_RFOLDER             BITMAP  DISCARDABLE     "res\\rfolder.bmp"
+IDB_UNKNOWN             BITMAP  DISCARDABLE     "res\\unknown.bmp"
+IDB_BINARY              BITMAP  DISCARDABLE     "res\\binary.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE PRELOAD DISCARDABLE 
+BEGIN
+    IDR_MAINFRAME           "WinMerge"
+    IDR_MERGETYPE           "\nWinMerge\nWinMerge\n\n\nWinMerge.Document\nWinMerge Document"
+    IDS_VERSION_FMT         "Version %1"
+    IDS_ALLFILES            "All Files (*.*)|*.*||"
+    IDS_CONFIRM_ALL_LEFT    "Are you sure you want to copy all differences to the left file?"
+    IDS_CONFIRM_ALL_RIGHT   "Are you sure you want to copy all differences to the right file?"
+    IDS_COPY2DIR_FMT        "Copy file to %1"
+    IDS_CONFIRM_COPY2DIR    "Are you sure you want to copy the selected file to %1?"
+    IDS_FONT_CHANGE         "The selected font change will not be applied to any currently visible difference windows."
+    IDS_DIRECTORY_WINDOW_TITLE "Directory Comparison Results"
+    IDS_DIRECTORY_WINDOW_STATUS_FMT "Results for %1 and %2"
+    IDS_FILES_ARE_DIFFERENT "Files are different"
+    IDS_BIN_FILES_DIFF      "Binary files are different"
+    IDS_ONLY_IN_FMT         "Only in %1"
+    IDS_IDENTICAL           "Identical"
+    IDS_CANT_COMPARE_FILES  "Unable to compare files"
+END
+
+STRINGTABLE PRELOAD DISCARDABLE 
+BEGIN
+    AFX_IDS_APP_TITLE       "WinMerge"
+    AFX_IDS_IDLEMESSAGE     "Ready"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_INDICATOR_EXT        "EXT"
+    ID_INDICATOR_CAPS       "CAP"
+    ID_INDICATOR_NUM        "NUM"
+    ID_INDICATOR_SCRL       "SCRL"
+    ID_INDICATOR_OVR        "OVR"
+    ID_INDICATOR_REC        "REC"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_FILE_NEW             "Create a new document\nNew"
+    ID_FILE_OPEN            "Open an existing document\nOpen"
+    ID_FILE_CLOSE           "Close the active document\nClose"
+    ID_FILE_SAVE            "Save the active document\nSave"
+    ID_FILE_SAVE_AS         "Save the active document with a new name\nSave As"
+    ID_FILE_PAGE_SETUP      "Change the printing options\nPage Setup"
+    ID_FILE_PRINT_SETUP     "Change the printer and printing options\nPrint Setup"
+    ID_FILE_PRINT           "Print the active document\nPrint"
+    ID_FILE_PRINT_PREVIEW   "Display full pages\nPrint Preview"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_APP_ABOUT            "Display program information, version number and copyright\nAbout"
+    ID_APP_EXIT             "Quit the application; prompts to save documents\nExit"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_FILE_MRU_FILE1       "Open this document"
+    ID_FILE_MRU_FILE2       "Open this document"
+    ID_FILE_MRU_FILE3       "Open this document"
+    ID_FILE_MRU_FILE4       "Open this document"
+    ID_FILE_MRU_FILE5       "Open this document"
+    ID_FILE_MRU_FILE6       "Open this document"
+    ID_FILE_MRU_FILE7       "Open this document"
+    ID_FILE_MRU_FILE8       "Open this document"
+    ID_FILE_MRU_FILE9       "Open this document"
+    ID_FILE_MRU_FILE10      "Open this document"
+    ID_FILE_MRU_FILE11      "Open this document"
+    ID_FILE_MRU_FILE12      "Open this document"
+    ID_FILE_MRU_FILE13      "Open this document"
+    ID_FILE_MRU_FILE14      "Open this document"
+    ID_FILE_MRU_FILE15      "Open this document"
+    ID_FILE_MRU_FILE16      "Open this document"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_NEXT_PANE            "Switch to the next window pane\nNext Pane"
+    ID_PREV_PANE            "Switch back to the previous window pane\nPrevious Pane"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_WINDOW_NEW           "Open another window for the active document\nNew Window"
+    ID_WINDOW_ARRANGE       "Arrange icons at the bottom of the window\nArrange Icons"
+    ID_WINDOW_CASCADE       "Arrange windows so they overlap\nCascade Windows"
+    ID_WINDOW_TILE_HORZ     "Arrange windows as non-overlapping tiles\nTile Windows"
+    ID_WINDOW_TILE_VERT     "Arrange windows as non-overlapping tiles\nTile Windows"
+    ID_WINDOW_SPLIT         "Split the active window into panes\nSplit"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_EDIT_CLEAR           "Erase the selection\nErase"
+    ID_EDIT_CLEAR_ALL       "Erase everything\nErase All"
+    ID_EDIT_COPY            "Copy the selection and put it on the Clipboard\nCopy"
+    ID_EDIT_CUT             "Cut the selection and put it on the Clipboard\nCut"
+    ID_EDIT_FIND            "Find the specified text\nFind"
+    ID_EDIT_PASTE           "Insert Clipboard contents\nPaste"
+    ID_EDIT_REPEAT          "Repeat the last action\nRepeat"
+    ID_EDIT_REPLACE         "Replace specific text with different text\nReplace"
+    ID_EDIT_SELECT_ALL      "Select the entire document\nSelect All"
+    ID_EDIT_UNDO            "Undo the last action\nUndo"
+    ID_EDIT_REDO            "Redo the previously undone action\nRedo"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_VIEW_TOOLBAR         "Show or hide the toolbar\nToggle ToolBar"
+    ID_VIEW_STATUS_BAR      "Show or hide the status bar\nToggle StatusBar"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    AFX_IDS_SCSIZE          "Change the window size"
+    AFX_IDS_SCMOVE          "Change the window position"
+    AFX_IDS_SCMINIMIZE      "Reduce the window to an icon"
+    AFX_IDS_SCMAXIMIZE      "Enlarge the window to full size"
+    AFX_IDS_SCNEXTWINDOW    "Switch to the next document window"
+    AFX_IDS_SCPREVWINDOW    "Switch to the previous document window"
+    AFX_IDS_SCCLOSE         "Close the active window and prompts to save the documents"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    AFX_IDS_SCRESTORE       "Restore the window to normal size"
+    AFX_IDS_SCTASKLIST      "Activate Task List"
+    AFX_IDS_MDICHILD        "Activate this window"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    AFX_IDS_PREVIEW_CLOSE   "Close print preview mode\nCancel Preview"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_L2R                  "Copy the selected text to the right file\nCopy Right (Right Arrow)"
+    ID_R2L                  "Copy the selected text to the left file\nCopy Left (Left Arrow)"
+    ID_OPTIONS_SHOWIDENTICAL "Displays files that are exactly the same"
+    ID_OPTIONS_SHOWDIFFERENT "Displays files that have differences"
+    ID_OPTIONS_SHOWUNIQUE   "Displays files that exist in only one directory"
+    ID_PREVDIFF             "Scroll to the previous difference\nPrev Diff (Shift+Tab)"
+    ID_NEXTDIFF             "Scroll to the next difference\nNext Diff (Tab)"
+    ID_OPTIONS_BACKUPORIGINALFILE 
+                            "Save a copy of the original as *.bak\nBackup"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_OPTIONS_SCROLLTOFIRSTDIFFERENCE 
+                            "Automatically scrolls to the first difference\nScroll First"
+    ID_OPTIONS_IGNOREWHITESPACE "Disregards whitespace when comparing files"
+    ID_HIDE_BACKUP_FILES    "Hides files with extension BAK"
+    ID_HELP_GNULICENSE      "Display the GNU public license"
+    ID_USEVSS               "Attempts to checkout read-only files from sourcesafe"
+    ID_PROPERTIES           "Set program options\nProperties"
+    ID_ALL_LEFT             "Copy all differences to the left file\nAll Left"
+    ID_ALL_RIGHT            "Copy all differences to the right file\nAll Right"
+    ID_VIEW_SELECTFONT      "Select the font for the diff views\nSelect Diff Font"
+    ID_VIEW_USEDEFAULTFONT  "Revert to using the default system font for diff views\nDefault Font"
+    ID_DIR_COPY_FILE_TO_LEFT "Copy selected file to named directory"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    ID_DIR_COPY_FILE_TO_RIGHT "Copy selected file to named directory"
+    ID_VIEW_LANGUAGE        "Select the current user interfacce language\nLanguage"
+    ID_HELP_CONTENTS        "Displays the WinMerge User's Guide\nHelp"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_SAVE_AS_TITLE       "Save As"
+    IDS_SELECT_FILES_OR_FOLDERS 
+                            "Your selections must be either both files or both folders"
+    IDS_BACKUP_FAILED_PROMPT 
+                            "Unable to backup original file.\nContinue anyway?"
+    IDS_PROPERTIES_TITLE    "Properties"
+    IDS_OPEN_TITLE          "Open"
+    IDS_EDIT_WINDOW_TITLE_FMT "Edit %1"
+    IDS_FILESSAME           "The selected files are identical."
+    IDS_FILEUNIQUE          "The selected file didn't exist in both directories and therefore couldn't be compared."
+    IDS_FILEERROR           "An error occurred while comparing the files."
+    IDS_SAVEREADONLY_FMT    "%1 is marked read-only.  Would you like to save the file under a different name?"
+    IDS_SAVE_FMT            "Save changes to %1?"
+    IDS_FILEBINARY          "Binary files cannot be visually compared."
+    IDS_SAVEVSS_FMT         "%1 is marked read-only.  Would you like to check it out from SourceSafe, or save it under a different name?"
+    IDS_VSSERROR            "SourceSafe returned an error while attempting to check out the file.  Unable to continue..."
+    IDS_NOPROJECT           "You must specify a SourceSafe project path in order to continue (ie:  $/MyProject)"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_AFRIKAANS           "Afrikaans"
+    IDS_ALBANIAN            "Albanian"
+    IDS_ARABIC_SAUDI        "Arabic (Saudi Arabian)"
+    IDS_ARABIC_IRAQ         "Arabic (Iraq)"
+    IDS_ARABIC_EGYPT        "Arabic (Egyptian)"
+    IDS_ARABIC_LIBYA        "Arabic (Libyan)"
+    IDS_ARABIC_ALGERIA      "Arabic (Algerian)"
+    IDS_ARABIC_MOROCCO      "Arabic (Moroccan)"
+    IDS_ARABIC_TUNISIA      "Arabic (Tunisian)"
+    IDS_ARABIC_OMAN         "Arabic (Oman)"
+    IDS_ARABIC_YEMEN        "Arabic (Yemen)"
+    IDS_ARABIC_SYRIA        "Arabic (Syrian)"
+    IDS_ARABIC_JORDAN       "Arabic (Jordan)"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_ARABIC_LEBANON      "Arabic (Lebanon)"
+    IDS_ARABIC_KUWAIT       "Arabic (Kuwait)"
+    IDS_ARABIC_UAE          "Arabic (UAE)"
+    IDS_ARABIC_BAHRAIN      "Arabic (Bahrain)"
+    IDS_ARABIC_QATAR        "Arabic (Qatar)"
+    IDS_BASQUE              "Basque"
+    IDS_BELARUSIAN          "Belarusian"
+    IDS_BULGARIAN           "Bulgarian"
+    IDS_CATALAN             "Catalan"
+    IDS_CHINESE_TRADITIONAL "Chinese (Trad)"
+    IDS_CHINESE_SIMPLIFIED  "Chinese (Simple)"
+    IDS_CHINESE_HONGKONG    "Chinese (Hong Kong)"
+    IDS_CHINESE_SINGAPORE   "Chinese (Singapore)"
+    IDS_CROATIAN            "Croatian"
+    IDS_CZECH               "Czechslovakian"
+    IDS_DANISH              "Danish"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_DUTCH               "Dutch"
+    IDS_ENGLISH_US          "English (US)"
+    IDS_ENGLISH_UK          "English (UK)"
+    IDS_ENGLISH_AUS         "English (Australian)"
+    IDS_ENGLISH_CAN         "English (Canadian)"
+    IDS_ENGLISH_NZ          "English (New Zealand)"
+    IDS_ENGLISH_EIRE        "English (Irish)"
+    IDS_ENGLISH_SOUTH_AFRICA "English (So. African)"
+    IDS_ENGLISH_JAMAICA     "English (Jamaican)"
+    IDS_ENGLISH_CARIBBEAN   "English (Caribbean)"
+    IDS_ENGLISH_BELIZE      "English (Belize)"
+    IDS_ENGLISH_TRINIDAD    "English (Trinidad)"
+    IDS_ESTONIAN            "Estonian"
+    IDS_FAEROESE            "Faeroese"
+    IDS_FARSI               "Farsi"
+    IDS_FINNISH             "Finnish"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_FRENCH              "French"
+    IDS_FRENCH_BELGIAN      "French (Belgian)"
+    IDS_FRENCH_CANADIAN     "French (Canadian)"
+    IDS_FRENCH_SWISS        "French (Swiss)"
+    IDS_FRENCH_LUXEMBOURG   "French (Lux)"
+    IDS_GERMAN              "German"
+    IDS_GERMAN_SWISS        "German (Swiss)"
+    IDS_GERMAN_AUSTRIAN     "German (Austrian)"
+    IDS_GERMAN_LUXEMBOURG   "German (Lux)"
+    IDS_GERMAN_LIECHTENSTEIN "German (Liecht)"
+    IDS_GREEK               "Greek"
+    IDS_HEBREW              "Hebrew"
+    IDS_HUNGARIAN           "Hungarian"
+    IDS_ICELANDIC           "Icelandic"
+    IDS_INDONESIAN          "Indonesian"
+    IDS_ITALIAN             "Italian"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_ITALIAN_SWISS       "Italian (Swiss)"
+    IDS_JAPANESE            "Japanese"
+    IDS_KOREAN              "Korean"
+    IDS_KOREAN_JOHAB        "Korean (Johab)"
+    IDS_LATVIAN             "Latvian"
+    IDS_LITHUANIAN          "Lithuanian"
+    IDS_NORWEGIAN_BOKMAL    "Norwegian (Bokmal)"
+    IDS_NORWEGIAN_NYNORSK   "Norwegian (Nynorsk)"
+    IDS_POLISH              "Polish"
+    IDS_PORTUGUESE          "Portugese"
+    IDS_PORTUGUESE_BRAZILIAN "Portugese (Brazil)"
+    IDS_ROMANIAN            "Romanian"
+    IDS_RUSSIAN             "Russian"
+    IDS_SERBIAN_LATIN       "Serbian (Latin)"
+    IDS_SERBIAN_CYRILLIC    "Serbian (Cyrillic)"
+    IDS_SLOVAK              "Slovak"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_SLOVENIAN           "Slovenian"
+    IDS_SPANISH             "Spanish"
+    IDS_SPANISH_MEXICAN     "Spanish (Mexican)"
+    IDS_SPANISH_MODERN      "Spanish (Modern)"
+    IDS_SPANISH_GUATEMALA   "Spanish (Guatemala)"
+    IDS_SPANISH_COSTA_RICA  "Spanish (Costa Rica)"
+    IDS_SPANISH_PANAMA      "Spanish (Panama)"
+    IDS_SPANISH_DOMINICAN   "Spanish (Dominican)"
+    IDS_SPANISH_VENEZUELA   "Spanish (Venezuela)"
+    IDS_SPANISH_COLOMBIA    "Spanish (Colombia)"
+    IDS_SPANISH_PERU        "Spanish (Peru)"
+    IDS_SPANISH_ARGENTINA   "Spanish (Argentina)"
+    IDS_SPANISH_ECUADOR     "Spanish (Ecuador)"
+    IDS_SPANISH_CHILE       "Spanish (Chile)"
+    IDS_SPANISH_URUGUAY     "Spanish (Uruguay)"
+    IDS_SPANISH_PARAGUAY    "Spanish (Paraguay)"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_SPANISH_BOLIVIA     "Spanish (Bolivia)"
+    IDS_SPANISH_EL_SALVADOR "Spanish (El Salvador)"
+    IDS_SPANISH_HONDURAS    "Spanish (Honduras)"
+    IDS_SPANISH_NICARAGUA   "Spanish (Nicaragua)"
+    IDS_SPANISH_PUERTO_RICO "Spanish (Puerto Rico)"
+    IDS_SWEDISH             "Swedish"
+    IDS_SWEDISH_FINLAND     "Swedish (Finland)"
+    IDS_THAI                "Thai"
+    IDS_TURKISH             "Turkish"
+    IDS_UKRANIAN            "Ukranian"
+    IDS_VIETNAMESE          "Vietnamese"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_FILENAME_HEADER     "Filename"
+    IDS_DIR_HEADER          "Directory"
+    IDS_RESULT_HEADER       "Comparison result"
+    IDS_FILE_COMPARISON_TITLE "File Comparison"
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif
+#include "res\Merge.rc2"  // non-Microsoft Visual C++ edited resources
+#include "afxres.rc"         // Standard components
+#include "afxprint.rc"       // printing/print preview resources
+#endif
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/Src/MergeDoc.cpp b/Src/MergeDoc.cpp
new file mode 100644 (file)
index 0000000..1e150b5
--- /dev/null
@@ -0,0 +1,591 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// MergeDoc.cpp : implementation of the CMergeDoc class
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+#include "direct.h"
+
+#include "MainFrm.h"
+#include "DiffView.h"
+
+#include "diff.h"
+#include "getopt.h"
+#include "fnmatch.h"
+#include "coretools.h"
+#include "VssPrompt.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeDoc
+
+IMPLEMENT_DYNCREATE(CMergeDoc, CDocument)
+
+BEGIN_MESSAGE_MAP(CMergeDoc, CDocument)
+       //{{AFX_MSG_MAP(CMergeDoc)
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeDoc construction/destruction
+
+CMergeDoc::CMergeDoc()
+{
+       m_diffs.SetSize(64);
+       m_nDiffs=0;
+       m_pView=NULL;
+}
+
+CMergeDoc::~CMergeDoc()
+{      
+       CUndoItem *pitem;
+       while (!m_undoList.IsEmpty())
+       {
+               pitem = (CUndoItem*)m_undoList.RemoveHead();
+               delete pitem;
+       }
+       mf->m_pMergeDoc = NULL;
+}
+
+BOOL CMergeDoc::OnNewDocument()
+{
+       if (!CDocument::OnNewDocument())
+               return FALSE;
+
+       CString s;
+       VERIFY(s.LoadString(IDS_FILE_COMPARISON_TITLE));
+       SetTitle(s);
+
+       return TRUE;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeDoc serialization
+
+void CMergeDoc::Serialize(CArchive& ar)
+{
+       if (ar.IsStoring())
+       {
+               // TODO: add storing code here
+       }
+       else
+       {
+               // TODO: add loading code here
+       }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeDoc diagnostics
+
+#ifdef _DEBUG
+void CMergeDoc::AssertValid() const
+{
+       CDocument::AssertValid();
+}
+
+void CMergeDoc::Dump(CDumpContext& dc) const
+{
+       CDocument::Dump(dc);
+}
+#endif //_DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+// CMergeDoc commands
+
+BOOL CMergeDoc::Rescan()
+{
+       struct file_data inf[2];
+       char *free0=NULL,*free1=NULL;
+       char dir0[MAX_PATH],dir1[MAX_PATH], name0[MAX_PATH], name1[MAX_PATH];
+       int val,failed=0, depth=0;
+       bool same_files=FALSE;
+       struct change *e, *p;
+       struct change *script=NULL;
+       BOOL bResult=FALSE;
+
+       BeginWaitCursor();
+
+       m_diffs.RemoveAll();
+       m_nDiffs=0;
+       
+       split_filename(m_strLeftFile, dir0, name0, NULL);
+       split_filename(m_strRightFile, dir1, name1, NULL); 
+       memset(&inf[0], 0,sizeof(inf[0]));
+       memset(&inf[1], 0,sizeof(inf[1]));
+       
+       /* Both exist and neither is a directory.  */
+       int o_binary = always_text_flag ? O_BINARY : 0;
+       
+       /* Open the files and record their descriptors.  */
+       inf[0].name = *dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0));
+       inf[0].desc = -2;
+       inf[1].name = *dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1));
+       inf[1].desc = -2;
+       if (inf[0].desc == -2)
+       {
+               if ((inf[0].desc = open (inf[0].name, O_RDONLY|o_binary, 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|o_binary, 0)) < 0)
+                       {
+                               perror_with_name (inf[1].name);
+                               failed = 1;
+                       }
+                       
+                       /* Compare the files, if no error was found.  */
+                       
+                       script = diff_2_files (inf, depth);
+                       
+                       struct change *next = script;
+                       int trans_a0, trans_b0, trans_a1, trans_b1;
+                       int first0, last0, first1, last1, deletes, inserts, op;
+                       struct change *thisob, *end;
+                       
+                       while (next)
+                       {
+                               /* Find a set of changes that belong together.  */
+                               thisob = 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;
+#ifdef DEBUG
+                               debug_script (thisob);
+#endif
+                               
+                               /* Print thisob hunk.  */
+                               //(*printfun) (thisob);
+                               {
+                                       
+                                       /* Determine range of line numbers involved in each file.  */
+                                       analyze_hunk (thisob, &first0, &last0, &first1, &last1, &deletes, &inserts);
+                                       if (!(!deletes && !inserts))
+                                       {
+                                               if (deletes && inserts)
+                                                       op = OP_DIFF;
+                                               else if (deletes)
+                                                       op = OP_LEFTONLY;
+                                               else
+                                                       op = OP_RIGHTONLY;
+                                               
+                                               /* Print the lines that the first file has.  */
+                                               translate_range (&inf[0], first0, last0, &trans_a0, &trans_b0);
+                                               translate_range (&inf[1], first1, last1, &trans_a1, &trans_b1);
+                                               AddDiffRange(trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, (BYTE)op);
+                                               TRACE("left=%d,%d   right=%d,%d   op=%d\n",trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, op);
+                                       }
+                               }
+                               
+                               
+                               /* Reconnect the script so it will all be freed properly.  */
+                               end->link = next;
+                       }
+                       
+                       // cleanup the script
+                       for (e = script; e; e = p)
+                       {
+                               p = e->link;
+                               free (e);
+                       }
+
+                       // test to see if we have a binary & text file
+                       BOOL b1 = FALSE;
+                       BOOL b2 = FALSE;
+                       if (m_nDiffs==0)
+                       {
+                               b1 = FileIsBinary(inf[0].desc);
+                               b2 = FileIsBinary(inf[1].desc);
+                       }                       
+                       cleanup_file_buffers(inf);
+                       
+                       /* 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;
+                       }
+                       
+                       // display the files
+                       if (m_nDiffs>0)
+                       {
+                               mf->m_pLeft->PrimeListWithFile(m_strLeftFile);
+                               mf->m_pRight->PrimeListWithFile(m_strRightFile);
+
+                               int lcnt = mf->m_pLeft->m_pList->GetItemCount();
+                               int rcnt = mf->m_pRight->m_pList->GetItemCount();
+                               if (lcnt < rcnt)
+                               {
+                                       m_diffs[m_nDiffs-1].dbegin0 = lcnt;
+                                       m_diffs[m_nDiffs-1].dend0 = rcnt;
+                                       m_diffs[m_nDiffs-1].blank0 = lcnt;
+                               }
+                               else if (rcnt < lcnt)
+                               {
+                                       m_diffs[m_nDiffs-1].dbegin1 = rcnt;
+                                       m_diffs[m_nDiffs-1].dend1 = lcnt;
+                                       m_diffs[m_nDiffs-1].blank1 = rcnt;
+                               }
+
+                               while (lcnt < rcnt)
+                               {
+                                       mf->m_pLeft->AddItem(lcnt, 0, "");
+                                       mf->m_pLeft->m_pList->SetItemData(lcnt, 1);
+                                       lcnt++;
+                               }
+                               while (rcnt < lcnt)
+                               {
+                                       mf->m_pRight->AddItem(rcnt, 0, "");
+                                       mf->m_pRight->m_pList->SetItemData(rcnt, 1);
+                                       rcnt++;
+                               }
+                               bResult=TRUE;
+                       }
+                       else if ((b1 && !b2)
+                               || (!b1 && b2))
+                       {
+                               CString s;
+                               VERIFY(s.LoadString(IDS_BIN_FILES_DIFF));
+                               AfxMessageBox(s, MB_ICONINFORMATION);
+                       }
+                       else
+                       {
+                               CString s;
+                               VERIFY(s.LoadString(IDS_FILESSAME));
+                               AfxMessageBox(s, MB_ICONINFORMATION);
+                       }
+               }
+       }
+       
+       if (free0)
+               free (free0);
+       if (free1)
+               free (free1);
+
+       EndWaitCursor();
+       return bResult;
+}
+
+
+void CMergeDoc::AddDiffRange(UINT begin0, UINT end0, UINT begin1, UINT end1, BYTE op)
+{
+       TRY {
+               DIFFRANGE dr;
+               memset(&dr, 0, sizeof(dr));
+               dr.begin0 = begin0;
+               dr.end0 = end0;
+               dr.begin1 = begin1;
+               dr.end1 = end1;
+               dr.op = op;
+               dr.blank0 = dr.blank1 = -1;
+               m_diffs.SetAtGrow(m_nDiffs, dr);
+               m_nDiffs++;
+       }
+       CATCH_ALL(e)
+       {
+               TCHAR msg[1024];
+               e->GetErrorMessage(msg, 1024);
+               AfxMessageBox(msg, MB_ICONSTOP);
+       }
+       END_CATCH_ALL;
+}
+
+
+void CMergeDoc::AddUndoAction(UINT nBegin, UINT nEnd, UINT nDiff, int nBlanks, BOOL bInsert, CDiffView *pList)
+{
+       CUndoItem *pitem = new CUndoItem;
+       if (pitem != NULL)
+       {
+               pitem->begin = nBegin;
+               pitem->end = nEnd;
+               pitem->diffidx = nDiff;
+               pitem->blank = nBlanks;
+               pitem->bInsert = bInsert;
+               pitem->m_pList = pList;
+               if (bInsert)
+                       for (UINT i=nBegin; i <= nEnd; i++)
+                       {
+                               CString s = pitem->m_pList->m_pList->GetItemText(i, 0);
+                               pitem->list.AddTail(s);
+                       }
+
+               m_undoList.AddHead(pitem);
+       }
+}
+
+BOOL CMergeDoc::Undo()
+{
+       if (!m_undoList.IsEmpty())
+       {
+               CUndoItem *pitem = (CUndoItem *)m_undoList.RemoveHead();
+               if (pitem != NULL)
+               {
+                       if (pitem->bInsert)
+                       {
+                               CString s;
+                               POSITION pos = pitem->list.GetHeadPosition();
+                               UINT i = pitem->begin;
+                               while (pos != NULL)
+                               {
+                                       s = pitem->list.GetNext(pos);
+                                       pitem->m_pList->m_pList->SetItemText(i,0,s);
+                                       i++;
+                               }
+                               if (pitem->m_pList == mf->m_pLeft)
+                                       m_diffs[pitem->diffidx].blank0 = pitem->blank;
+                               else
+                                       m_diffs[pitem->diffidx].blank1 = pitem->blank;
+                       }
+                       else
+                       {
+                               ASSERT(0);
+                       }
+
+                       pitem->m_pList->SubMod();
+                       delete pitem;
+
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+
+void CMergeDoc::ListCopy(CDiffView * pSrcList, CDiffView * pDestList)
+{
+       CString s;
+       int sel;
+       int begin,end=-1;
+       begin = sel = pSrcList->m_pList->GetNextItem(-1,LVNI_SELECTED);
+       if (sel==-1)
+               return;
+       while ((sel=pSrcList->m_pList->GetNextItem(sel,LVNI_SELECTED)) != -1)
+       {
+               end=sel;
+       }
+       if (end==-1)
+               end=begin;
+               
+       int diff = LineToDiff(begin);
+       if (pDestList->m_bIsLeft)
+       {
+               AddUndoAction(begin, end, diff, m_diffs[diff].blank0, TRUE, pDestList);
+               m_diffs[diff].blank0 = m_diffs[diff].blank1;
+       }
+       else 
+       {
+               AddUndoAction(begin, end, diff, m_diffs[diff].blank1, TRUE, pDestList);
+               m_diffs[diff].blank1 = m_diffs[diff].blank0;
+       }
+       sel=-1;
+       while ((sel=pSrcList->m_pList->GetNextItem(sel,LVNI_SELECTED)) != -1)
+       {
+               s = pSrcList->m_pList->GetItemText(sel,0);
+               pDestList->m_pList->SetItemText(sel,0,s);
+       }
+       pDestList->AddMod();
+       pDestList->UpdateWindow();
+}
+
+
+
+BOOL CMergeDoc::DoSave(LPCTSTR szPath, CListCtrl * pList, BOOL bLeft)
+{
+       HANDLE hf;
+       CString strSavePath(szPath);
+
+       if (!mf->m_strSaveAsPath.IsEmpty())
+       {
+               CFileStatus status;
+               if (CFile::GetStatus(mf->m_strSaveAsPath, status)
+                       && (status.m_attribute & CFile::Attribute::directory))
+               {
+                       // third arg was a directory, so get append the filename
+                       TCHAR name[MAX_PATH];
+                       split_filename(szPath, NULL, name, NULL);
+                       strSavePath = mf->m_strSaveAsPath;
+                       if (mf->m_strSaveAsPath.Right(1) != _T('\\'))
+                               strSavePath += _T('\\');
+                       strSavePath += name;
+               }
+               else
+                       strSavePath = mf->m_strSaveAsPath;      
+       }
+
+       if (!mf->CheckSavePath(strSavePath))
+               return FALSE;
+
+       if (!mf->CreateBackup(strSavePath))
+               return FALSE;
+
+       if ((hf=FOPEN(strSavePath, GENERIC_WRITE, CREATE_ALWAYS)) != INVALID_HANDLE_VALUE)
+       {
+               CString s;
+               int idx,cnt = pList->GetItemCount();
+               int blank0,blank1;
+               for (int i=0; i < cnt; i++)
+               {
+                       idx=LineToDiff(i);
+                       s = pList->GetItemText(i,0);
+                       if (idx!=-1)
+                       {
+                               blank0 = m_diffs[idx].blank0;
+                               blank1 = m_diffs[idx].blank1;
+                               if ((bLeft && (blank0 < 0 || i < blank0))
+                               || (!bLeft && (blank1 < 0 || i < blank1)))
+                               {
+                                       FPRINTF(hf, "%s\r\n", s);
+                                       //FPRINTF(hf, "%s\r\n", Tabify(s));
+                               }
+                       }
+                       else
+                               FPRINTF(hf, "%s\r\n", s);
+                       //FPRINTF(hf, "%s\r\n", Tabify(s));
+               }
+               CloseHandle(hf);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+
+/*CString CMergeDoc::ExpandTabs(LPCTSTR szText)
+{
+       LPCTSTR p;
+       CString strResult(_T(""));
+       CString spaces(_T(' '), mf->m_nTabSize);
+       if (szText != NULL && *szText != _T('\0'))
+       {
+               for (p=szText; *p != _T('\0'); p=_tcsinc(p))
+               {
+                       if (*p == _T('\t'))
+                               strResult += spaces;
+                       else
+                               strResult += *p;
+               }
+       }
+       return strResult;
+}
+
+CString CMergeDoc::Tabify(LPCTSTR szText)
+{
+       LPCSTR p=szText,p2;
+       TCHAR temp[1024];
+       CString strResult("");
+       CString spaces(' ',mf->m_nTabSize);
+       while (1)
+       {
+               if ((p2=strstr(p, spaces)) != NULL)
+               {
+                       strncpy(temp,p,p2-p);
+                       temp[p2-p]=NULL;
+                       strResult += temp;
+                       strResult += "\t";
+                       p = p2+mf->m_nTabSize;
+               }
+               else
+               {
+                       strResult += p;
+                       break;
+               }
+       }
+       return strResult;
+}*/
+
+int CMergeDoc::LineToDiff(UINT nLine)
+{
+       for (UINT i=0; i < m_nDiffs; i++)
+       {
+               if (nLine >= m_diffs[i].dbegin0
+                       && nLine <= m_diffs[i].dend0)
+               {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+void CMergeDoc::SetDiffViewMode(BOOL bEnable)
+{
+       if (bEnable)
+       {
+       }
+       else
+       {
+       }
+}
+
+
+UINT CMergeDoc::CountPrevBlanks(UINT nCurLine, BOOL bLeft)
+{
+       UINT nBlanks=0;
+       int blk,end;
+       for (int i=0; i < (int)m_nDiffs; i++)
+       {
+               if (bLeft)
+               {
+                       blk = m_diffs[i].blank0;
+                       end = m_diffs[i].dend0;
+               }
+               else
+               {
+                       blk = m_diffs[i].blank1;
+                       end = m_diffs[i].dend0;
+               }
+               if (blk >= (int)nCurLine)
+               {
+                       break;
+               }
+               else if (blk >= 0)
+                       nBlanks += end - blk + 1;
+       }
+       return nBlanks;
+}
+
+BOOL CMergeDoc::CanCloseFrame(CFrameWnd* pFrame) 
+{
+       if (mf->m_pLeft)
+               if (!mf->m_pLeft->SaveHelper())
+                       return FALSE;
+       
+       return CDocument::CanCloseFrame(pFrame);
+}
diff --git a/Src/MergeDoc.h b/Src/MergeDoc.h
new file mode 100644 (file)
index 0000000..02b8657
--- /dev/null
@@ -0,0 +1,118 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// MergeDoc.h : interface of the CMergeDoc class
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_MERGEDOC_H__BBCD4F90_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
+#define AFX_MERGEDOC_H__BBCD4F90_34E4_11D1_BAA6_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#include "afxtempl.h"
+
+#define OP_NONE                        0
+#define OP_LEFTONLY            1
+#define OP_DIFF                        2
+#define OP_RIGHTONLY   3
+
+typedef struct tagDIFFRANGE {
+       UINT begin0,end0,begin1,end1;
+       UINT dbegin0,dend0,dbegin1,dend1;
+       int blank0,blank1;
+       BYTE op;
+}DIFFRANGE;
+
+class CDiffView;
+
+class CUndoItem
+{
+public:
+       UINT begin,end,diffidx;
+       int blank;
+       BOOL bInsert;
+       CDiffView *m_pList;
+       CStringList list;
+};
+
+
+class CMergeDoc : public CDocument
+{
+protected: // create from serialization only
+       CMergeDoc();
+       DECLARE_DYNCREATE(CMergeDoc)
+
+// Attributes
+public:
+       
+       // Operations
+public:        
+       CDiffView * m_pView;
+       CPtrList m_undoList;
+       CArray<DIFFRANGE,DIFFRANGE> m_diffs;
+       UINT m_nDiffs;
+       CString m_strLeftFile, m_strRightFile;
+
+       BOOL Rescan();
+       void AddDiffRange(UINT begin0, UINT end0, UINT begin1, UINT end1, BYTE op);
+       void AddUndoAction(UINT nBegin, UINT nEnd, UINT nDiff, int nBlanks, BOOL bInsert, CDiffView *pList);
+       BOOL Undo();
+       void ListCopy(CDiffView * pSrcList, CDiffView * pDestList);
+       BOOL DoSave(LPCTSTR szPath, CListCtrl * pList, BOOL bLeft);
+       //CString ExpandTabs(LPCTSTR szText);
+       //CString Tabify(LPCTSTR szText);
+       int LineToDiff(UINT nLine);
+       void SetDiffViewMode(BOOL bEnable);
+       
+       // Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CMergeDoc)
+       public:
+       virtual BOOL OnNewDocument();
+       virtual void Serialize(CArchive& ar);
+       virtual BOOL CanCloseFrame(CFrameWnd* pFrame);
+       //}}AFX_VIRTUAL
+
+// Implementation
+public:
+       UINT CountPrevBlanks(UINT nCurLine, BOOL bLeft);
+       virtual ~CMergeDoc();
+#ifdef _DEBUG
+       virtual void AssertValid() const;
+       virtual void Dump(CDumpContext& dc) const;
+#endif
+
+protected:
+
+// Generated message map functions
+protected:
+       //{{AFX_MSG(CMergeDoc)
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_MERGEDOC_H__BBCD4F90_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
diff --git a/Src/OpenDlg.cpp b/Src/OpenDlg.cpp
new file mode 100644 (file)
index 0000000..197c9e6
--- /dev/null
@@ -0,0 +1,298 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// OpenDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Merge.h"
+#include "OpenDlg.h"
+#include "coretools.h"
+#include "StringEx.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+#define DIRSEL_TAG   _T("Directory Selection")
+
+
+/////////////////////////////////////////////////////////////////////////////
+// COpenDlg dialog
+
+
+COpenDlg::COpenDlg(CWnd* pParent /*=NULL*/)
+       : CDialog(COpenDlg::IDD, pParent)
+{
+       //{{AFX_DATA_INIT(COpenDlg)
+       m_strLeft = _T("");
+       m_strRight = _T("");
+       m_bRecurse = FALSE;
+       m_strExt = _T("*.*");
+       //}}AFX_DATA_INIT
+       
+       m_strParsedExt = _T(".*");
+}
+
+
+void COpenDlg::DoDataExchange(CDataExchange* pDX)
+{
+       CDialog::DoDataExchange(pDX);
+       //{{AFX_DATA_MAP(COpenDlg)
+       DDX_Control(pDX, IDC_EXT_COMBO, m_ctlExt);
+       DDX_Control(pDX, IDOK, m_ctlOk);
+       DDX_Control(pDX, IDC_RECURS_CHECK, m_ctlRecurse);
+       DDX_Control(pDX, IDC_RIGHT_COMBO, m_ctlRight);
+       DDX_Control(pDX, IDC_LEFT_COMBO, m_ctlLeft);
+       DDX_CBString(pDX, IDC_LEFT_COMBO, m_strLeft);
+       DDX_CBString(pDX, IDC_RIGHT_COMBO, m_strRight);
+       DDX_Check(pDX, IDC_RECURS_CHECK, m_bRecurse);
+       DDX_CBString(pDX, IDC_EXT_COMBO, m_strExt);
+       //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(COpenDlg, CDialog)
+       //{{AFX_MSG_MAP(COpenDlg)
+       ON_BN_CLICKED(IDC_LEFT_BUTTON, OnLeftButton)
+       ON_BN_CLICKED(IDC_RIGHT_BUTTON, OnRightButton)
+       ON_CBN_SELCHANGE(IDC_LEFT_COMBO, OnSelchangeLeftCombo)
+       ON_CBN_SELCHANGE(IDC_RIGHT_COMBO, OnSelchangeRightCombo)
+       ON_CBN_EDITCHANGE(IDC_LEFT_COMBO, UpdateButtonStates)
+       ON_CBN_SELENDCANCEL(IDC_LEFT_COMBO, UpdateButtonStates)
+       ON_CBN_EDITCHANGE(IDC_RIGHT_COMBO, UpdateButtonStates)
+       ON_CBN_SELENDCANCEL(IDC_RIGHT_COMBO, UpdateButtonStates)
+       ON_CBN_KILLFOCUS(IDC_LEFT_COMBO, OnKillfocusLeftCombo)
+       ON_CBN_KILLFOCUS(IDC_RIGHT_COMBO, OnKillfocusRightCombo)
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// COpenDlg message handlers
+
+void COpenDlg::OnLeftButton() 
+{
+       CString s;
+       TCHAR folder[MAX_PATH]=_T(""), name[MAX_PATH];
+       CFileStatus status;
+       UpdateData(TRUE); 
+
+       if (CFile::GetStatus(m_strLeft, status)
+               && (status.m_attribute & CFile::Attribute::directory))
+                       _tcscpy(folder, m_strLeft);
+       else
+               split_filename(m_strLeft, folder, NULL, NULL);
+       if (SelectFile(s, folder))
+       {
+               split_filename(s, folder, name, NULL);
+               if (!_tcscmp(name, DIRSEL_TAG))
+               {
+                       m_strLeft = folder;
+               }
+               else
+                       m_strLeft = s;
+               UpdateData(FALSE);
+               UpdateButtonStates();
+       }       
+}
+
+void COpenDlg::OnRightButton() 
+{
+       CString s;
+       TCHAR folder[MAX_PATH]=_T(""), name[MAX_PATH];
+       CFileStatus status;
+       UpdateData(TRUE);
+
+       if (CFile::GetStatus(m_strRight, status)
+               && (status.m_attribute & CFile::Attribute::directory))
+                       _tcscpy(folder, m_strRight);
+       else 
+               split_filename(m_strRight, folder, NULL, NULL);
+       if (SelectFile(s, folder))
+       {
+               split_filename(s, folder, name, NULL);
+               if (!_tcscmp(name, DIRSEL_TAG))
+                       m_strRight = folder;
+               else
+                       m_strRight = s;
+               UpdateData(FALSE);
+               UpdateButtonStates();
+       }       
+}
+
+void COpenDlg::OnOK() 
+{
+       UpdateData(TRUE);
+
+       m_ctlLeft.SaveState(_T("Files\\Left"));
+       m_ctlRight.SaveState(_T("Files\\Right"));
+       m_ctlExt.SaveState(_T("Files\\Ext"));
+
+       // parse the extensions
+       // replace all *. with .*\\.
+       int idx=0;
+       LPCTSTR pszSeps = _T("; |*%^&.,\\/<>:\"'`?\t\r\n");
+       CStringEx strExt(m_strExt);
+       strExt.TrimLeft();
+       strExt.TrimRight();
+
+       TCHAR ext[2048];
+       _tcscpy(ext, strExt);
+       LPTSTR p;
+       CString strPattern(_T("^.*\\.("));
+
+       p = _tcstok(ext, pszSeps);
+       if (p == NULL)
+               m_strParsedExt = _T(".*");
+       else
+       {
+               while (p != NULL)
+               {
+                       strPattern += p;                
+                       p = _tcstok(NULL, pszSeps);
+                       if (p != NULL)
+                               strPattern += _T('|');
+               }
+               strPattern += _T(")$");
+               m_strParsedExt = strPattern;
+       }
+
+       CDialog::OnOK();
+}
+
+BOOL COpenDlg::OnInitDialog() 
+{
+       CDialog::OnInitDialog();
+       
+       
+       m_ctlLeft.LoadState(_T("Files\\Left"));
+       m_ctlRight.LoadState(_T("Files\\Right"));
+       m_ctlExt.LoadState(_T("Files\\Ext"));
+       UpdateData(m_strLeft.IsEmpty() && m_strRight.IsEmpty());
+       UpdateButtonStates();
+
+       return TRUE;  
+}
+
+void COpenDlg::UpdateButtonStates()
+{
+       BOOL bLIsDir, bRIsDir;
+       UpdateData(TRUE);
+
+       // only enable OK button if both file exist
+       BOOL bEnableOK = (IsFileOk(m_strLeft, &bLIsDir) && IsFileOk(m_strRight, &bRIsDir));
+       m_ctlOk.EnableWindow(bEnableOK);
+       m_ctlRecurse.EnableWindow(bEnableOK && bLIsDir && bRIsDir);
+}
+
+BOOL COpenDlg::SelectFile(CString& path, LPCTSTR pszFolder) 
+{
+       CString s;           
+                   
+       s.LoadString(IDS_ALLFILES); 
+       CFileDialog *pdlg = new CFileDialog(TRUE, NULL, NULL, 
+                                   OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, s);
+       if (NULL != pdlg)
+       {
+               TCHAR buf[MAX_PATH] = DIRSEL_TAG;
+               CString title;
+               VERIFY(title.LoadString(IDS_OPEN_TITLE));
+               pdlg->m_ofn.lpstrTitle = (LPCTSTR)title;
+               pdlg->m_ofn.lpstrInitialDir = (LPSTR)pszFolder;
+               pdlg->m_ofn.nMaxFile = MAX_PATH;
+               pdlg->m_ofn.lpstrFile = buf;
+
+               if (pdlg->DoModal()==IDOK)
+               {
+                       path = pdlg->GetPathName(); 
+                       delete pdlg;
+                       return TRUE;
+               }
+               path.Empty();
+               delete pdlg;
+       }
+       return FALSE;      
+}
+
+void COpenDlg::OnSelchangeLeftCombo() 
+{
+       int sel = m_ctlLeft.GetCurSel();
+       if (sel != CB_ERR)
+       {
+               m_ctlLeft.GetLBText(sel, m_strLeft);
+               m_ctlLeft.SetWindowText(m_strLeft);
+               UpdateData(TRUE);
+       }
+       UpdateButtonStates();
+       
+}
+
+void COpenDlg::OnSelchangeRightCombo() 
+{
+       int sel = m_ctlRight.GetCurSel();
+       if (sel != CB_ERR)
+       {
+               m_ctlRight.GetLBText(sel, m_strRight);
+               m_ctlRight.SetWindowText(m_strRight);
+               UpdateData(TRUE);
+       }
+       UpdateButtonStates();
+}
+
+void COpenDlg::OnKillfocusLeftCombo() 
+{
+       UpdateData(TRUE);
+       // remove trailing slashes
+       RemoveTrailingSlash(m_strLeft);
+       m_ctlLeft.SetWindowText(m_strLeft);
+       UpdateButtonStates();
+}
+
+void COpenDlg::OnKillfocusRightCombo() 
+{
+       UpdateData(TRUE);
+       RemoveTrailingSlash(m_strRight);
+       m_ctlRight.SetWindowText(m_strRight);
+       UpdateButtonStates();
+}
+
+BOOL COpenDlg::IsFileOk(CString & strFile, BOOL *pbDir /*= NULL*/)
+{
+       CFileStatus status;
+       CString s(strFile);
+
+       while (s.Right(1) == _T('\\') || s.Right(1) == _T('/'))
+               s = s.Left(s.GetLength()-1);
+       
+       BOOL bResult = CFile::GetStatus(strFile, status);
+       if (pbDir != NULL)
+               *pbDir = (status.m_attribute & CFile::Attribute::directory);
+
+       return bResult;
+}
+
+void COpenDlg::RemoveTrailingSlash(CString & s)
+{
+       while (s.Right(1) == _T('\\') || s.Right(1) == _T('/'))
+               s = s.Left(s.GetLength()-1);
+}
diff --git a/Src/OpenDlg.h b/Src/OpenDlg.h
new file mode 100644 (file)
index 0000000..a9de43f
--- /dev/null
@@ -0,0 +1,86 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+#if !defined(AFX_OPENDLG_H__69FB0D77_2A05_11D1_BA92_00A024706EDC__INCLUDED_)
+#define AFX_OPENDLG_H__69FB0D77_2A05_11D1_BA92_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// OpenDlg.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// COpenDlg dialog
+#include "SuperComboBox.h"
+
+class COpenDlg : public CDialog
+{
+// Construction
+public:
+       CString m_strParsedExt;
+       void UpdateButtonStates();
+       COpenDlg(CWnd* pParent = NULL);   // standard constructor
+       BOOL SelectFile(CString& path, LPCTSTR pszFolder);
+
+// Dialog Data
+       //{{AFX_DATA(COpenDlg)
+       enum { IDD = IDD_OPEN };
+       CSuperComboBox  m_ctlExt;
+       CButton m_ctlOk;
+       CButton m_ctlRecurse;
+       CSuperComboBox  m_ctlRight;
+       CSuperComboBox  m_ctlLeft;
+       CString m_strLeft;
+       CString m_strRight;
+       BOOL    m_bRecurse;
+       CString m_strExt;
+       //}}AFX_DATA
+
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(COpenDlg)
+       protected:
+       virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+       void RemoveTrailingSlash(CString& s);
+       BOOL IsFileOk(CString& strFile, BOOL *pbDir = NULL);
+
+       // Generated message map functions
+       //{{AFX_MSG(COpenDlg)
+       afx_msg void OnLeftButton();
+       afx_msg void OnRightButton();
+       virtual void OnOK();
+       virtual BOOL OnInitDialog();
+       afx_msg void OnSelchangeLeftCombo();
+       afx_msg void OnSelchangeRightCombo();
+       afx_msg void OnKillfocusLeftCombo();
+       afx_msg void OnKillfocusRightCombo();
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_OPENDLG_H__69FB0D77_2A05_11D1_BA92_00A024706EDC__INCLUDED_)
diff --git a/Src/PropGeneral.cpp b/Src/PropGeneral.cpp
new file mode 100644 (file)
index 0000000..0c68078
--- /dev/null
@@ -0,0 +1,76 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// PropGeneral.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "merge.h"
+#include "PropGeneral.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropGeneral property page
+
+IMPLEMENT_DYNCREATE(CPropGeneral, CPropertyPage)
+
+CPropGeneral::CPropGeneral() : CPropertyPage(CPropGeneral::IDD)
+{
+       //{{AFX_DATA_INIT(CPropGeneral)
+       m_bBackup = FALSE;
+       m_bScroll = FALSE;
+       m_bIgnoreWhite = FALSE;
+       m_nTabSize = 0;
+       m_bIgnoreCase = FALSE;
+       m_bIgnoreBlankLines = FALSE;
+       //}}AFX_DATA_INIT
+}
+
+CPropGeneral::~CPropGeneral()
+{
+}
+
+void CPropGeneral::DoDataExchange(CDataExchange* pDX)
+{
+       CPropertyPage::DoDataExchange(pDX);
+       //{{AFX_DATA_MAP(CPropGeneral)
+       DDX_Check(pDX, IDC_BACKUP_CHECK, m_bBackup);
+       DDX_Check(pDX, IDC_SCROLL_CHECK, m_bScroll);
+       DDX_Check(pDX, IDC_WHITESPACE_CHECK, m_bIgnoreWhite);
+       DDX_Text(pDX, IDC_TAB_EDIT, m_nTabSize);
+       DDX_Check(pDX, IDC_IGNCASE_CHECK, m_bIgnoreCase);
+       DDX_Check(pDX, IDC_IGNBLANKS_CHECK, m_bIgnoreBlankLines);
+       //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CPropGeneral, CPropertyPage)
+       //{{AFX_MSG_MAP(CPropGeneral)
+               // NOTE: the ClassWizard will add message map macros here
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropGeneral message handlers
diff --git a/Src/PropGeneral.h b/Src/PropGeneral.h
new file mode 100644 (file)
index 0000000..ebcb195
--- /dev/null
@@ -0,0 +1,54 @@
+#if !defined(AFX_PROPGENERAL_H__30AD07B0_E420_11D1_BBC5_00A024706EDC__INCLUDED_)
+#define AFX_PROPGENERAL_H__30AD07B0_E420_11D1_BBC5_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// PropGeneral.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropGeneral dialog
+
+class CPropGeneral : public CPropertyPage
+{
+       DECLARE_DYNCREATE(CPropGeneral)
+
+// Construction
+public:
+       CPropGeneral();
+       ~CPropGeneral();
+
+// Dialog Data
+       //{{AFX_DATA(CPropGeneral)
+       enum { IDD = IDD_PROPPAGE_LARGE };
+       BOOL    m_bBackup;
+       BOOL    m_bScroll;
+       BOOL    m_bIgnoreWhite;
+       UINT    m_nTabSize;
+       BOOL    m_bIgnoreCase;
+       BOOL    m_bIgnoreBlankLines;
+       //}}AFX_DATA
+
+
+// Overrides
+       // ClassWizard generate virtual function overrides
+       //{{AFX_VIRTUAL(CPropGeneral)
+       protected:
+       virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+       // Generated message map functions
+       //{{AFX_MSG(CPropGeneral)
+               // NOTE: the ClassWizard will add member functions here
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_PROPGENERAL_H__30AD07B0_E420_11D1_BBC5_00A024706EDC__INCLUDED_)
diff --git a/Src/PropVss.cpp b/Src/PropVss.cpp
new file mode 100644 (file)
index 0000000..1b8e7e6
--- /dev/null
@@ -0,0 +1,125 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// PropVss.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "merge.h"
+#include "PropVss.h"
+#include "dirtools.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropVss property page
+
+IMPLEMENT_DYNCREATE(CPropVss, CPropertyPage)
+
+CPropVss::CPropVss() : CPropertyPage(CPropVss::IDD)
+{
+       //{{AFX_DATA_INIT(CPropVss)
+       m_strPath = _T("");
+       m_bDoVss = FALSE;
+       //}}AFX_DATA_INIT
+}
+
+CPropVss::~CPropVss()
+{
+}
+
+void CPropVss::DoDataExchange(CDataExchange* pDX)
+{
+       CPropertyPage::DoDataExchange(pDX);
+       //{{AFX_DATA_MAP(CPropVss)
+       DDX_Control(pDX, IDC_VSS_L1, m_ctlVssL1);
+       DDX_Control(pDX, IDC_PATH_EDIT, m_ctlPath);
+       DDX_Control(pDX, IDC_BROWSE_BUTTON, m_ctlBrowse);
+       DDX_Text(pDX, IDC_PATH_EDIT, m_strPath);
+       DDX_Check(pDX, IDC_DOVSS_CHECK, m_bDoVss);
+       //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CPropVss, CPropertyPage)
+       //{{AFX_MSG_MAP(CPropVss)
+       ON_BN_CLICKED(IDC_DOVSS_CHECK, OnDovssCheck)
+       ON_BN_CLICKED(IDC_BROWSE_BUTTON, OnBrowseButton)
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropVss message handlers
+
+BOOL ChooseFile( CString& strResult, 
+                                LPCTSTR szStartPath /* = NULL */, 
+                                LPCTSTR szCaption /* = "Open" */, 
+                                LPCTSTR szFilter /* = "All Files (*.*)|*.*||" */, 
+                                BOOL bOpenDlg /* = TRUE */) 
+// displays a shell file selector
+{
+       CFileDialog dlg(bOpenDlg, NULL, NULL, 
+                                   OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | (bOpenDlg? OFN_FILEMUSTEXIST:0) , szFilter);
+       dlg.m_ofn.lpstrTitle = (LPTSTR)szCaption;
+       dlg.m_ofn.lpstrInitialDir = (LPTSTR)szStartPath;
+       if (dlg.DoModal()==IDOK)
+       {
+               strResult = dlg.GetPathName(); 
+               return TRUE;
+       }
+       strResult = _T("");
+       return FALSE;      
+}
+
+
+
+void CPropVss::OnDovssCheck() 
+{
+       UpdateData(TRUE);
+       m_ctlPath.EnableWindow(m_bDoVss);
+       m_ctlVssL1.EnableWindow(m_bDoVss);
+       m_ctlBrowse.EnableWindow(m_bDoVss);
+}
+
+void CPropVss::OnBrowseButton() 
+{
+       CString s;
+       if (ChooseFile(s))
+       {
+               UpdateData(TRUE);
+               m_strPath = s;
+               UpdateData(FALSE);
+       }
+       
+}
+
+BOOL CPropVss::OnInitDialog() 
+{
+       CPropertyPage::OnInitDialog();
+       
+       UpdateData(FALSE);
+       OnDovssCheck();
+       
+       return TRUE;  
+}
diff --git a/Src/PropVss.h b/Src/PropVss.h
new file mode 100644 (file)
index 0000000..e120fa2
--- /dev/null
@@ -0,0 +1,55 @@
+#if !defined(AFX_PROPVSS_H__30AD07AF_E420_11D1_BBC5_00A024706EDC__INCLUDED_)
+#define AFX_PROPVSS_H__30AD07AF_E420_11D1_BBC5_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// PropVss.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropVss dialog
+
+class CPropVss : public CPropertyPage
+{
+       DECLARE_DYNCREATE(CPropVss)
+
+// Construction
+public:
+       CPropVss();
+       ~CPropVss();
+
+// Dialog Data
+       //{{AFX_DATA(CPropVss)
+       enum { IDD = IDD_PROP_VSS };
+       CStatic m_ctlVssL1;
+       CEdit   m_ctlPath;
+       CButton m_ctlBrowse;
+       CString m_strPath;
+       BOOL    m_bDoVss;
+       //}}AFX_DATA
+
+
+// Overrides
+       // ClassWizard generate virtual function overrides
+       //{{AFX_VIRTUAL(CPropVss)
+       protected:
+       virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+       // Generated message map functions
+       //{{AFX_MSG(CPropVss)
+       afx_msg void OnDovssCheck();
+       afx_msg void OnBrowseButton();
+       virtual BOOL OnInitDialog();
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_PROPVSS_H__30AD07AF_E420_11D1_BBC5_00A024706EDC__INCLUDED_)
diff --git a/Src/STACK.C b/Src/STACK.C
new file mode 100644 (file)
index 0000000..5dda1ca
--- /dev/null
@@ -0,0 +1 @@
+extern unsigned _stklen = 24*1024;
diff --git a/Src/Splash.cpp b/Src/Splash.cpp
new file mode 100644 (file)
index 0000000..10ee78e
--- /dev/null
@@ -0,0 +1,175 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// Splash.cpp : implementation file
+//
+
+#include "stdafx.h"  // e. g. stdafx.h
+#include "resource.h"  // e.g. resource.h
+
+#include "Splash.h"  // e.g. splash.h
+#include "version.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//   Splash Screen class
+
+BOOL CSplashWnd::c_bShowSplashWnd;
+CSplashWnd* CSplashWnd::c_pSplashWnd;
+CSplashWnd::CSplashWnd()
+{
+}
+
+CSplashWnd::~CSplashWnd()
+{
+       // Clear the static window pointer.
+       ASSERT(c_pSplashWnd == this);
+       c_pSplashWnd = NULL;
+}
+
+BEGIN_MESSAGE_MAP(CSplashWnd, CWnd)
+       //{{AFX_MSG_MAP(CSplashWnd)
+       ON_WM_CREATE()
+       ON_WM_PAINT()
+       ON_WM_TIMER()
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+void CSplashWnd::EnableSplashScreen(BOOL bEnable /*= TRUE*/)
+{
+       c_bShowSplashWnd = bEnable;
+}
+
+void CSplashWnd::ShowSplashScreen(CWnd* pParentWnd /*= NULL*/)
+{
+       if (!c_bShowSplashWnd || c_pSplashWnd != NULL)
+               return;
+
+       // Allocate a new splash screen, and create the window.
+       c_pSplashWnd = new CSplashWnd;
+       if (!c_pSplashWnd->Create(pParentWnd))
+               delete c_pSplashWnd;
+       else
+               c_pSplashWnd->UpdateWindow();
+}
+
+BOOL CSplashWnd::PreTranslateAppMessage(MSG* pMsg)
+{
+       if (c_pSplashWnd == NULL)
+               return FALSE;
+
+       // If we get a keyboard or mouse message, hide the splash screen.
+       if (pMsg->message == WM_KEYDOWN ||
+           pMsg->message == WM_SYSKEYDOWN ||
+           pMsg->message == WM_LBUTTONDOWN ||
+           pMsg->message == WM_RBUTTONDOWN ||
+           pMsg->message == WM_MBUTTONDOWN ||
+           pMsg->message == WM_NCLBUTTONDOWN ||
+           pMsg->message == WM_NCRBUTTONDOWN ||
+           pMsg->message == WM_NCMBUTTONDOWN)
+       {
+               c_pSplashWnd->HideSplashScreen();
+               return TRUE;    // message handled here
+       }
+
+       return FALSE;   // message not handled
+}
+
+BOOL CSplashWnd::Create(CWnd* pParentWnd /*= NULL*/)
+{
+       if (!m_bitmap.LoadBitmap(IDB_SPLASH))
+               return FALSE;
+
+       BITMAP bm;
+       m_bitmap.GetBitmap(&bm);
+
+       return CreateEx(0,
+               AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
+               NULL, WS_POPUP | WS_VISIBLE, 0, 0, bm.bmWidth, bm.bmHeight, pParentWnd->GetSafeHwnd(), NULL);
+}
+
+void CSplashWnd::HideSplashScreen()
+{
+       // Destroy the window, and update the mainframe.
+       DestroyWindow();
+       AfxGetMainWnd()->UpdateWindow();
+}
+
+void CSplashWnd::PostNcDestroy()
+{
+       // Free the C++ class.
+       delete this;
+}
+
+int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+       if (CWnd::OnCreate(lpCreateStruct) == -1)
+               return -1;
+
+       // Center the window.
+       CenterWindow();
+
+       // Set a timer to destroy the splash screen.
+       SetTimer(1, 5000, NULL);
+
+       return 0;
+}
+
+void CSplashWnd::OnPaint()
+{
+       CPaintDC dc(this);
+
+       CDC dcImage;
+       if (!dcImage.CreateCompatibleDC(&dc))
+               return;
+
+       BITMAP bm;
+       m_bitmap.GetBitmap(&bm);
+
+       // Paint the image.
+       CBitmap* pOldBitmap = dcImage.SelectObject(&m_bitmap);
+       dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcImage, 0, 0, SRCCOPY);
+       dcImage.SelectObject(pOldBitmap);
+
+       CVersionInfo version;
+       CString s;
+       CFont ft,*oldfont=NULL;
+
+       if (ft.CreatePointFont( 100, _T("Arial"), &dc ))
+               oldfont = dc.SelectObject(&ft);
+
+       AfxFormatString1(s, IDS_VERSION_FMT, version.GetProductVersion());
+       dc.SetBkMode(TRANSPARENT);
+       dc.TextOut(90, 110, s);
+       if (oldfont != NULL)
+               dc.SelectObject(oldfont);
+
+}
+
+void CSplashWnd::OnTimer(UINT nIDEvent)
+{
+       // Destroy the splash screen window.
+       HideSplashScreen();
+}
diff --git a/Src/Splash.h b/Src/Splash.h
new file mode 100644 (file)
index 0000000..4025956
--- /dev/null
@@ -0,0 +1,72 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997  Dean P. Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+#ifndef _SPLASH_SCRN_
+#define _SPLASH_SCRN_
+
+// Splash.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+//   Splash Screen class
+
+class CSplashWnd : public CWnd
+{
+// Construction
+protected:
+       CSplashWnd();
+
+// Attributes:
+public:
+       CBitmap m_bitmap;
+
+// Operations
+public:
+       static void EnableSplashScreen(BOOL bEnable = TRUE);
+       static void ShowSplashScreen(CWnd* pParentWnd = NULL);
+       static BOOL PreTranslateAppMessage(MSG* pMsg);
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CSplashWnd)
+       //}}AFX_VIRTUAL
+
+// Implementation
+public:
+       ~CSplashWnd();
+       virtual void PostNcDestroy();
+
+protected:
+       BOOL Create(CWnd* pParentWnd = NULL);
+       void HideSplashScreen();
+       static BOOL c_bShowSplashWnd;
+       static CSplashWnd* c_pSplashWnd;
+
+// Generated message map functions
+protected:
+       //{{AFX_MSG(CSplashWnd)
+       afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+       afx_msg void OnPaint();
+       afx_msg void OnTimer(UINT nIDEvent);
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+
+#endif
diff --git a/Src/StdAfx.cpp b/Src/StdAfx.cpp
new file mode 100644 (file)
index 0000000..d3c989c
--- /dev/null
@@ -0,0 +1,26 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// stdafx.cpp : source file that includes just the standard includes
+//     Merge.pch will be the pre-compiled header
+//     stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
diff --git a/Src/StdAfx.h b/Src/StdAfx.h
new file mode 100644 (file)
index 0000000..991f314
--- /dev/null
@@ -0,0 +1,28 @@
+// stdafx.h : include file for standard system include files,
+//  or project specific include files that are used frequently, but
+//      are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__BBCD4F8A_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
+#define AFX_STDAFX_H__BBCD4F8A_34E4_11D1_BAA6_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#define VC_EXTRALEAN           // Exclude rarely-used stuff from Windows headers
+
+#include <afxwin.h>         // MFC core and standard components
+#include <afxext.h>         // MFC extensions
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>                    // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <afxtempl.h>
+#include <afxsock.h>
+#include <afxole.h>
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__BBCD4F8A_34E4_11D1_BAA6_00A024706EDC__INCLUDED_)
diff --git a/Src/VssPrompt.cpp b/Src/VssPrompt.cpp
new file mode 100644 (file)
index 0000000..536b8ed
--- /dev/null
@@ -0,0 +1,89 @@
+/////////////////////////////////////////////////////////////////////////////
+//    WinMerge:  an interactive diff/merge utility
+//    Copyright (C) 1997-2000  Thingamahoochie Software
+//    Author: Dean Grimm
+//
+//    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.
+//
+/////////////////////////////////////////////////////////////////////////////
+// VssPrompt.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "merge.h"
+#include "VssPrompt.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CVssPrompt dialog
+
+
+CVssPrompt::CVssPrompt(CWnd* pParent /*=NULL*/)
+       : CDialog(CVssPrompt::IDD, pParent)
+{
+       //{{AFX_DATA_INIT(CVssPrompt)
+       m_strProject = _T("");
+       m_strMessage = _T("");
+       //}}AFX_DATA_INIT
+}
+
+
+void CVssPrompt::DoDataExchange(CDataExchange* pDX)
+{
+       CDialog::DoDataExchange(pDX);
+       //{{AFX_DATA_MAP(CVssPrompt)
+       DDX_Control(pDX, IDC_PROJECT_COMBO, m_ctlProject);
+       DDX_CBString(pDX, IDC_PROJECT_COMBO, m_strProject);
+       DDX_Text(pDX, IDC_MESSAGE, m_strMessage);
+       //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CVssPrompt, CDialog)
+       //{{AFX_MSG_MAP(CVssPrompt)
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CVssPrompt message handlers
+
+BOOL CVssPrompt::OnInitDialog() 
+{
+       CDialog::OnInitDialog();
+       
+       m_ctlProject.LoadState(_T("Vss"));
+       
+       return TRUE;  
+}
+
+void CVssPrompt::OnOK() 
+{
+       UpdateData(TRUE);
+       if (m_strProject.IsEmpty())
+       {
+               AfxMessageBox(IDS_NOPROJECT,MB_ICONSTOP);
+               m_ctlProject.SetFocus();
+               return;
+       }
+
+       m_ctlProject.SaveState(_T("Vss"));
+       
+       CDialog::OnOK();
+}
diff --git a/Src/VssPrompt.h b/Src/VssPrompt.h
new file mode 100644 (file)
index 0000000..1833fab
--- /dev/null
@@ -0,0 +1,50 @@
+#if !defined(AFX_VSSPROMPT_H__F767E53B_90F4_11D1_BB11_00A024706EDC__INCLUDED_)
+#define AFX_VSSPROMPT_H__F767E53B_90F4_11D1_BB11_00A024706EDC__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+// VssPrompt.h : header file
+//
+#include "SuperComboBox.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CVssPrompt dialog
+
+class CVssPrompt : public CDialog
+{
+// Construction
+public:
+       CVssPrompt(CWnd* pParent = NULL);   // standard constructor
+
+// Dialog Data
+       //{{AFX_DATA(CVssPrompt)
+       enum { IDD = IDD_VSS };
+       CSuperComboBox  m_ctlProject;
+       CString m_strProject;
+       CString m_strMessage;
+       //}}AFX_DATA
+
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CVssPrompt)
+       protected:
+       virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+       // Generated message map functions
+       //{{AFX_MSG(CVssPrompt)
+       virtual BOOL OnInitDialog();
+       virtual void OnOK();
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_VSSPROMPT_H__F767E53B_90F4_11D1_BB11_00A024706EDC__INCLUDED_)
diff --git a/Src/diffmain.c b/Src/diffmain.c
new file mode 100644 (file)
index 0000000..5a6ede6
--- /dev/null
@@ -0,0 +1,983 @@
+/* GNU DIFF main routine.
+   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.  */
+
+/* GNU DIFF was written by Mike Haertel, David Hayes,
+   Richard Stallman, Len Tower, and Paul Eggert.  */
+
+#define GDIFF_MAIN
+#include "diff.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 *));
+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 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;
+
+/* 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;
+}
+\f
+/* 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;
+}
+\f
+/* 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 = (char *)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);
+}
+\f
+/* 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'},
+  {"binary", 0, 0, 'q'},       /* An alias, no longer recommended */
+  {"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},
+  {0, 0, 0, 0}
+};
+
+
+int
+main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  int val;
+  int c;
+  int prev = -1;
+  int width = DEFAULT_WIDTH;
+
+  // Do our initializations.  
+  program = argv[0];
+  output_style = OUTPUT_NORMAL;
+  context = -1;
+  line_end_char = '\n';
+
+  // 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;
+         setmode(0, O_BINARY);
+         setmode(1, O_BINARY);
+
+         break;
+
+       case 'b':
+         // Ignore changes in amount of white space.  
+         ignore_space_change_flag = 1;
+         length_varies = 1;
+         ignore_some_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);
+         {
+           
+         }
+         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;
+         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;
+         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.  
+         specify_style (OUTPUT_CONTEXT);
+         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':
+         fprintf (stderr, "GNU diff version %s\n", version_string);
+         break;
+
+       case 'w':
+         // Ignore horizontal white space when comparing lines.  
+         ignore_all_space_flag = 1;
+         ignore_some_changes = 1;
+         length_varies = 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;
+
+       default:
+         usage ();
+       }
+      prev = c;
+    }
+
+  if (optind != argc - 2)
+    usage ();
+
+
+  {
+    //
+    //*        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 (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)
+    {
+      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 ();
+
+  if (ferror (stdout) || fclose (stdout) != 0)
+    fatal ("write error");
+  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
+usage ()
+{
+  fprintf (stderr, "Usage: %s [options] from-file to-file\n", program);
+  fprintf (stderr, "Options:\n\
+       [-abBcdefhHilnNpPqrstTuvwy] [-C lines] [-D name] [-F regexp]\n\
+       [-I regexp] [-L from-label [-L to-label]] [-S starting-file] [-U lines]\n\
+       [-W columns] [-x pattern] [-X pattern-file]\n");
+  fprintf (stderr, "\
+       [--brief] [--changed-group-format=format] [--context[=lines]] [--ed]\n\
+       [--exclude=pattern] [--exclude-from=pattern-file] [--expand-tabs]\n\
+       [--forward-ed] [--horizon-lines=lines] [--ifdef=name]\n\
+       [--ignore-all-space] [--ignore-blank-lines] [--ignore-case]\n");
+  fprintf (stderr, "\
+       [--ignore-matching-lines=regexp] [--ignore-space-change]\n\
+       [--initial-tab] [--label=from-label [--label=to-label]]\n\
+       [--left-column] [--minimal] [--new-file] [--new-group-format=format]\n\
+       [--new-line-format=format] [--old-group-format=format]\n");
+  fprintf (stderr, "\
+       [--old-line-format=format] [--paginate] [--rcs] [--recursive]\n\
+       [--report-identical-files] [--sdiff-merge-assist] [--show-c-function]\n\
+       [--show-function-line=regexp] [--side-by-side] [--speed-large-files]\n\
+       [--starting-file=starting-file] [--suppress-common-lines] [--text]\n");
+  fprintf (stderr, "\
+       [--unchanged-group-format=format] [--unchanged-line-format=format]\n\
+       [--unidirectional-new-file] [--unified[=lines]] [--version]\n\
+       [--width=columns]\n");
+  exit (2);
+}
+
+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;
+}
+\f
+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 popular file types */
+  /* S_ISLNK is impossible with `stat'.  */
+#ifdef S_ISSOCK
+  if (S_ISSOCK (st->st_mode)) return "socket";
+#endif
+
+  return "weird file";
+}
+\f
+/* 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.  */
+
+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;
+    }
+
+  /* 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++)
+    {
+      bzero (&inf[i].stat, sizeof (struct stat));
+      inf[i].dir_p = 0;
+
+      if (inf[i].desc != -1)
+       {
+         int stat_result;
+
+         if (i && strcmp (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 = strrchr (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;
+
+    }
+#if defined(__MSDOS__) || defined(__NT__) || defined(WIN32)
+  else if (same_files = 0) /* yes, only ONE equal sign intended! hmo11apr93 */
+    ;
+#else
+  else if ((same_files =    inf[0].stat.st_ino == inf[1].stat.st_ino
+                        && inf[0].stat.st_dev == inf[1].stat.st_dev
+                        && inf[0].stat.st_size == inf[1].stat.st_size
+                        && inf[0].desc != -1
+                        && inf[1].desc != -1)
+          && 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;
+    }
+#endif /*__MSDOS__||__NT__*/
+  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.  */
+      int o_binary = always_text_flag ? O_BINARY : 0;
+      /* Open the files and record their descriptors.  */
+
+      if (inf[0].desc == -2)
+       if ((inf[0].desc = open (inf[0].name, O_RDONLY|o_binary, 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|o_binary, 0)) < 0)
+         {
+           perror_with_name (inf[1].name);
+           failed = 1;
+         }
+
+      /* 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/Src/diffutils/CONFIG.H b/Src/diffutils/CONFIG.H
new file mode 100644 (file)
index 0000000..f15459d
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ *     config.h - pre-built version for MSDOS and NT.
+ */
+#define DIRENT 1
+#define HAVE_VPRINTF 1
+#define RETSIGTYPE void
+#define STDC_HEADERS 1
+#define HAVE_DUP2 1
+#define HAVE_MEMCHR 1
+#define HAVE_SIGACTION 1
+#define HAVE_STRERROR 1
+#define HAVE_WAITPID 1
+#define HAVE_FCNTL_H 1
+#define HAVE_LIMITS_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_TIME_H 1
diff --git a/Src/diffutils/GnuVersion.c b/Src/diffutils/GnuVersion.c
new file mode 100644 (file)
index 0000000..2aeae68
--- /dev/null
@@ -0,0 +1,5 @@
+/* Version number of GNU diff.  */
+
+#include "config.h"
+
+char const version_string[] = "2.5";
diff --git a/Src/diffutils/lib/ALLOCA.C b/Src/diffutils/lib/ALLOCA.C
new file mode 100644 (file)
index 0000000..bd4932a
--- /dev/null
@@ -0,0 +1,484 @@
+/* alloca.c -- allocate automatically reclaimed memory
+   (Mostly) portable public-domain implementation -- D A Gwyn
+
+   This implementation of the PWB library alloca function,
+   which is used to allocate space off the run-time stack so
+   that it is automatically reclaimed upon procedure exit,
+   was inspired by discussions with J. Q. Johnson of Cornell.
+   J.Otto Tennant <jot@cray.com> contributed the Cray support.
+
+   There are some preprocessor constants that can
+   be defined when compiling for your specific system, for
+   improved efficiency; however, the defaults should be okay.
+
+   The general concept of this implementation is to keep
+   track of all alloca-allocated blocks, and reclaim any
+   that are found to be deeper in the stack than the current
+   invocation.  This heuristic does not reclaim storage as
+   soon as it becomes invalid, but it will do so eventually.
+
+   As a special case, alloca(0) reclaims storage without
+   allocating any.  It is a good idea to use alloca(0) in
+   your main control loop, etc. to force garbage collection.  */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+/* If compiling with GCC 2, this file's not needed.  */
+#if !defined (__GNUC__) || __GNUC__ < 2
+
+/* If someone has defined alloca as a macro,
+   there must be some other way alloca is supposed to work.  */
+#ifndef alloca
+
+#ifdef emacs
+#ifdef static
+/* actually, only want this if static is defined as ""
+   -- this is for usg, in which emacs must undefine static
+   in order to make unexec workable
+   */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+/* If your stack is a linked list of frames, you have to
+   provide an "address metric" ADDRESS_FUNCTION macro.  */
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+long i00afunc ();
+#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
+#else
+#define ADDRESS_FUNCTION(arg) &(arg)
+#endif
+
+#if __STDC__
+typedef void *pointer;
+#else
+typedef char *pointer;
+#endif
+
+#define        NULL    0
+
+/* Different portions of Emacs need to call different versions of
+   malloc.  The Emacs executable needs alloca to call xmalloc, because
+   ordinary malloc isn't protected from input signals.  On the other
+   hand, the utilities in lib-src need alloca to call malloc; some of
+   them are very simple, and don't have an xmalloc routine.
+
+   Non-Emacs programs expect this to call use xmalloc.
+
+   Callers below should use malloc.  */
+
+#ifndef emacs
+#define malloc xmalloc
+#endif
+extern pointer malloc ();
+
+/* Define STACK_DIRECTION 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  */
+
+#ifndef STACK_DIRECTION
+#define        STACK_DIRECTION 0       /* Direction unknown.  */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define        STACK_DIR       STACK_DIRECTION /* Known at compile-time.  */
+
+#else /* STACK_DIRECTION == 0; need run-time code.  */
+
+static int stack_dir;          /* 1 or -1 once known.  */
+#define        STACK_DIR       stack_dir
+
+static void
+find_stack_direction ()
+{
+  static char *addr = NULL;    /* Address of first `dummy', once known.  */
+  auto char dummy;             /* To get stack address.  */
+
+  if (addr == NULL)
+    {                          /* Initial entry.  */
+      addr = ADDRESS_FUNCTION (dummy);
+
+      find_stack_direction (); /* Recurse once.  */
+    }
+  else
+    {
+      /* Second entry.  */
+      if (ADDRESS_FUNCTION (dummy) > addr)
+       stack_dir = 1;          /* Stack grew upward.  */
+      else
+       stack_dir = -1;         /* Stack grew downward.  */
+    }
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/* An "alloca header" is used to:
+   (a) chain together all alloca'ed blocks;
+   (b) keep track of stack depth.
+
+   It is very important that sizeof(header) agree with malloc
+   alignment chunk size.  The following default should work okay.  */
+
+#ifndef        ALIGN_SIZE
+#define        ALIGN_SIZE      sizeof(double)
+#endif
+
+typedef union hdr
+{
+  char align[ALIGN_SIZE];      /* To force sizeof(header).  */
+  struct
+    {
+      union hdr *next;         /* For chaining headers.  */
+      char *deep;              /* For stack depth measure.  */
+    } h;
+} header;
+
+static header *last_alloca_header = NULL;      /* -> last alloca header.  */
+
+/* Return a pointer to at least SIZE bytes of storage,
+   which will be automatically reclaimed upon exit from
+   the procedure that called alloca.  Originally, this space
+   was supposed to be taken from the current stack frame of the
+   caller, but that method cannot be made to work for some
+   implementations of C, for example under Gould's UTX/32.  */
+
+pointer
+alloca (size)
+     unsigned size;
+{
+  auto char probe;             /* Probes stack depth: */
+  register char *depth = ADDRESS_FUNCTION (probe);
+
+#if STACK_DIRECTION == 0
+  if (STACK_DIR == 0)          /* Unknown growth direction.  */
+    find_stack_direction ();
+#endif
+
+  /* Reclaim garbage, defined as all alloca'd storage that
+     was allocated from deeper in the stack than currently. */
+
+  {
+    register header *hp;       /* Traverses linked list.  */
+
+    for (hp = last_alloca_header; hp != NULL;)
+      if ((STACK_DIR > 0 && hp->h.deep > depth)
+         || (STACK_DIR < 0 && hp->h.deep < depth))
+       {
+         register header *np = hp->h.next;
+
+         free ((pointer) hp);  /* Collect garbage.  */
+
+         hp = np;              /* -> next header.  */
+       }
+      else
+       break;                  /* Rest are not deeper.  */
+
+    last_alloca_header = hp;   /* -> last valid storage.  */
+  }
+
+  if (size == 0)
+    return NULL;               /* No allocation required.  */
+
+  /* Allocate combined header + user data storage.  */
+
+  {
+    register pointer new = malloc (sizeof (header) + size);
+    /* Address of header.  */
+
+    ((header *) new)->h.next = last_alloca_header;
+    ((header *) new)->h.deep = depth;
+
+    last_alloca_header = (header *) new;
+
+    /* User storage begins just after header.  */
+
+    return (pointer) ((char *) new + sizeof (header));
+  }
+}
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+
+#ifdef DEBUG_I00AFUNC
+#include <stdio.h>
+#endif
+
+#ifndef CRAY_STACK
+#define CRAY_STACK
+#ifndef CRAY2
+/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
+struct stack_control_header
+  {
+    long shgrow:32;            /* Number of times stack has grown.  */
+    long shaseg:32;            /* Size of increments to stack.  */
+    long shhwm:32;             /* High water mark of stack.  */
+    long shsize:32;            /* Current size of stack (all segments).  */
+  };
+
+/* The stack segment linkage control information occurs at
+   the high-address end of a stack segment.  (The stack
+   grows from low addresses to high addresses.)  The initial
+   part of the stack segment linkage control information is
+   0200 (octal) words.  This provides for register storage
+   for the routine which overflows the stack.  */
+
+struct stack_segment_linkage
+  {
+    long ss[0200];             /* 0200 overflow words.  */
+    long sssize:32;            /* Number of words in this segment.  */
+    long ssbase:32;            /* Offset to stack base.  */
+    long:32;
+    long sspseg:32;            /* Offset to linkage control of previous
+                                  segment of stack.  */
+    long:32;
+    long sstcpt:32;            /* Pointer to task common address block.  */
+    long sscsnm;               /* Private control structure number for
+                                  microtasking.  */
+    long ssusr1;               /* Reserved for user.  */
+    long ssusr2;               /* Reserved for user.  */
+    long sstpid;               /* Process ID for pid based multi-tasking.  */
+    long ssgvup;               /* Pointer to multitasking thread giveup.  */
+    long sscray[7];            /* Reserved for Cray Research.  */
+    long ssa0;
+    long ssa1;
+    long ssa2;
+    long ssa3;
+    long ssa4;
+    long ssa5;
+    long ssa6;
+    long ssa7;
+    long sss0;
+    long sss1;
+    long sss2;
+    long sss3;
+    long sss4;
+    long sss5;
+    long sss6;
+    long sss7;
+  };
+
+#else /* CRAY2 */
+/* The following structure defines the vector of words
+   returned by the STKSTAT library routine.  */
+struct stk_stat
+  {
+    long now;                  /* Current total stack size.  */
+    long maxc;                 /* Amount of contiguous space which would
+                                  be required to satisfy the maximum
+                                  stack demand to date.  */
+    long high_water;           /* Stack high-water mark.  */
+    long overflows;            /* Number of stack overflow ($STKOFEN) calls.  */
+    long hits;                 /* Number of internal buffer hits.  */
+    long extends;              /* Number of block extensions.  */
+    long stko_mallocs;         /* Block allocations by $STKOFEN.  */
+    long underflows;           /* Number of stack underflow calls ($STKRETN).  */
+    long stko_free;            /* Number of deallocations by $STKRETN.  */
+    long stkm_free;            /* Number of deallocations by $STKMRET.  */
+    long segments;             /* Current number of stack segments.  */
+    long maxs;                 /* Maximum number of stack segments so far.  */
+    long pad_size;             /* Stack pad size.  */
+    long current_address;      /* Current stack segment address.  */
+    long current_size;         /* Current stack segment size.  This
+                                  number is actually corrupted by STKSTAT to
+                                  include the fifteen word trailer area.  */
+    long initial_address;      /* Address of initial segment.  */
+    long initial_size;         /* Size of initial segment.  */
+  };
+
+/* The following structure describes the data structure which trails
+   any stack segment.  I think that the description in 'asdef' is
+   out of date.  I only describe the parts that I am sure about.  */
+
+struct stk_trailer
+  {
+    long this_address;         /* Address of this block.  */
+    long this_size;            /* Size of this block (does not include
+                                  this trailer).  */
+    long unknown2;
+    long unknown3;
+    long link;                 /* Address of trailer block of previous
+                                  segment.  */
+    long unknown5;
+    long unknown6;
+    long unknown7;
+    long unknown8;
+    long unknown9;
+    long unknown10;
+    long unknown11;
+    long unknown12;
+    long unknown13;
+    long unknown14;
+  };
+
+#endif /* CRAY2 */
+#endif /* not CRAY_STACK */
+
+#ifdef CRAY2
+/* Determine a "stack measure" for an arbitrary ADDRESS.
+   I doubt that "lint" will like this much. */
+
+static long
+i00afunc (long *address)
+{
+  struct stk_stat status;
+  struct stk_trailer *trailer;
+  long *block, size;
+  long result = 0;
+
+  /* We want to iterate through all of the segments.  The first
+     step is to get the stack status structure.  We could do this
+     more quickly and more directly, perhaps, by referencing the
+     $LM00 common block, but I know that this works.  */
+
+  STKSTAT (&status);
+
+  /* Set up the iteration.  */
+
+  trailer = (struct stk_trailer *) (status.current_address
+                                   + status.current_size
+                                   - 15);
+
+  /* There must be at least one stack segment.  Therefore it is
+     a fatal error if "trailer" is null.  */
+
+  if (trailer == 0)
+    abort ();
+
+  /* Discard segments that do not contain our argument address.  */
+
+  while (trailer != 0)
+    {
+      block = (long *) trailer->this_address;
+      size = trailer->this_size;
+      if (block == 0 || size == 0)
+       abort ();
+      trailer = (struct stk_trailer *) trailer->link;
+      if ((block <= address) && (address < (block + size)))
+       break;
+    }
+
+  /* Set the result to the offset in this segment and add the sizes
+     of all predecessor segments.  */
+
+  result = address - block;
+
+  if (trailer == 0)
+    {
+      return result;
+    }
+
+  do
+    {
+      if (trailer->this_size <= 0)
+       abort ();
+      result += trailer->this_size;
+      trailer = (struct stk_trailer *) trailer->link;
+    }
+  while (trailer != 0);
+
+  /* We are done.  Note that if you present a bogus address (one
+     not in any segment), you will get a different number back, formed
+     from subtracting the address of the first block.  This is probably
+     not what you want.  */
+
+  return (result);
+}
+
+#else /* not CRAY2 */
+/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
+   Determine the number of the cell within the stack,
+   given the address of the cell.  The purpose of this
+   routine is to linearize, in some sense, stack addresses
+   for alloca.  */
+
+static long
+i00afunc (long address)
+{
+  long stkl = 0;
+
+  long size, pseg, this_segment, stack;
+  long result = 0;
+
+  struct stack_segment_linkage *ssptr;
+
+  /* Register B67 contains the address of the end of the
+     current stack segment.  If you (as a subprogram) store
+     your registers on the stack and find that you are past
+     the contents of B67, you have overflowed the segment.
+
+     B67 also points to the stack segment linkage control
+     area, which is what we are really interested in.  */
+
+  stkl = CRAY_STACKSEG_END ();
+  ssptr = (struct stack_segment_linkage *) stkl;
+
+  /* If one subtracts 'size' from the end of the segment,
+     one has the address of the first word of the segment.
+
+     If this is not the first segment, 'pseg' will be
+     nonzero.  */
+
+  pseg = ssptr->sspseg;
+  size = ssptr->sssize;
+
+  this_segment = stkl - size;
+
+  /* It is possible that calling this routine itself caused
+     a stack overflow.  Discard stack segments which do not
+     contain the target address.  */
+
+  while (!(this_segment <= address && address <= stkl))
+    {
+#ifdef DEBUG_I00AFUNC
+      fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
+#endif
+      if (pseg == 0)
+       break;
+      stkl = stkl - pseg;
+      ssptr = (struct stack_segment_linkage *) stkl;
+      size = ssptr->sssize;
+      pseg = ssptr->sspseg;
+      this_segment = stkl - size;
+    }
+
+  result = address - this_segment;
+
+  /* If you subtract pseg from the current end of the stack,
+     you get the address of the previous stack segment's end.
+     This seems a little convoluted to me, but I'll bet you save
+     a cycle somewhere.  */
+
+  while (pseg != 0)
+    {
+#ifdef DEBUG_I00AFUNC
+      fprintf (stderr, "%011o %011o\n", pseg, size);
+#endif
+      stkl = stkl - pseg;
+      ssptr = (struct stack_segment_linkage *) stkl;
+      size = ssptr->sssize;
+      pseg = ssptr->sspseg;
+      result += size;
+    }
+  return (result);
+}
+
+#endif /* not CRAY2 */
+#endif /* CRAY */
+
+#endif /* no alloca */
+#endif /* not GCC version 2 */
diff --git a/Src/diffutils/lib/CMPBUF.C b/Src/diffutils/lib/CMPBUF.C
new file mode 100644 (file)
index 0000000..e95a8f9
--- /dev/null
@@ -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/Src/diffutils/lib/CMPBUF.H b/Src/diffutils/lib/CMPBUF.H
new file mode 100644 (file)
index 0000000..a7621b7
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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.  */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+size_t buffer_lcm PARAMS((size_t, size_t));
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/Src/diffutils/lib/ERROR.C b/Src/diffutils/lib/ERROR.C
new file mode 100644 (file)
index 0000000..a54e241
--- /dev/null
@@ -0,0 +1,110 @@
+/* error.c -- error handler for noninteractive utilities
+   Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Written by David MacKenzie.  */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_VPRINTF
+
+#if __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else /* !__STDC__ */
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif /* !__STDC__ */
+
+#else /* !HAVE_VPRINTF */
+
+#ifdef HAVE_DOPRNT
+#define va_alist args
+#define va_dcl int args;
+#else /* !HAVE_DOPRNT */
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif /* !HAVE_DOPRNT */
+
+#endif /* !HAVE_VPRINTF */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else /* !STDC_HEADERS */
+void exit ();
+#endif /* !STDC_HEADERS */
+
+extern char *program_name;
+
+#ifndef HAVE_STRERROR
+static char *
+private_strerror (errnum)
+     int errnum;
+{
+  extern char *sys_errlist[];
+  extern int sys_nerr;
+
+  if (errnum > 0 && errnum <= sys_nerr)
+    return sys_errlist[errnum];
+  return "Unknown system error";
+}
+#define strerror private_strerror
+#endif /* !HAVE_STRERROR */
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+   format string with optional args.
+   If ERRNUM is nonzero, print its corresponding system error message.
+   Exit with status STATUS if it is nonzero.  */
+/* VARARGS */
+void
+#if defined (HAVE_VPRINTF) && __STDC__
+error (int status, int errnum, char *message, ...)
+#else /* !HAVE_VPRINTF or !__STDC__ */
+error (status, errnum, message, va_alist)
+     int status;
+     int errnum;
+     char *message;
+     va_dcl
+#endif /* !HAVE_VPRINTF or !__STDC__ */
+{
+#ifdef HAVE_VPRINTF
+  va_list args;
+#endif /* HAVE_VPRINTF */
+
+  fprintf (stderr, "%s: ", program_name);
+#ifdef HAVE_VPRINTF
+  VA_START (args, message);
+  vfprintf (stderr, message, args);
+  va_end (args);
+#else /* !HAVE_VPRINTF */
+#ifdef HAVE_DOPRNT
+  _doprnt (message, &args, stderr);
+#else /* !HAVE_DOPRNT */
+  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* !HAVE_DOPRNT */
+#endif /* !HAVE_VPRINTF */
+  if (errnum)
+    fprintf (stderr, ": %s", strerror (errnum));
+  putc ('\n', stderr);
+  fflush (stderr);
+  if (status)
+    exit (status);
+}
diff --git a/Src/diffutils/lib/FNMATCH.C b/Src/diffutils/lib/FNMATCH.C
new file mode 100644 (file)
index 0000000..2fb65b5
--- /dev/null
@@ -0,0 +1,200 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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/Src/diffutils/lib/FNMATCH.H b/Src/diffutils/lib/FNMATCH.H
new file mode 100644 (file)
index 0000000..5c94813
--- /dev/null
@@ -0,0 +1,60 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef        _FNMATCH_H
+
+#define        _FNMATCH_H      1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef __P
+#define        __P(args)       args
+#else /* Not C++ or ANSI C.  */
+#undef __P
+#define        __P(args)       ()
+/* 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.  */
+
+/* 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/Src/diffutils/lib/GETOPT.C b/Src/diffutils/lib/GETOPT.C
new file mode 100644 (file)
index 0000000..7a4673b
--- /dev/null
@@ -0,0 +1,757 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+       Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || 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
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#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.  */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+   long-named option.  Because this is not POSIX.2 compliant, it is
+   being phased out.  */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* XXX 1003.2 says this must be 1 before any call.  */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+\f
+#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.
+   (Supposedly there are some machines where it might get a warning,
+   but changing this conditional to __STDC__ is too risky.)  */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif                         /* GNU C library.  */
+\f
+/* 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;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns `EOF'.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  int option_index;
+
+  optarg = 0;
+
+  /* Initialize the internal data when the first call is made.
+     Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  if (optind == 0)
+    {
+      first_nonopt = last_nonopt = optind = 1;
+
+      nextchar = NULL;
+
+      /* Determine how to handle the ordering of options and nonoptions.  */
+
+      if (optstring[0] == '-')
+       {
+         ordering = RETURN_IN_ORDER;
+         ++optstring;
+       }
+      else if (optstring[0] == '+')
+       {
+         ordering = REQUIRE_ORDER;
+         ++optstring;
+       }
+      else if (getenv ("POSIXLY_CORRECT") != NULL)
+       ordering = REQUIRE_ORDER;
+      else
+       ordering = PERMUTE;
+    }
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      if (ordering == PERMUTE)
+       {
+         /* If we have just processed some options following some non-options,
+            exchange them so that the options come first.  */
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (last_nonopt != optind)
+           first_nonopt = optind;
+
+         /* Now skip any additional non-options
+            and extend the range of non-options previously skipped.  */
+
+         while (optind < argc
+                && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+                && (longopts == NULL
+                    || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif                         /* GETOPT_COMPAT */
+                )
+           optind++;
+         last_nonopt = optind;
+       }
+
+      /* Special ARGV-element `--' means premature end of options.
+        Skip it like a null option,
+        then exchange with previous non-options as if it were an option,
+        then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+       {
+         optind++;
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (first_nonopt == last_nonopt)
+           first_nonopt = optind;
+         last_nonopt = argc;
+
+         optind = argc;
+       }
+
+      /* If we have done all the ARGV-elements, stop the scan
+        and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+       {
+         /* Set the next-arg-index to point at the non-options
+            that we previously skipped, so the caller will digest them.  */
+         if (first_nonopt != last_nonopt)
+           optind = first_nonopt;
+         return EOF;
+       }
+
+      /* If we have come to a non-option and did not permute it,
+        either stop the scan or describe it to the caller and pass it by.  */
+
+      if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+         && (longopts == NULL
+             || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif                         /* GETOPT_COMPAT */
+         )
+       {
+         if (ordering == REQUIRE_ORDER)
+           return EOF;
+         optarg = argv[optind++];
+         return 1;
+       }
+
+      /* We have found another option-ARGV-element.
+        Start decoding its characters.  */
+
+      nextchar = (argv[optind] + 1
+                 + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  if (longopts != NULL
+      && ((argv[optind][0] == '-'
+          && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+         || argv[optind][0] == '+'
+#endif                         /* GETOPT_COMPAT */
+         ))
+    {
+      const struct option *p;
+      char *s = nextchar;
+      int exact = 0;
+      int ambig = 0;
+      const struct option *pfound = NULL;
+      int indfound;
+
+      while (*s && *s != '=')
+       s++;
+
+      /* Test all options for either exact match or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name;
+          p++, option_index++)
+       if (!strncmp (p->name, nextchar, s - nextchar))
+         {
+           if (s - nextchar == strlen (p->name))
+             {
+               /* Exact match found.  */
+               pfound = p;
+               indfound = option_index;
+               exact = 1;
+               break;
+             }
+           else if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else
+             /* Second nonexact match found.  */
+             ambig = 1;
+         }
+
+      if (ambig && !exact)
+       {
+         if (opterr)
+           fprintf (stderr, "%s: option `%s' is ambiguous\n",
+                    argv[0], argv[optind]);
+         nextchar += strlen (nextchar);
+         optind++;
+         return '?';
+       }
+
+      if (pfound != NULL)
+       {
+         option_index = indfound;
+         optind++;
+         if (*s)
+           {
+             /* Don't test has_arg with >, because some C compilers don't
+                allow it to be used on enums.  */
+             if (pfound->has_arg)
+               optarg = s + 1;
+             else
+               {
+                 if (opterr)
+                   {
+                     if (argv[optind - 1][1] == '-')
+                       /* --option */
+                       fprintf (stderr,
+                                "%s: option `--%s' doesn't allow an argument\n",
+                                argv[0], pfound->name);
+                     else
+                       /* +option or -option */
+                       fprintf (stderr,
+                            "%s: option `%c%s' doesn't allow an argument\n",
+                            argv[0], argv[optind - 1][0], pfound->name);
+                   }
+                 nextchar += strlen (nextchar);
+                 return '?';
+               }
+           }
+         else if (pfound->has_arg == 1)
+           {
+             if (optind < argc)
+               optarg = argv[optind++];
+             else
+               {
+                 if (opterr)
+                   fprintf (stderr, "%s: option `%s' requires an argument\n",
+                            argv[0], argv[optind - 1]);
+                 nextchar += strlen (nextchar);
+                 return optstring[0] == ':' ? ':' : '?';
+               }
+           }
+         nextchar += strlen (nextchar);
+         if (longind != NULL)
+           *longind = option_index;
+         if (pfound->flag)
+           {
+             *(pfound->flag) = pfound->val;
+             return 0;
+           }
+         return pfound->val;
+       }
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short
+        option, then it's an error.
+        Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+         || argv[optind][0] == '+'
+#endif                         /* GETOPT_COMPAT */
+         || my_index (optstring, *nextchar) == NULL)
+       {
+         if (opterr)
+           {
+             if (argv[optind][1] == '-')
+               /* --option */
+               fprintf (stderr, "%s: unrecognized option `--%s'\n",
+                        argv[0], nextchar);
+             else
+               /* +option or -option */
+               fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+                        argv[0], argv[optind][0], nextchar);
+           }
+         nextchar = (char *) "";
+         optind++;
+         return '?';
+       }
+    }
+
+  /* Look at and handle the next option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+       if (opterr)
+         {
+#if 0
+           if (c < 040 || c >= 0177)
+             fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+                      argv[0], c);
+           else
+             fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+           /* 1003.2 specifies the format of this message.  */
+           fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+         }
+       optopt = c;
+       return '?';
+      }
+    if (temp[1] == ':')
+      {
+       if (temp[2] == ':')
+         {
+           /* This is an option that accepts an argument optionally.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               optind++;
+             }
+           else
+             optarg = 0;
+           nextchar = NULL;
+         }
+       else
+         {
+           /* This is an option that requires an argument.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               /* If we end this ARGV-element by taking the rest as an arg,
+                  we must advance to the next element now.  */
+               optind++;
+             }
+           else if (optind == argc)
+             {
+               if (opterr)
+                 {
+#if 0
+                   fprintf (stderr, "%s: option `-%c' requires an argument\n",
+                            argv[0], c);
+#else
+                   /* 1003.2 specifies the format of this message.  */
+                   fprintf (stderr, "%s: option requires an argument -- %c\n",
+                            argv[0], c);
+#endif
+                 }
+               optopt = c;
+               if (optstring[0] == ':')
+                 c = ':';
+               else
+                 c = '?';
+             }
+           else
+             /* We already incremented `optind' once;
+                increment it again when taking next ARGV-elt as argument.  */
+             optarg = argv[optind++];
+           nextchar = NULL;
+         }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+                          (const struct option *) 0,
+                          (int *) 0,
+                          0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+\f
+#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/Src/diffutils/lib/GETOPT.H b/Src/diffutils/lib/GETOPT.H
new file mode 100644 (file)
index 0000000..45541f5
--- /dev/null
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+#if    __STDC__
+  const char *name;
+#else
+  char *name;
+#endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define        no_argument             0
+#define required_argument      1
+#define optional_argument      2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+                       const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind,
+                            int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/Src/diffutils/lib/GETOPT1.C b/Src/diffutils/lib/GETOPT1.C
new file mode 100644 (file)
index 0000000..30fa292
--- /dev/null
@@ -0,0 +1,195 @@
+/* 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 __cplusplus
+extern "C" {
+#endif
+\f
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || 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 "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__.  */
+\f
+#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 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
diff --git a/Src/diffutils/lib/REGEX.C b/Src/diffutils/lib/REGEX.C
new file mode 100644 (file)
index 0000000..15ca91b
--- /dev/null
@@ -0,0 +1,5155 @@
+/* Extended regular expression matching and search library,
+   version 0.12.
+   (Implements POSIX draft P10003.2/D11.2, except for
+   internationalization features.)
+
+   Copyright (C) 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+  #pragma alloca
+#endif
+
+#define _GNU_SOURCE
+
+/* We need this for `regex.h', and perhaps for the Emacs include files.  */
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* The `emacs' switch turns on certain matching commands
+   that make sense only in Emacs. */
+#ifdef emacs
+
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+/* Emacs uses `NULL' as a predicate.  */
+#undef NULL
+
+#else  /* not emacs */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+#endif
+
+
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+   `BSTRING', as far as I know, and neither of them use this code.  */
+#if HAVE_STRING_H || STDC_HEADERS
+#include <string.h>
+#ifndef bcmp
+#define bcmp(s1, s2, n)        memcmp ((s1), (s2), (n))
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n)    memset ((s), 0, (n))
+#endif
+#else
+#include <strings.h>
+#endif
+
+/* Define the syntax stuff for \<, \>, etc.  */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+   commands in re_match_2.  */
+#ifndef Sword 
+#define Sword 1
+#endif
+
+#ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set.  */
+#define CHAR_SET_SIZE 256
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+   register int c;
+   static int done = 0;
+
+   if (done)
+     return;
+
+   bzero (re_syntax_table, sizeof re_syntax_table);
+
+   for (c = 'a'; c <= 'z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = 'A'; c <= 'Z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = '0'; c <= '9'; c++)
+     re_syntax_table[c] = Sword;
+
+   re_syntax_table['_'] = Sword;
+
+   done = 1;
+}
+
+#endif /* not SYNTAX_TABLE */
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+\f
+/* Get the interface, including the syntax bits.  */
+#include "regex.h"
+
+/* isalpha etc. are used for the character classes.  */
+#include <ctype.h>
+
+/* Jim Meyering writes:
+
+   "... Some ctype macros are valid only for character codes that
+   isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+   using /bin/cc or gcc but without giving an ansi option).  So, all
+   ctype uses should be through macros like ISPRINT...  If
+   STDC_HEADERS is defined, then autoconf has verified that the ctype
+   macros don't need to be guarded with references to isascii. ...
+   Defining isascii to 1 should let any compiler worth its salt
+   eliminate the && through constant folding."  */
+#if ! defined (isascii) || defined (STDC_HEADERS)
+#undef isascii
+#define isascii(c) 1
+#endif
+
+#ifdef isblank
+#define ISBLANK(c) (isascii (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (isascii (c) && isgraph (c))
+#else
+#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (isascii (c) && isprint (c))
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#define ISALNUM(c) (isascii (c) && isalnum (c))
+#define ISALPHA(c) (isascii (c) && isalpha (c))
+#define ISCNTRL(c) (isascii (c) && iscntrl (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
+#define ISPUNCT(c) (isascii (c) && ispunct (c))
+#define ISSPACE(c) (isascii (c) && isspace (c))
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+   since ours (we hope) works properly with all combinations of
+   machines, compilers, `char' and `unsigned char' argument types.
+   (Per Bothner suggested the basic approach.)  */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else  /* not __STDC__ */
+/* As in Harbison and Steele.  */
+#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+\f
+/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
+   use `alloca' instead of `malloc'.  This is because using malloc in
+   re_search* or re_match* could cause memory leaks when C-g is used in
+   Emacs; also, malloc is slower and causes storage fragmentation.  On
+   the other hand, malloc is more portable, and easier to debug.  
+   
+   Because we sometimes use alloca, some routines have to be macros,
+   not functions -- `alloca'-allocated space disappears at the end of the
+   function it is called in.  */
+
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+
+#else /* not REGEX_MALLOC  */
+
+/* Emacs already defines alloca, sometimes.  */
+#ifndef alloca
+
+/* Make alloca work the best possible way.  */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top.  */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */ 
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+#define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable.  */
+#define REGEX_REALLOCATE(source, osize, nsize)                         \
+  (destination = (char *) alloca (nsize),                              \
+   bcopy (source, destination, osize),                                 \
+   destination)
+
+#endif /* not REGEX_MALLOC */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+   `string1' or just past its end.  This works if PTR is NULL, which is
+   a good thing.  */
+#define FIRST_STRING_P(ptr)                                    \
+  (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail.  */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define RETALLOC_IF(addr, n, t) \
+  if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t)
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits.  */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+\f
+/* These are the command codes that appear in compiled regular
+   expressions.  Some opcodes are followed by argument bytes.  A
+   command code can specify any interpretation whatsoever for its
+   arguments.  Zero bytes may appear in the compiled regular expression.
+
+   The value of `exactn' is needed in search.c (search_buffer) in Emacs.
+   So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+   `exactn' we use here must also be 1.  */
+
+typedef enum
+{
+  no_op = 0,
+
+        /* Followed by one byte giving n, then by n literal bytes.  */
+  exactn = 1,
+
+        /* Matches any (more or less) character.  */
+  anychar,
+
+        /* Matches any one char belonging to specified set.  First
+           following byte is number of bitmap bytes.  Then come bytes
+           for a bitmap saying which chars are in.  Bits in each byte
+           are ordered low-bit-first.  A character is in the set if its
+           bit is 1.  A character too large to have a bit in the map is
+           automatically not in the set.  */
+  charset,
+
+        /* Same parameters as charset, but match any character that is
+           not one of those specified.  */
+  charset_not,
+
+        /* Start remembering the text that is matched, for storing in a
+           register.  Followed by one byte with the register number, in
+           the range 0 to one less than the pattern buffer's re_nsub
+           field.  Then followed by one byte with the number of groups
+           inner to this one.  (This last has to be part of the
+           start_memory only because we need it in the on_failure_jump
+           of re_match_2.)  */
+  start_memory,
+
+        /* Stop remembering the text that is matched and store it in a
+           memory register.  Followed by one byte with the register
+           number, in the range 0 to one less than `re_nsub' in the
+           pattern buffer, and one byte with the number of inner groups,
+           just like `start_memory'.  (We need the number of inner
+           groups here because we don't have any easy way of finding the
+           corresponding start_memory when we're at a stop_memory.)  */
+  stop_memory,
+
+        /* Match a duplicate of something remembered. Followed by one
+           byte containing the register number.  */
+  duplicate,
+
+        /* Fail unless at beginning of line.  */
+  begline,
+
+        /* Fail unless at end of line.  */
+  endline,
+
+        /* Succeeds if at beginning of buffer (if emacs) or at beginning
+           of string to be matched (if not).  */
+  begbuf,
+
+        /* Analogously, for end of buffer/string.  */
+  endbuf,
+        /* Followed by two byte relative address to which to jump.  */
+  jump, 
+
+       /* Same as jump, but marks the end of an alternative.  */
+  jump_past_alt,
+
+        /* Followed by two-byte relative address of place to resume at
+           in case of failure.  */
+  on_failure_jump,
+       
+        /* Like on_failure_jump, but pushes a placeholder instead of the
+           current string position when executed.  */
+  on_failure_keep_string_jump,
+  
+        /* Throw away latest failure point and then jump to following
+           two-byte relative address.  */
+  pop_failure_jump,
+
+        /* Change to pop_failure_jump if know won't have to backtrack to
+           match; otherwise change to jump.  This is used to jump
+           back to the beginning of a repeat.  If what follows this jump
+           clearly won't match what the repeat does, such that we can be
+           sure that there is no use backtracking out of repetitions
+           already matched, then we change it to a pop_failure_jump.
+           Followed by two-byte address.  */
+  maybe_pop_jump,
+
+        /* Jump to following two-byte address, and push a dummy failure
+           point. This failure point will be thrown away if an attempt
+           is made to use it for a failure.  A `+' construct makes this
+           before the first repeat.  Also used as an intermediary kind
+           of jump when compiling an alternative.  */
+  dummy_failure_jump,
+
+       /* Push a dummy failure point and continue.  Used at the end of
+          alternatives.  */
+  push_dummy_failure,
+
+        /* Followed by two-byte relative address and two-byte number n.
+           After matching N times, jump to the address upon failure.  */
+  succeed_n,
+
+        /* Followed by two-byte relative address, and two-byte number n.
+           Jump to the address N times, then fail.  */
+  jump_n,
+
+        /* Set the following two-byte relative address to the
+           subsequent two-byte number.  The address *includes* the two
+           bytes of number.  */
+  set_number_at,
+
+  wordchar,    /* Matches any word-constituent character.  */
+  notwordchar, /* Matches any char that is not a word-constituent.  */
+
+  wordbeg,     /* Succeeds if at word beginning.  */
+  wordend,     /* Succeeds if at word end.  */
+
+  wordbound,   /* Succeeds if at a word boundary.  */
+  notwordbound /* Succeeds if not at a word boundary.  */
+
+#ifdef emacs
+  ,before_dot, /* Succeeds if before point.  */
+  at_dot,      /* Succeeds if at point.  */
+  after_dot,   /* Succeeds if after point.  */
+
+       /* Matches any character whose syntax is specified.  Followed by
+           a byte which contains a syntax code, e.g., Sword.  */
+  syntaxspec,
+
+       /* Matches any character whose syntax is not that specified.  */
+  notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+\f
+/* Common operations on the compiled pattern.  */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
+
+#define STORE_NUMBER(destination, number)                              \
+  do {                                                                 \
+    (destination)[0] = (number) & 0377;                                        \
+    (destination)[1] = (number) >> 8;                                  \
+  } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+   the byte after where the number is stored.  Therefore, DESTINATION
+   must be an lvalue.  */
+
+#define STORE_NUMBER_AND_INCR(destination, number)                     \
+  do {                                                                 \
+    STORE_NUMBER (destination, number);                                        \
+    (destination) += 2;                                                        \
+  } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+   at SOURCE.  */
+
+#define EXTRACT_NUMBER(destination, source)                            \
+  do {                                                                 \
+    (destination) = *(source) & 0377;                                  \
+    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;          \
+  } while (0)
+
+#ifdef DEBUG
+static void
+extract_number (dest, source)
+    int *dest;
+    unsigned char *source;
+{
+  int temp = SIGN_EXTEND_CHAR (*(source + 1)); 
+  *dest = *source & 0377;
+  *dest += temp << 8;
+}
+
+#ifndef EXTRACT_MACROS /* To debug the macros.  */
+#undef EXTRACT_NUMBER
+#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+   SOURCE must be an lvalue.  */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source)                   \
+  do {                                                                 \
+    EXTRACT_NUMBER (destination, source);                              \
+    (source) += 2;                                                     \
+  } while (0)
+
+#ifdef DEBUG
+static void
+extract_number_and_incr (destination, source)
+    int *destination;
+    unsigned char **source;
+{ 
+  extract_number (destination, *source);
+  *source += 2;
+}
+
+#ifndef EXTRACT_MACROS
+#undef EXTRACT_NUMBER_AND_INCR
+#define EXTRACT_NUMBER_AND_INCR(dest, src) \
+  extract_number_and_incr (&dest, &src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+\f
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+   it is doing (if the variable `debug' is nonzero).  If linked with the
+   main program in `iregex.c', you can enter patterns and strings
+   interactively.  And if linked with the main program in `main.c' and
+   the other test files, you can run the already-written tests.  */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging.  */
+#include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging.  */
+#include <assert.h>
+
+static int debug = 0;
+
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)                          \
+  if (debug) print_partial_compiled_pattern (s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)                 \
+  if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+extern void printchar ();
+
+/* Print the fastmap in human-readable form.  */
+
+void
+print_fastmap (fastmap)
+    char *fastmap;
+{
+  unsigned was_a_range = 0;
+  unsigned i = 0;  
+  
+  while (i < (1 << BYTEWIDTH))
+    {
+      if (fastmap[i++])
+       {
+         was_a_range = 0;
+          printchar (i - 1);
+          while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
+            {
+              was_a_range = 1;
+              i++;
+            }
+         if (was_a_range)
+            {
+              printf ("-");
+              printchar (i - 1);
+            }
+        }
+    }
+  putchar ('\n'); 
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+   the START pointer into it and ending just before the pointer END.  */
+
+void
+print_partial_compiled_pattern (start, end)
+    unsigned char *start;
+    unsigned char *end;
+{
+  int mcnt, mcnt2;
+  unsigned char *p = start;
+  unsigned char *pend = end;
+
+  if (start == NULL)
+    {
+      printf ("(null)\n");
+      return;
+    }
+    
+  /* Loop over pattern commands.  */
+  while (p < pend)
+    {
+      printf ("%d:\t", p - start);
+
+      switch ((re_opcode_t) *p++)
+       {
+        case no_op:
+          printf ("/no_op");
+          break;
+
+       case exactn:
+         mcnt = *p++;
+          printf ("/exactn/%d", mcnt);
+          do
+           {
+              putchar ('/');
+             printchar (*p++);
+            }
+          while (--mcnt);
+          break;
+
+       case start_memory:
+          mcnt = *p++;
+          printf ("/start_memory/%d/%d", mcnt, *p++);
+          break;
+
+       case stop_memory:
+          mcnt = *p++;
+         printf ("/stop_memory/%d/%d", mcnt, *p++);
+          break;
+
+       case duplicate:
+         printf ("/duplicate/%d", *p++);
+         break;
+
+       case anychar:
+         printf ("/anychar");
+         break;
+
+       case charset:
+        case charset_not:
+          {
+            register int c, last = -100;
+           register int in_range = 0;
+
+           printf ("/charset [%s",
+                   (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
+            
+            assert (p + *p < pend);
+
+            for (c = 0; c < 256; c++)
+             if (c / 8 < *p
+                 && (p[1 + (c/8)] & (1 << (c % 8))))
+               {
+                 /* Are we starting a range?  */
+                 if (last + 1 == c && ! in_range)
+                   {
+                     putchar ('-');
+                     in_range = 1;
+                   }
+                 /* Have we broken a range?  */
+                 else if (last + 1 != c && in_range)
+              {
+                     printchar (last);
+                     in_range = 0;
+                   }
+                
+                 if (! in_range)
+                   printchar (c);
+
+                 last = c;
+              }
+
+           if (in_range)
+             printchar (last);
+
+           putchar (']');
+
+           p += 1 + *p;
+         }
+         break;
+
+       case begline:
+         printf ("/begline");
+          break;
+
+       case endline:
+          printf ("/endline");
+          break;
+
+       case on_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/on_failure_jump to %d", p + mcnt - start);
+          break;
+
+       case on_failure_keep_string_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/on_failure_keep_string_jump to %d", p + mcnt - start);
+          break;
+
+       case dummy_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/dummy_failure_jump to %d", p + mcnt - start);
+          break;
+
+       case push_dummy_failure:
+          printf ("/push_dummy_failure");
+          break;
+          
+        case maybe_pop_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/maybe_pop_jump to %d", p + mcnt - start);
+         break;
+
+        case pop_failure_jump:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/pop_failure_jump to %d", p + mcnt - start);
+         break;          
+          
+        case jump_past_alt:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/jump_past_alt to %d", p + mcnt - start);
+         break;          
+          
+        case jump:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/jump to %d", p + mcnt - start);
+         break;
+
+        case succeed_n: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2);
+          break;
+        
+        case jump_n: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2);
+          break;
+        
+        case set_number_at: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2);
+          break;
+        
+        case wordbound:
+         printf ("/wordbound");
+         break;
+
+       case notwordbound:
+         printf ("/notwordbound");
+          break;
+
+       case wordbeg:
+         printf ("/wordbeg");
+         break;
+          
+       case wordend:
+         printf ("/wordend");
+          
+#ifdef emacs
+       case before_dot:
+         printf ("/before_dot");
+          break;
+
+       case at_dot:
+         printf ("/at_dot");
+          break;
+
+       case after_dot:
+         printf ("/after_dot");
+          break;
+
+       case syntaxspec:
+          printf ("/syntaxspec");
+         mcnt = *p++;
+         printf ("/%d", mcnt);
+          break;
+         
+       case notsyntaxspec:
+          printf ("/notsyntaxspec");
+         mcnt = *p++;
+         printf ("/%d", mcnt);
+         break;
+#endif /* emacs */
+
+       case wordchar:
+         printf ("/wordchar");
+          break;
+         
+       case notwordchar:
+         printf ("/notwordchar");
+          break;
+
+       case begbuf:
+         printf ("/begbuf");
+          break;
+
+       case endbuf:
+         printf ("/endbuf");
+          break;
+
+        default:
+          printf ("?%d", *(p-1));
+       }
+
+      putchar ('\n');
+    }
+
+  printf ("%d:\tend of pattern.\n", p - start);
+}
+
+
+void
+print_compiled_pattern (bufp)
+    struct re_pattern_buffer *bufp;
+{
+  unsigned char *buffer = bufp->buffer;
+
+  print_partial_compiled_pattern (buffer, buffer + bufp->used);
+  printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
+
+  if (bufp->fastmap_accurate && bufp->fastmap)
+    {
+      printf ("fastmap: ");
+      print_fastmap (bufp->fastmap);
+    }
+
+  printf ("re_nsub: %d\t", bufp->re_nsub);
+  printf ("regs_alloc: %d\t", bufp->regs_allocated);
+  printf ("can_be_null: %d\t", bufp->can_be_null);
+  printf ("newline_anchor: %d\n", bufp->newline_anchor);
+  printf ("no_sub: %d\t", bufp->no_sub);
+  printf ("not_bol: %d\t", bufp->not_bol);
+  printf ("not_eol: %d\t", bufp->not_eol);
+  printf ("syntax: %d\n", bufp->syntax);
+  /* Perhaps we should print the translate table?  */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+    const char *where;
+    const char *string1;
+    const char *string2;
+    int size1;
+    int size2;
+{
+  unsigned this_char;
+  
+  if (where == NULL)
+    printf ("(null)");
+  else
+    {
+      if (FIRST_STRING_P (where))
+        {
+          for (this_char = where - string1; this_char < size1; this_char++)
+            printchar (string1[this_char]);
+
+          where = string2;    
+        }
+
+      for (this_char = where - string2; this_char < size2; this_char++)
+        printchar (string2[this_char]);
+    }
+}
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+\f
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (syntax)
+    reg_syntax_t syntax;
+{
+  reg_syntax_t ret = re_syntax_options;
+  
+  re_syntax_options = syntax;
+  return ret;
+}
+\f
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.  */
+
+static const char *re_error_msg[] =
+  { NULL,                                      /* REG_NOERROR */
+    "No match",                                        /* REG_NOMATCH */
+    "Invalid regular expression",              /* REG_BADPAT */
+    "Invalid collation character",             /* REG_ECOLLATE */
+    "Invalid character class name",            /* REG_ECTYPE */
+    "Trailing backslash",                      /* REG_EESCAPE */
+    "Invalid back reference",                  /* REG_ESUBREG */
+    "Unmatched [ or [^",                       /* REG_EBRACK */
+    "Unmatched ( or \\(",                      /* REG_EPAREN */
+    "Unmatched \\{",                           /* REG_EBRACE */
+    "Invalid content of \\{\\}",               /* REG_BADBR */
+    "Invalid range end",                       /* REG_ERANGE */
+    "Memory exhausted",                                /* REG_ESPACE */
+    "Invalid preceding regular expression",    /* REG_BADRPT */
+    "Premature end of regular expression",     /* REG_EEND */
+    "Regular expression too big",              /* REG_ESIZE */
+    "Unmatched ) or \\)",                      /* REG_ERPAREN */
+  };
+\f
+/* Avoiding alloca during matching, to placate r_alloc.  */
+
+/* Define MATCH_MAY_ALLOCATE if we need to make sure that the
+   searching and matching functions should not call alloca.  On some
+   systems, alloca is implemented in terms of malloc, and if we're
+   using the relocating allocator routines, then malloc could cause a
+   relocation, which might (if the strings being searched are in the
+   ralloc heap) shift the data out from underneath the regexp
+   routines.
+
+   Here's another reason to avoid allocation: Emacs insists on
+   processing input from X in a signal handler; processing X input may
+   call malloc; if input arrives while a matching routine is calling
+   malloc, then we're scrod.  But Emacs can't just block input while
+   calling matching routines; then we don't notice interrupts when
+   they come in.  So, Emacs blocks input around all regexp calls
+   except the matching calls, which it leaves unprotected, in the
+   faith that they will not malloc.  */
+
+/* Normally, this is fine.  */
+#define MATCH_MAY_ALLOCATE
+
+/* But under some circumstances, it's not.  */
+#if defined (emacs) || (defined (REL_ALLOC) && defined (C_ALLOCA))
+#undef MATCH_MAY_ALLOCATE
+#endif
+
+\f
+/* Failure stack declarations and macros; both re_compile_fastmap and
+   re_match_2 use a failure stack.  These have to be macros because of
+   REGEX_ALLOCATE.  */
+   
+
+/* Number of failure points for which to initially allocate space
+   when matching.  If this number is exceeded, we allocate more
+   space, so it is not a hard limit.  */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack.  Would be
+   exactly that if always used MAX_FAILURE_SPACE each time we failed.
+   This is a variable only so users of regex can assign to it; we never
+   change it ourselves.  */
+int re_max_failures = 2000;
+
+typedef const unsigned char *fail_stack_elt_t;
+
+typedef struct
+{
+  fail_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;                      /* Offset of next open position.  */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
+#define FAIL_STACK_TOP()       (fail_stack.stack[fail_stack.avail])
+
+
+/* Initialize `fail_stack'.  Do `return -2' if the alloc fails.  */
+
+#ifdef MATCH_MAY_ALLOCATE
+#define INIT_FAIL_STACK()                                              \
+  do {                                                                 \
+    fail_stack.stack = (fail_stack_elt_t *)                            \
+      REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+                                                                       \
+    if (fail_stack.stack == NULL)                                      \
+      return -2;                                                       \
+                                                                       \
+    fail_stack.size = INIT_FAILURE_ALLOC;                              \
+    fail_stack.avail = 0;                                              \
+  } while (0)
+#else
+#define INIT_FAIL_STACK()                                              \
+  do {                                                                 \
+    fail_stack.avail = 0;                                              \
+  } while (0)
+#endif
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+   Return 1 if succeeds, and 0 if either ran out of memory
+   allocating space for it or it was already too large.  
+   
+   REGEX_REALLOCATE requires `destination' be declared.   */
+
+#define DOUBLE_FAIL_STACK(fail_stack)                                  \
+  ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS             \
+   ? 0                                                                 \
+   : ((fail_stack).stack = (fail_stack_elt_t *)                                \
+        REGEX_REALLOCATE ((fail_stack).stack,                          \
+          (fail_stack).size * sizeof (fail_stack_elt_t),               \
+          ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)),       \
+                                                                       \
+      (fail_stack).stack == NULL                                       \
+      ? 0                                                              \
+      : ((fail_stack).size <<= 1,                                      \
+         1)))
+
+
+/* Push PATTERN_OP on FAIL_STACK. 
+
+   Return 1 if was able to do so and 0 if ran out of memory allocating
+   space to do so.  */
+#define PUSH_PATTERN_OP(pattern_op, fail_stack)                                \
+  ((FAIL_STACK_FULL ()                                                 \
+    && !DOUBLE_FAIL_STACK (fail_stack))                                        \
+    ? 0                                                                        \
+    : ((fail_stack).stack[(fail_stack).avail++] = pattern_op,          \
+       1))
+
+/* This pushes an item onto the failure stack.  Must be a four-byte
+   value.  Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_ITEM(item)                                                \
+  fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
+
+/* The complement operation.  Assumes `fail_stack' is nonempty.  */
+#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging.  */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+   if we ever fail back to it.  
+   
+   Requires variables fail_stack, regstart, regend, reg_info, and
+   num_regs be declared.  DOUBLE_FAIL_STACK requires `destination' be
+   declared.
+   
+   Does `return FAILURE_CODE' if runs out of memory.  */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)  \
+  do {                                                                 \
+    char *destination;                                                 \
+    /* Must be int, so when we don't save any registers, the arithmetic        \
+       of 0 + -1 isn't done as unsigned.  */                           \
+    int this_reg;                                                      \
+                                                                       \
+    DEBUG_STATEMENT (failure_id++);                                    \
+    DEBUG_STATEMENT (nfailure_points_pushed++);                                \
+    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);          \
+    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
+    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
+                                                                       \
+    DEBUG_PRINT2 ("  slots needed: %d\n", NUM_FAILURE_ITEMS);          \
+    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);      \
+                                                                       \
+    /* Ensure we have enough space allocated for what we will push.  */        \
+    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)                  \
+      {                                                                        \
+        if (!DOUBLE_FAIL_STACK (fail_stack))                   \
+          return failure_code;                                         \
+                                                                       \
+        DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",             \
+                      (fail_stack).size);                              \
+        DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+      }                                                                        \
+                                                                       \
+    /* Push the info, starting with the registers.  */                 \
+    DEBUG_PRINT1 ("\n");                                               \
+                                                                       \
+    for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+         this_reg++)                                                   \
+      {                                                                        \
+       DEBUG_PRINT2 ("  Pushing reg: %d\n", this_reg);                 \
+        DEBUG_STATEMENT (num_regs_pushed++);                           \
+                                                                       \
+       DEBUG_PRINT2 ("    start: 0x%x\n", regstart[this_reg]);         \
+        PUSH_FAILURE_ITEM (regstart[this_reg]);                                \
+                                                                        \
+       DEBUG_PRINT2 ("    end: 0x%x\n", regend[this_reg]);             \
+        PUSH_FAILURE_ITEM (regend[this_reg]);                          \
+                                                                       \
+       DEBUG_PRINT2 ("    info: 0x%x\n      ", reg_info[this_reg]);    \
+        DEBUG_PRINT2 (" match_null=%d",                                        \
+                      REG_MATCH_NULL_STRING_P (reg_info[this_reg]));   \
+        DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));   \
+        DEBUG_PRINT2 (" matched_something=%d",                         \
+                      MATCHED_SOMETHING (reg_info[this_reg]));         \
+        DEBUG_PRINT2 (" ever_matched=%d",                              \
+                      EVER_MATCHED_SOMETHING (reg_info[this_reg]));    \
+       DEBUG_PRINT1 ("\n");                                            \
+        PUSH_FAILURE_ITEM (reg_info[this_reg].word);                   \
+      }                                                                        \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing  low active reg: %d\n", lowest_active_reg);\
+    PUSH_FAILURE_ITEM (lowest_active_reg);                             \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing high active reg: %d\n", highest_active_reg);\
+    PUSH_FAILURE_ITEM (highest_active_reg);                            \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing pattern 0x%x: ", pattern_place);          \
+    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);          \
+    PUSH_FAILURE_ITEM (pattern_place);                                 \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing string 0x%x: `", string_place);           \
+    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,   \
+                                size2);                                \
+    DEBUG_PRINT1 ("'\n");                                              \
+    PUSH_FAILURE_ITEM (string_place);                                  \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);           \
+    DEBUG_PUSH (failure_id);                                           \
+  } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+   for each register.  */
+#define NUM_REG_ITEMS  3
+
+/* Individual items aside from the registers.  */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id.  */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack.  */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items.  */
+#define NUM_FAILURE_ITEMS                                              \
+  ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS        \
+    + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it.  */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+   We restore into the parameters, all of which should be lvalues:
+     STR -- the saved data position.
+     PAT -- the saved pattern position.
+     LOW_REG, HIGH_REG -- the highest and lowest active registers.
+     REGSTART, REGEND -- arrays of string positions.
+     REG_INFO -- array of information about each subexpression.
+   
+   Also assumes the variables `fail_stack' and (if debugging), `bufp',
+   `pend', `string1', `size1', `string2', and `size2'.  */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{                                                                      \
+  DEBUG_STATEMENT (fail_stack_elt_t failure_id;)                       \
+  int this_reg;                                                                \
+  const unsigned char *string_temp;                                    \
+                                                                       \
+  assert (!FAIL_STACK_EMPTY ());                                       \
+                                                                       \
+  /* Remove failure points and point to how many regs pushed.  */      \
+  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");                               \
+  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);   \
+  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size);    \
+                                                                       \
+  assert (fail_stack.avail >= NUM_NONREG_ITEMS);                       \
+                                                                       \
+  DEBUG_POP (&failure_id);                                             \
+  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);             \
+                                                                       \
+  /* If the saved string location is NULL, it came from an             \
+     on_failure_keep_string_jump opcode, and we want to throw away the \
+     saved NULL, thus retaining our current position in the string.  */        \
+  string_temp = POP_FAILURE_ITEM ();                                   \
+  if (string_temp != NULL)                                             \
+    str = (const char *) string_temp;                                  \
+                                                                       \
+  DEBUG_PRINT2 ("  Popping string 0x%x: `", str);                      \
+  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);     \
+  DEBUG_PRINT1 ("'\n");                                                        \
+                                                                       \
+  pat = (unsigned char *) POP_FAILURE_ITEM ();                         \
+  DEBUG_PRINT2 ("  Popping pattern 0x%x: ", pat);                      \
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);                      \
+                                                                       \
+  /* Restore register info.  */                                                \
+  high_reg = (unsigned) POP_FAILURE_ITEM ();                           \
+  DEBUG_PRINT2 ("  Popping high active reg: %d\n", high_reg);          \
+                                                                       \
+  low_reg = (unsigned) POP_FAILURE_ITEM ();                            \
+  DEBUG_PRINT2 ("  Popping  low active reg: %d\n", low_reg);           \
+                                                                       \
+  for (this_reg = high_reg; this_reg >= low_reg; this_reg--)           \
+    {                                                                  \
+      DEBUG_PRINT2 ("    Popping reg: %d\n", this_reg);                        \
+                                                                       \
+      reg_info[this_reg].word = POP_FAILURE_ITEM ();                   \
+      DEBUG_PRINT2 ("      info: 0x%x\n", reg_info[this_reg]);         \
+                                                                       \
+      regend[this_reg] = (const char *) POP_FAILURE_ITEM ();           \
+      DEBUG_PRINT2 ("      end: 0x%x\n", regend[this_reg]);            \
+                                                                       \
+      regstart[this_reg] = (const char *) POP_FAILURE_ITEM ();         \
+      DEBUG_PRINT2 ("      start: 0x%x\n", regstart[this_reg]);                \
+    }                                                                  \
+                                                                       \
+  DEBUG_STATEMENT (nfailure_points_popped++);                          \
+} /* POP_FAILURE_POINT */
+
+
+\f
+/* Structure for per-register (a.k.a. per-group) information.
+   This must not be longer than one word, because we push this value
+   onto the failure stack.  Other register information, such as the
+   starting and ending positions (which are addresses), and the list of
+   inner groups (which is a bits list) are maintained in separate
+   variables.  
+   
+   We are making a (strictly speaking) nonportable assumption here: that
+   the compiler will pack our bit fields into something that fits into
+   the type of `word', i.e., is something that fits into one item on the
+   failure stack.  */
+typedef union
+{
+  fail_stack_elt_t word;
+  struct
+  {
+      /* This field is one if this group can match the empty string,
+         zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
+#define MATCH_NULL_UNSET_VALUE 3
+    unsigned match_null_string_p : 2;
+    unsigned is_active : 1;
+    unsigned matched_something : 1;
+    unsigned ever_matched_something : 1;
+  } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R)  ((R).bits.is_active)
+#define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+   for the subexpressions which we are currently inside.  Also records
+   that those subexprs have matched.  */
+#define SET_REGS_MATCHED()                                             \
+  do                                                                   \
+    {                                                                  \
+      unsigned r;                                                      \
+      for (r = lowest_active_reg; r <= highest_active_reg; r++)                \
+        {                                                              \
+          MATCHED_SOMETHING (reg_info[r])                              \
+            = EVER_MATCHED_SOMETHING (reg_info[r])                     \
+            = 1;                                                       \
+        }                                                              \
+    }                                                                  \
+  while (0)
+
+
+/* Registers are set to a sentinel when they haven't yet matched.  */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+\f
+/* How do we implement a missing MATCH_MAY_ALLOCATE?
+   We make the fail stack a global thing, and then grow it to
+   re_max_failures when we compile.  */
+#ifndef MATCH_MAY_ALLOCATE
+static fail_stack_type fail_stack;
+
+static const char **     regstart, **     regend;
+static const char ** old_regstart, ** old_regend;
+static const char **best_regstart, **best_regend;
+static register_info_type *reg_info; 
+static const char **reg_dummy;
+static register_info_type *reg_info_dummy;
+#endif
+
+\f
+/* Subroutine declarations and macros for regex_compile.  */
+
+static void store_op1 (), store_op2 ();
+static void insert_op1 (), insert_op2 ();
+static boolean at_begline_loc_p (), at_endline_loc_p ();
+static boolean group_in_compile_stack ();
+static reg_errcode_t compile_range ();
+
+/* Fetch the next character in the uncompiled pattern---translating it 
+   if necessary.  Also cast from a signed character in the constant
+   string passed to us by the user to an unsigned char that we can use
+   as an array index (in, e.g., `translate').  */
+#define PATFETCH(c)                                                    \
+  do {if (p == pend) return REG_EEND;                                  \
+    c = (unsigned char) *p++;                                          \
+    if (translate) c = translate[c];                                   \
+  } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+   translation.  */
+#define PATFETCH_RAW(c)                                                        \
+  do {if (p == pend) return REG_EEND;                                  \
+    c = (unsigned char) *p++;                                          \
+  } while (0)
+
+/* Go backwards one character in the pattern.  */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D.  We
+   cast the subscript to translate because some data is declared as
+   `char *', to avoid warnings when a string constant is passed.  But
+   when we use a character as a subscript we must make it unsigned.  */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'.  */
+
+/* If the buffer isn't allocated when it comes in, use this.  */
+#define INIT_BUF_SIZE  32
+
+/* Make sure we have at least N more bytes of space in buffer.  */
+#define GET_BUFFER_SPACE(n)                                            \
+    while (b - bufp->buffer + (n) > bufp->allocated)                   \
+      EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it.  */
+#define BUF_PUSH(c)                                                    \
+  do {                                                                 \
+    GET_BUFFER_SPACE (1);                                              \
+    *b++ = (unsigned char) (c);                                                \
+  } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
+#define BUF_PUSH_2(c1, c2)                                             \
+  do {                                                                 \
+    GET_BUFFER_SPACE (2);                                              \
+    *b++ = (unsigned char) (c1);                                       \
+    *b++ = (unsigned char) (c2);                                       \
+  } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes.  */
+#define BUF_PUSH_3(c1, c2, c3)                                         \
+  do {                                                                 \
+    GET_BUFFER_SPACE (3);                                              \
+    *b++ = (unsigned char) (c1);                                       \
+    *b++ = (unsigned char) (c2);                                       \
+    *b++ = (unsigned char) (c3);                                       \
+  } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO.  We store a
+   relative address offset by the three bytes the jump itself occupies.  */
+#define STORE_JUMP(op, loc, to) \
+  store_op1 (op, loc, (to) - (loc) - 3)
+
+/* Likewise, for a two-argument jump.  */
+#define STORE_JUMP2(op, loc, to, arg) \
+  store_op2 (op, loc, (to) - (loc) - 3, arg)
+
+/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP(op, loc, to) \
+  insert_op1 (op, loc, (to) - (loc) - 3, b)
+
+/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP2(op, loc, to, arg) \
+  insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+   into the pattern are two bytes long.  So if 2^16 bytes turns out to
+   be too small, many things would have to change.  */
+#define MAX_BUF_SIZE (1L << 16)
+
+
+/* Extend the buffer by twice its current size via realloc and
+   reset the pointers that pointed into the old block to point to the
+   correct places in the new one.  If extending the buffer results in it
+   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
+#define EXTEND_BUFFER()                                                        \
+  do {                                                                         \
+    unsigned char *old_buffer = bufp->buffer;                          \
+    if (bufp->allocated == MAX_BUF_SIZE)                               \
+      return REG_ESIZE;                                                        \
+    bufp->allocated <<= 1;                                             \
+    if (bufp->allocated > MAX_BUF_SIZE)                                        \
+      bufp->allocated = MAX_BUF_SIZE;                                  \
+    bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
+    if (bufp->buffer == NULL)                                          \
+      return REG_ESPACE;                                               \
+    /* If the buffer moved, move all the pointers into it.  */         \
+    if (old_buffer != bufp->buffer)                                    \
+      {                                                                        \
+        b = (b - old_buffer) + bufp->buffer;                           \
+        begalt = (begalt - old_buffer) + bufp->buffer;                 \
+        if (fixup_alt_jump)                                            \
+          fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+        if (laststart)                                                 \
+          laststart = (laststart - old_buffer) + bufp->buffer;         \
+        if (pending_exact)                                             \
+          pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+      }                                                                        \
+  } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+   {start,stop}_memory, the maximum number of groups we can report
+   things about is what fits in that byte.  */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers.  We just
+   ignore the excess.  */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack.  */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+  pattern_offset_t begalt_offset;
+  pattern_offset_t fixup_alt_jump;
+  pattern_offset_t inner_group_offset;
+  pattern_offset_t laststart_offset;  
+  regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+  compile_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;                      /* Offset of next open position.  */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
+
+/* The next available element.  */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list.  */
+#define SET_LIST_BIT(c)                               \
+  (b[((unsigned char) (c)) / BYTEWIDTH]               \
+   |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern.  */
+#define GET_UNSIGNED_NUMBER(num)                                       \
+  { if (p != pend)                                                     \
+     {                                                                 \
+       PATFETCH (c);                                                   \
+       while (ISDIGIT (c))                                             \
+         {                                                             \
+           if (num < 0)                                                        \
+              num = 0;                                                 \
+           num = num * 10 + c - '0';                                   \
+           if (p == pend)                                              \
+              break;                                                   \
+           PATFETCH (c);                                               \
+         }                                                             \
+       }                                                               \
+    }          
+
+#define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+
+#define IS_CHAR_CLASS(string)                                          \
+   (STREQ (string, "alpha") || STREQ (string, "upper")                 \
+    || STREQ (string, "lower") || STREQ (string, "digit")              \
+    || STREQ (string, "alnum") || STREQ (string, "xdigit")             \
+    || STREQ (string, "space") || STREQ (string, "print")              \
+    || STREQ (string, "punct") || STREQ (string, "graph")              \
+    || STREQ (string, "cntrl") || STREQ (string, "blank"))
+\f
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+   Returns one of error codes defined in `regex.h', or zero for success.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate'
+   fields are set in BUFP on entry.
+
+   If it succeeds, results are put in BUFP (if it returns an error, the
+   contents of BUFP are undefined):
+     `buffer' is the compiled pattern;
+     `syntax' is set to SYNTAX;
+     `used' is set to the length of the compiled pattern;
+     `fastmap_accurate' is zero;
+     `re_nsub' is the number of subexpressions in PATTERN;
+     `not_bol' and `not_eol' are zero;
+   
+   The `fastmap' and `newline_anchor' fields are neither
+   examined nor set.  */
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+     const char *pattern;
+     int size;
+     reg_syntax_t syntax;
+     struct re_pattern_buffer *bufp;
+{
+  /* We fetch characters from PATTERN here.  Even though PATTERN is
+     `char *' (i.e., signed), we declare these variables as unsigned, so
+     they can be reliably used as array indices.  */
+  register unsigned char c, c1;
+  
+  /* A random tempory spot in PATTERN.  */
+  const char *p1;
+
+  /* Points to the end of the buffer, where we should append.  */
+  register unsigned char *b;
+  
+  /* Keeps track of unclosed groups.  */
+  compile_stack_type compile_stack;
+
+  /* Points to the current (ending) position in the pattern.  */
+  const char *p = pattern;
+  const char *pend = pattern + size;
+  
+  /* How to translate the characters in the pattern.  */
+  char *translate = bufp->translate;
+
+  /* Address of the count-byte of the most recently inserted `exactn'
+     command.  This makes it possible to tell if a new exact-match
+     character can be added to that command or if the character requires
+     a new `exactn' command.  */
+  unsigned char *pending_exact = 0;
+
+  /* Address of start of the most recently finished expression.
+     This tells, e.g., postfix * where to find the start of its
+     operand.  Reset at the beginning of groups and alternatives.  */
+  unsigned char *laststart = 0;
+
+  /* Address of beginning of regexp, or inside of last group.  */
+  unsigned char *begalt;
+
+  /* Place in the uncompiled pattern (i.e., the {) to
+     which to go back if the interval is invalid.  */
+  const char *beg_interval;
+                
+  /* Address of the place where a forward jump should go to the end of
+     the containing expression.  Each alternative of an `or' -- except the
+     last -- ends with a forward jump of this sort.  */
+  unsigned char *fixup_alt_jump = 0;
+
+  /* Counts open-groups as they are encountered.  Remembered for the
+     matching close-group on the compile stack, so the same register
+     number is put in the stop_memory as the start_memory.  */
+  regnum_t regnum = 0;
+
+#ifdef DEBUG
+  DEBUG_PRINT1 ("\nCompiling pattern: ");
+  if (debug)
+    {
+      unsigned debug_count;
+      
+      for (debug_count = 0; debug_count < size; debug_count++)
+        printchar (pattern[debug_count]);
+      putchar ('\n');
+    }
+#endif /* DEBUG */
+
+  /* Initialize the compile stack.  */
+  compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+  if (compile_stack.stack == NULL)
+    return REG_ESPACE;
+
+  compile_stack.size = INIT_COMPILE_STACK_SIZE;
+  compile_stack.avail = 0;
+
+  /* Initialize the pattern buffer.  */
+  bufp->syntax = syntax;
+  bufp->fastmap_accurate = 0;
+  bufp->not_bol = bufp->not_eol = 0;
+
+  /* Set `used' to zero, so that if we return an error, the pattern
+     printer (for debugging) will think there's no pattern.  We reset it
+     at the end.  */
+  bufp->used = 0;
+  
+  /* Always count groups, whether or not bufp->no_sub is set.  */
+  bufp->re_nsub = 0;                           
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+  /* Initialize the syntax table.  */
+   init_syntax_once ();
+#endif
+
+  if (bufp->allocated == 0)
+    {
+      if (bufp->buffer)
+       { /* If zero allocated, but buffer is non-null, try to realloc
+             enough space.  This loses if buffer's address is bogus, but
+             that is the user's responsibility.  */
+          RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+        }
+      else
+        { /* Caller did not allocate a buffer.  Do it for them.  */
+          bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+        }
+      if (!bufp->buffer) return REG_ESPACE;
+
+      bufp->allocated = INIT_BUF_SIZE;
+    }
+
+  begalt = b = bufp->buffer;
+
+  /* Loop through the uncompiled pattern until we're at the end.  */
+  while (p != pend)
+    {
+      PATFETCH (c);
+
+      switch (c)
+        {
+        case '^':
+          {
+            if (   /* If at start of pattern, it's an operator.  */
+                   p == pattern + 1
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's come before.  */
+                || at_begline_loc_p (pattern, p, syntax))
+              BUF_PUSH (begline);
+            else
+              goto normal_char;
+          }
+          break;
+
+
+        case '$':
+          {
+            if (   /* If at end of pattern, it's an operator.  */
+                   p == pend 
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's next.  */
+                || at_endline_loc_p (p, pend, syntax))
+               BUF_PUSH (endline);
+             else
+               goto normal_char;
+           }
+           break;
+
+
+       case '+':
+        case '?':
+          if ((syntax & RE_BK_PLUS_QM)
+              || (syntax & RE_LIMITED_OPS))
+            goto normal_char;
+        handle_plus:
+        case '*':
+          /* If there is no previous pattern... */
+          if (!laststart)
+            {
+              if (syntax & RE_CONTEXT_INVALID_OPS)
+                return REG_BADRPT;
+              else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+                goto normal_char;
+            }
+
+          {
+            /* Are we optimizing this jump?  */
+            boolean keep_string_p = false;
+            
+            /* 1 means zero (many) matches is allowed.  */
+            char zero_times_ok = 0, many_times_ok = 0;
+
+            /* If there is a sequence of repetition chars, collapse it
+               down to just one (the right one).  We can't combine
+               interval operators with these because of, e.g., `a{2}*',
+               which should only match an even number of `a's.  */
+
+            for (;;)
+              {
+                zero_times_ok |= c != '+';
+                many_times_ok |= c != '?';
+
+                if (p == pend)
+                  break;
+
+                PATFETCH (c);
+
+                if (c == '*'
+                    || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+                  ;
+
+                else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
+                  {
+                    if (p == pend) return REG_EESCAPE;
+
+                    PATFETCH (c1);
+                    if (!(c1 == '+' || c1 == '?'))
+                      {
+                        PATUNFETCH;
+                        PATUNFETCH;
+                        break;
+                      }
+
+                    c = c1;
+                  }
+                else
+                  {
+                    PATUNFETCH;
+                    break;
+                  }
+
+                /* If we get here, we found another repeat character.  */
+               }
+
+            /* Star, etc. applied to an empty pattern is equivalent
+               to an empty pattern.  */
+            if (!laststart)  
+              break;
+
+            /* Now we know whether or not zero matches is allowed
+               and also whether or not two or more matches is allowed.  */
+            if (many_times_ok)
+              { /* More than one repetition is allowed, so put in at the
+                   end a backward relative jump from `b' to before the next
+                   jump we're going to put in below (which jumps from
+                   laststart to after this jump).  
+
+                   But if we are at the `*' in the exact sequence `.*\n',
+                   insert an unconditional jump backwards to the .,
+                   instead of the beginning of the loop.  This way we only
+                   push a failure point once, instead of every time
+                   through the loop.  */
+                assert (p - 1 > pattern);
+
+                /* Allocate the space for the jump.  */
+                GET_BUFFER_SPACE (3);
+
+                /* We know we are not at the first character of the pattern,
+                   because laststart was nonzero.  And we've already
+                   incremented `p', by the way, to be the character after
+                   the `*'.  Do we have to do something analogous here
+                   for null bytes, because of RE_DOT_NOT_NULL?  */
+                if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+                   && zero_times_ok
+                    && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+                    && !(syntax & RE_DOT_NEWLINE))
+                  { /* We have .*\n.  */
+                    STORE_JUMP (jump, b, laststart);
+                    keep_string_p = true;
+                  }
+                else
+                  /* Anything else.  */
+                  STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+                /* We've added more stuff to the buffer.  */
+                b += 3;
+              }
+
+            /* On failure, jump from laststart to b + 3, which will be the
+               end of the buffer after this jump is inserted.  */
+            GET_BUFFER_SPACE (3);
+            INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+                                       : on_failure_jump,
+                         laststart, b + 3);
+            pending_exact = 0;
+            b += 3;
+
+            if (!zero_times_ok)
+              {
+                /* At least one repetition is required, so insert a
+                   `dummy_failure_jump' before the initial
+                   `on_failure_jump' instruction of the loop. This
+                   effects a skip over that instruction the first time
+                   we hit that loop.  */
+                GET_BUFFER_SPACE (3);
+                INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+                b += 3;
+              }
+            }
+         break;
+
+
+       case '.':
+          laststart = b;
+          BUF_PUSH (anychar);
+          break;
+
+
+        case '[':
+          {
+            boolean had_char_class = false;
+
+            if (p == pend) return REG_EBRACK;
+
+            /* Ensure that we have enough space to push a charset: the
+               opcode, the length count, and the bitset; 34 bytes in all.  */
+           GET_BUFFER_SPACE (34);
+
+            laststart = b;
+
+            /* We test `*p == '^' twice, instead of using an if
+               statement, so we only need one BUF_PUSH.  */
+            BUF_PUSH (*p == '^' ? charset_not : charset); 
+            if (*p == '^')
+              p++;
+
+            /* Remember the first position in the bracket expression.  */
+            p1 = p;
+
+            /* Push the number of bytes in the bitmap.  */
+            BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* Clear the whole map.  */
+            bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* charset_not matches newline according to a syntax bit.  */
+            if ((re_opcode_t) b[-2] == charset_not
+                && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+              SET_LIST_BIT ('\n');
+
+            /* Read in characters and ranges, setting map bits.  */
+            for (;;)
+              {
+                if (p == pend) return REG_EBRACK;
+
+                PATFETCH (c);
+
+                /* \ might escape characters inside [...] and [^...].  */
+                if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+                  {
+                    if (p == pend) return REG_EESCAPE;
+
+                    PATFETCH (c1);
+                    SET_LIST_BIT (c1);
+                    continue;
+                  }
+
+                /* Could be the end of the bracket expression.  If it's
+                   not (i.e., when the bracket expression is `[]' so
+                   far), the ']' character bit gets set way below.  */
+                if (c == ']' && p != p1 + 1)
+                  break;
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character class.  */
+                if (had_char_class && c == '-' && *p != ']')
+                  return REG_ERANGE;
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character: if this is a hyphen not at the
+                   beginning or the end of a list, then it's the range
+                   operator.  */
+                if (c == '-' 
+                    && !(p - 2 >= pattern && p[-2] == '[') 
+                    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+                    && *p != ']')
+                  {
+                    reg_errcode_t ret
+                      = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) return ret;
+                  }
+
+                else if (p[0] == '-' && p[1] != ']')
+                  { /* This handles ranges made up of characters only.  */
+                    reg_errcode_t ret;
+
+                   /* Move past the `-'.  */
+                    PATFETCH (c1);
+                    
+                    ret = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) return ret;
+                  }
+
+                /* See if we're at the beginning of a possible character
+                   class.  */
+
+                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+                  { /* Leave room for the null.  */
+                    char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+                    PATFETCH (c);
+                    c1 = 0;
+
+                    /* If pattern is `[[:'.  */
+                    if (p == pend) return REG_EBRACK;
+
+                    for (;;)
+                      {
+                        PATFETCH (c);
+                        if (c == ':' || c == ']' || p == pend
+                            || c1 == CHAR_CLASS_MAX_LENGTH)
+                          break;
+                        str[c1++] = c;
+                      }
+                    str[c1] = '\0';
+
+                    /* If isn't a word bracketed by `[:' and:`]':
+                       undo the ending character, the letters, and leave 
+                       the leading `:' and `[' (but set bits for them).  */
+                    if (c == ':' && *p == ']')
+                      {
+                        int ch;
+                        boolean is_alnum = STREQ (str, "alnum");
+                        boolean is_alpha = STREQ (str, "alpha");
+                        boolean is_blank = STREQ (str, "blank");
+                        boolean is_cntrl = STREQ (str, "cntrl");
+                        boolean is_digit = STREQ (str, "digit");
+                        boolean is_graph = STREQ (str, "graph");
+                        boolean is_lower = STREQ (str, "lower");
+                        boolean is_print = STREQ (str, "print");
+                        boolean is_punct = STREQ (str, "punct");
+                        boolean is_space = STREQ (str, "space");
+                        boolean is_upper = STREQ (str, "upper");
+                        boolean is_xdigit = STREQ (str, "xdigit");
+                        
+                        if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+
+                        /* Throw away the ] at the end of the character
+                           class.  */
+                        PATFETCH (c);                                  
+
+                        if (p == pend) return REG_EBRACK;
+
+                        for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+                          {
+                            if (   (is_alnum  && ISALNUM (ch))
+                                || (is_alpha  && ISALPHA (ch))
+                                || (is_blank  && ISBLANK (ch))
+                                || (is_cntrl  && ISCNTRL (ch))
+                                || (is_digit  && ISDIGIT (ch))
+                                || (is_graph  && ISGRAPH (ch))
+                                || (is_lower  && ISLOWER (ch))
+                                || (is_print  && ISPRINT (ch))
+                                || (is_punct  && ISPUNCT (ch))
+                                || (is_space  && ISSPACE (ch))
+                                || (is_upper  && ISUPPER (ch))
+                                || (is_xdigit && ISXDIGIT (ch)))
+                            SET_LIST_BIT (ch);
+                          }
+                        had_char_class = true;
+                      }
+                    else
+                      {
+                        c1++;
+                        while (c1--)    
+                          PATUNFETCH;
+                        SET_LIST_BIT ('[');
+                        SET_LIST_BIT (':');
+                        had_char_class = false;
+                      }
+                  }
+                else
+                  {
+                    had_char_class = false;
+                    SET_LIST_BIT (c);
+                  }
+              }
+
+            /* Discard any (non)matching list bytes that are all 0 at the
+               end of the map.  Decrease the map-length byte too.  */
+            while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) 
+              b[-1]--; 
+            b += b[-1];
+          }
+          break;
+
+
+       case '(':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_open;
+          else
+            goto normal_char;
+
+
+        case ')':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_close;
+          else
+            goto normal_char;
+
+
+        case '\n':
+          if (syntax & RE_NEWLINE_ALT)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+       case '|':
+          if (syntax & RE_NO_BK_VBAR)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+        case '{':
+           if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+             goto handle_interval;
+           else
+             goto normal_char;
+
+
+        case '\\':
+          if (p == pend) return REG_EESCAPE;
+
+          /* Do not translate the character after the \, so that we can
+             distinguish, e.g., \B from \b, even if we normally would
+             translate, e.g., B to b.  */
+          PATFETCH_RAW (c);
+
+          switch (c)
+            {
+            case '(':
+              if (syntax & RE_NO_BK_PARENS)
+                goto normal_backslash;
+
+            handle_open:
+              bufp->re_nsub++;
+              regnum++;
+
+              if (COMPILE_STACK_FULL)
+                { 
+                  RETALLOC (compile_stack.stack, compile_stack.size << 1,
+                            compile_stack_elt_t);
+                  if (compile_stack.stack == NULL) return REG_ESPACE;
+
+                  compile_stack.size <<= 1;
+                }
+
+              /* These are the values to restore when we hit end of this
+                 group.  They are all relative offsets, so that if the
+                 whole pattern moves because of realloc, they will still
+                 be valid.  */
+              COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+              COMPILE_STACK_TOP.fixup_alt_jump 
+                = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+              COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+              COMPILE_STACK_TOP.regnum = regnum;
+
+              /* We will eventually replace the 0 with the number of
+                 groups inner to this one.  But do not push a
+                 start_memory for groups beyond the last one we can
+                 represent in the compiled pattern.  */
+              if (regnum <= MAX_REGNUM)
+                {
+                  COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+                  BUF_PUSH_3 (start_memory, regnum, 0);
+                }
+                
+              compile_stack.avail++;
+
+              fixup_alt_jump = 0;
+              laststart = 0;
+              begalt = b;
+             /* If we've reached MAX_REGNUM groups, then this open
+                won't actually generate any code, so we'll have to
+                clear pending_exact explicitly.  */
+             pending_exact = 0;
+              break;
+
+
+            case ')':
+              if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+              if (COMPILE_STACK_EMPTY)
+                if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                  goto normal_backslash;
+                else
+                  return REG_ERPAREN;
+
+            handle_close:
+              if (fixup_alt_jump)
+                { /* Push a dummy failure point at the end of the
+                     alternative for a possible future
+                     `pop_failure_jump' to pop.  See comments at
+                     `push_dummy_failure' in `re_match_2'.  */
+                  BUF_PUSH (push_dummy_failure);
+                  
+                  /* We allocated space for this jump when we assigned
+                     to `fixup_alt_jump', in the `handle_alt' case below.  */
+                  STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+                }
+
+              /* See similar code for backslashed left paren above.  */
+              if (COMPILE_STACK_EMPTY)
+                if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                  goto normal_char;
+                else
+                  return REG_ERPAREN;
+
+              /* Since we just checked for an empty stack above, this
+                 ``can't happen''.  */
+              assert (compile_stack.avail != 0);
+              {
+                /* We don't just want to restore into `regnum', because
+                   later groups should continue to be numbered higher,
+                   as in `(ab)c(de)' -- the second group is #2.  */
+                regnum_t this_group_regnum;
+
+                compile_stack.avail--;         
+                begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+                fixup_alt_jump
+                  = COMPILE_STACK_TOP.fixup_alt_jump
+                    ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 
+                    : 0;
+                laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+                this_group_regnum = COMPILE_STACK_TOP.regnum;
+               /* If we've reached MAX_REGNUM groups, then this open
+                  won't actually generate any code, so we'll have to
+                  clear pending_exact explicitly.  */
+               pending_exact = 0;
+
+                /* We're at the end of the group, so now we know how many
+                   groups were inside this one.  */
+                if (this_group_regnum <= MAX_REGNUM)
+                  {
+                    unsigned char *inner_group_loc
+                      = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+                    
+                    *inner_group_loc = regnum - this_group_regnum;
+                    BUF_PUSH_3 (stop_memory, this_group_regnum,
+                                regnum - this_group_regnum);
+                  }
+              }
+              break;
+
+
+            case '|':                                  /* `\|'.  */
+              if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+                goto normal_backslash;
+            handle_alt:
+              if (syntax & RE_LIMITED_OPS)
+                goto normal_char;
+
+              /* Insert before the previous alternative a jump which
+                 jumps to this alternative if the former fails.  */
+              GET_BUFFER_SPACE (3);
+              INSERT_JUMP (on_failure_jump, begalt, b + 6);
+              pending_exact = 0;
+              b += 3;
+
+              /* The alternative before this one has a jump after it
+                 which gets executed if it gets matched.  Adjust that
+                 jump so it will jump to this alternative's analogous
+                 jump (put in below, which in turn will jump to the next
+                 (if any) alternative's such jump, etc.).  The last such
+                 jump jumps to the correct final destination.  A picture:
+                          _____ _____ 
+                          |   | |   |   
+                          |   v |   v 
+                         a | b   | c   
+
+                 If we are at `b', then fixup_alt_jump right now points to a
+                 three-byte space after `a'.  We'll put in the jump, set
+                 fixup_alt_jump to right after `b', and leave behind three
+                 bytes which we'll fill in when we get to after `c'.  */
+
+              if (fixup_alt_jump)
+                STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+              /* Mark and leave space for a jump after this alternative,
+                 to be filled in later either by next alternative or
+                 when know we're at the end of a series of alternatives.  */
+              fixup_alt_jump = b;
+              GET_BUFFER_SPACE (3);
+              b += 3;
+
+              laststart = 0;
+              begalt = b;
+              break;
+
+
+            case '{': 
+              /* If \{ is a literal.  */
+              if (!(syntax & RE_INTERVALS)
+                     /* If we're at `\{' and it's not the open-interval 
+                        operator.  */
+                  || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+                  || (p - 2 == pattern  &&  p == pend))
+                goto normal_backslash;
+
+            handle_interval:
+              {
+                /* If got here, then the syntax allows intervals.  */
+
+                /* At least (most) this many matches must be made.  */
+                int lower_bound = -1, upper_bound = -1;
+
+                beg_interval = p - 1;
+
+                if (p == pend)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else
+                      return REG_EBRACE;
+                  }
+
+                GET_UNSIGNED_NUMBER (lower_bound);
+
+                if (c == ',')
+                  {
+                    GET_UNSIGNED_NUMBER (upper_bound);
+                    if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+                  }
+                else
+                  /* Interval such as `{1}' => match exactly once. */
+                  upper_bound = lower_bound;
+
+                if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+                    || lower_bound > upper_bound)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      return REG_BADBR;
+                  }
+
+                if (!(syntax & RE_NO_BK_BRACES)) 
+                  {
+                    if (c != '\\') return REG_EBRACE;
+
+                    PATFETCH (c);
+                  }
+
+                if (c != '}')
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      return REG_BADBR;
+                  }
+
+                /* We just parsed a valid interval.  */
+
+                /* If it's invalid to have no preceding re.  */
+                if (!laststart)
+                  {
+                    if (syntax & RE_CONTEXT_INVALID_OPS)
+                      return REG_BADRPT;
+                    else if (syntax & RE_CONTEXT_INDEP_OPS)
+                      laststart = b;
+                    else
+                      goto unfetch_interval;
+                  }
+
+                /* If the upper bound is zero, don't want to succeed at
+                   all; jump from `laststart' to `b + 3', which will be
+                   the end of the buffer after we insert the jump.  */
+                 if (upper_bound == 0)
+                   {
+                     GET_BUFFER_SPACE (3);
+                     INSERT_JUMP (jump, laststart, b + 3);
+                     b += 3;
+                   }
+
+                 /* Otherwise, we have a nontrivial interval.  When
+                    we're all done, the pattern will look like:
+                      set_number_at <jump count> <upper bound>
+                      set_number_at <succeed_n count> <lower bound>
+                      succeed_n <after jump addr> <succed_n count>
+                      <body of loop>
+                      jump_n <succeed_n addr> <jump count>
+                    (The upper bound and `jump_n' are omitted if
+                    `upper_bound' is 1, though.)  */
+                 else 
+                   { /* If the upper bound is > 1, we need to insert
+                        more at the end of the loop.  */
+                     unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+                     GET_BUFFER_SPACE (nbytes);
+
+                     /* Initialize lower bound of the `succeed_n', even
+                        though it will be set during matching by its
+                        attendant `set_number_at' (inserted next),
+                        because `re_compile_fastmap' needs to know.
+                        Jump to the `jump_n' we might insert below.  */
+                     INSERT_JUMP2 (succeed_n, laststart,
+                                   b + 5 + (upper_bound > 1) * 5,
+                                   lower_bound);
+                     b += 5;
+
+                     /* Code to initialize the lower bound.  Insert 
+                        before the `succeed_n'.  The `5' is the last two
+                        bytes of this `set_number_at', plus 3 bytes of
+                        the following `succeed_n'.  */
+                     insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+                     b += 5;
+
+                     if (upper_bound > 1)
+                       { /* More than one repetition is allowed, so
+                            append a backward jump to the `succeed_n'
+                            that starts this interval.
+                            
+                            When we've reached this during matching,
+                            we'll have matched the interval once, so
+                            jump back only `upper_bound - 1' times.  */
+                         STORE_JUMP2 (jump_n, b, laststart + 5,
+                                      upper_bound - 1);
+                         b += 5;
+
+                         /* The location we want to set is the second
+                            parameter of the `jump_n'; that is `b-2' as
+                            an absolute address.  `laststart' will be
+                            the `set_number_at' we're about to insert;
+                            `laststart+3' the number to set, the source
+                            for the relative address.  But we are
+                            inserting into the middle of the pattern --
+                            so everything is getting moved up by 5.
+                            Conclusion: (b - 2) - (laststart + 3) + 5,
+                            i.e., b - laststart.
+                            
+                            We insert this at the beginning of the loop
+                            so that if we fail during matching, we'll
+                            reinitialize the bounds.  */
+                         insert_op2 (set_number_at, laststart, b - laststart,
+                                     upper_bound - 1, b);
+                         b += 5;
+                       }
+                   }
+                pending_exact = 0;
+                beg_interval = NULL;
+              }
+              break;
+
+            unfetch_interval:
+              /* If an invalid interval, match the characters as literals.  */
+               assert (beg_interval);
+               p = beg_interval;
+               beg_interval = NULL;
+
+               /* normal_char and normal_backslash need `c'.  */
+               PATFETCH (c);   
+
+               if (!(syntax & RE_NO_BK_BRACES))
+                 {
+                   if (p > pattern  &&  p[-1] == '\\')
+                     goto normal_backslash;
+                 }
+               goto normal_char;
+
+#ifdef emacs
+            /* There is no way to specify the before_dot and after_dot
+               operators.  rms says this is ok.  --karl  */
+            case '=':
+              BUF_PUSH (at_dot);
+              break;
+
+            case 's':  
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+              break;
+
+            case 'S':
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+              break;
+#endif /* emacs */
+
+
+            case 'w':
+              laststart = b;
+              BUF_PUSH (wordchar);
+              break;
+
+
+            case 'W':
+              laststart = b;
+              BUF_PUSH (notwordchar);
+              break;
+
+
+            case '<':
+              BUF_PUSH (wordbeg);
+              break;
+
+            case '>':
+              BUF_PUSH (wordend);
+              break;
+
+            case 'b':
+              BUF_PUSH (wordbound);
+              break;
+
+            case 'B':
+              BUF_PUSH (notwordbound);
+              break;
+
+            case '`':
+              BUF_PUSH (begbuf);
+              break;
+
+            case '\'':
+              BUF_PUSH (endbuf);
+              break;
+
+            case '1': case '2': case '3': case '4': case '5':
+            case '6': case '7': case '8': case '9':
+              if (syntax & RE_NO_BK_REFS)
+                goto normal_char;
+
+              c1 = c - '0';
+
+              if (c1 > regnum)
+                return REG_ESUBREG;
+
+              /* Can't back reference to a subexpression if inside of it.  */
+              if (group_in_compile_stack (compile_stack, c1))
+                goto normal_char;
+
+              laststart = b;
+              BUF_PUSH_2 (duplicate, c1);
+              break;
+
+
+            case '+':
+            case '?':
+              if (syntax & RE_BK_PLUS_QM)
+                goto handle_plus;
+              else
+                goto normal_backslash;
+
+            default:
+            normal_backslash:
+              /* You might think it would be useful for \ to mean
+                 not to translate; but if we don't translate it
+                 it will never match anything.  */
+              c = TRANSLATE (c);
+              goto normal_char;
+            }
+          break;
+
+
+       default:
+        /* Expects the character in `c'.  */
+       normal_char:
+             /* If no exactn currently being built.  */
+          if (!pending_exact 
+
+              /* If last exactn not at current position.  */
+              || pending_exact + *pending_exact + 1 != b
+              
+              /* We have only one byte following the exactn for the count.  */
+             || *pending_exact == (1 << BYTEWIDTH) - 1
+
+              /* If followed by a repetition operator.  */
+              || *p == '*' || *p == '^'
+             || ((syntax & RE_BK_PLUS_QM)
+                 ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+                 : (*p == '+' || *p == '?'))
+             || ((syntax & RE_INTERVALS)
+                  && ((syntax & RE_NO_BK_BRACES)
+                     ? *p == '{'
+                      : (p[0] == '\\' && p[1] == '{'))))
+           {
+             /* Start building a new exactn.  */
+              
+              laststart = b;
+
+             BUF_PUSH_2 (exactn, 0);
+             pending_exact = b - 1;
+            }
+            
+         BUF_PUSH (c);
+          (*pending_exact)++;
+         break;
+        } /* switch (c) */
+    } /* while p != pend */
+
+  
+  /* Through the pattern now.  */
+  
+  if (fixup_alt_jump)
+    STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+  if (!COMPILE_STACK_EMPTY) 
+    return REG_EPAREN;
+
+  free (compile_stack.stack);
+
+  /* We have succeeded; set the length of the buffer.  */
+  bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+  if (debug)
+    {
+      DEBUG_PRINT1 ("\nCompiled pattern: \n");
+      print_compiled_pattern (bufp);
+    }
+#endif /* DEBUG */
+
+#ifndef MATCH_MAY_ALLOCATE
+  /* Initialize the failure stack to the largest possible stack.  This
+     isn't necessary unless we're trying to avoid calling alloca in
+     the search and match routines.  */
+  {
+    int num_regs = bufp->re_nsub + 1;
+
+    /* Since DOUBLE_FAIL_STACK refuses to double only if the current size
+       is strictly greater than re_max_failures, the largest possible stack
+       is 2 * re_max_failures failure points.  */
+    fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS);
+    if (fail_stack.stack)
+      fail_stack.stack =
+       (fail_stack_elt_t *) realloc (fail_stack.stack,
+                                     (fail_stack.size
+                                      * sizeof (fail_stack_elt_t)));
+    else
+      fail_stack.stack =
+       (fail_stack_elt_t *) malloc (fail_stack.size 
+                                    * sizeof (fail_stack_elt_t));
+
+    /* Initialize some other variables the matcher uses.  */
+    RETALLOC_IF (regstart,      num_regs, const char *);
+    RETALLOC_IF (regend,        num_regs, const char *);
+    RETALLOC_IF (old_regstart,  num_regs, const char *);
+    RETALLOC_IF (old_regend,    num_regs, const char *);
+    RETALLOC_IF (best_regstart,  num_regs, const char *);
+    RETALLOC_IF (best_regend,   num_regs, const char *);
+    RETALLOC_IF (reg_info,      num_regs, register_info_type);
+    RETALLOC_IF (reg_dummy,     num_regs, const char *);
+    RETALLOC_IF (reg_info_dummy, num_regs, register_info_type);
+  }
+#endif
+
+  return REG_NOERROR;
+} /* regex_compile */
+\f
+/* Subroutines for `regex_compile'.  */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG.  */
+
+static void
+store_op1 (op, loc, arg)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg1);
+  STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+   for OP followed by two-byte integer parameter ARG.  */
+
+static void
+insert_op1 (op, loc, arg, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+    unsigned char *end;    
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 3;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+    
+  store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+    unsigned char *end;    
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 5;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+    
+  store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
+   after an alternative or a begin-subexpression.  We assume there is at
+   least one character before the ^.  */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+    const char *pattern, *p;
+    reg_syntax_t syntax;
+{
+  const char *prev = p - 2;
+  boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+  
+  return
+       /* After a subexpression?  */
+       (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+       /* After an alternative?  */
+    || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
+   at least one character after the $, i.e., `P < PEND'.  */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+    const char *p, *pend;
+    int syntax;
+{
+  const char *next = p;
+  boolean next_backslash = *next == '\\';
+  const char *next_next = p + 1 < pend ? p + 1 : NULL;
+  
+  return
+       /* Before a subexpression?  */
+       (syntax & RE_NO_BK_PARENS ? *next == ')'
+        : next_backslash && next_next && *next_next == ')')
+       /* Before an alternative?  */
+    || (syntax & RE_NO_BK_VBAR ? *next == '|'
+        : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and 
+   false if it's not.  */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+    compile_stack_type compile_stack;
+    regnum_t regnum;
+{
+  int this_element;
+
+  for (this_element = compile_stack.avail - 1;  
+       this_element >= 0; 
+       this_element--)
+    if (compile_stack.stack[this_element].regnum == regnum)
+      return true;
+
+  return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
+   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
+   Then we set the translation of all bits between the starting and
+   ending characters (inclusive) in the compiled pattern B.
+   
+   Return an error code.
+   
+   We use these short variable names so we can use the same macros as
+   `regex_compile' itself.  */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+    const char **p_ptr, *pend;
+    char *translate;
+    reg_syntax_t syntax;
+    unsigned char *b;
+{
+  unsigned this_char;
+
+  const char *p = *p_ptr;
+  int range_start, range_end;
+  
+  if (p == pend)
+    return REG_ERANGE;
+
+  /* Even though the pattern is a signed `char *', we need to fetch
+     with unsigned char *'s; if the high bit of the pattern character
+     is set, the range endpoints will be negative if we fetch using a
+     signed char *.
+
+     We also want to fetch the endpoints without translating them; the 
+     appropriate translation is done in the bit-setting loop below.  */
+  range_start = ((unsigned char *) p)[-2];
+  range_end   = ((unsigned char *) p)[0];
+
+  /* Have to increment the pointer into the pattern string, so the
+     caller isn't still at the ending character.  */
+  (*p_ptr)++;
+
+  /* If the start is after the end, the range is empty.  */
+  if (range_start > range_end)
+    return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+  /* Here we see why `this_char' has to be larger than an `unsigned
+     char' -- the range is inclusive, so if `range_end' == 0xff
+     (assuming 8-bit characters), we would otherwise go into an infinite
+     loop, since all characters <= 0xff.  */
+  for (this_char = range_start; this_char <= range_end; this_char++)
+    {
+      SET_LIST_BIT (TRANSLATE (this_char));
+    }
+  
+  return REG_NOERROR;
+}
+\f
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
+   characters can start a string that matches the pattern.  This fastmap
+   is used by re_search to skip quickly over impossible starting points.
+
+   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+   area as BUFP->fastmap.
+   
+   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+   the pattern buffer.
+
+   Returns 0 if we succeed, -2 if an internal error.   */
+
+int
+re_compile_fastmap (bufp)
+     struct re_pattern_buffer *bufp;
+{
+  int j, k;
+#ifdef MATCH_MAY_ALLOCATE
+  fail_stack_type fail_stack;
+#endif
+#ifndef REGEX_MALLOC
+  char *destination;
+#endif
+  /* We don't push any register information onto the failure stack.  */
+  unsigned num_regs = 0;
+  
+  register char *fastmap = bufp->fastmap;
+  unsigned char *pattern = bufp->buffer;
+  unsigned long size = bufp->used;
+  const unsigned char *p = pattern;
+  register unsigned char *pend = pattern + size;
+
+  /* Assume that each path through the pattern can be null until
+     proven otherwise.  We set this false at the bottom of switch
+     statement, to which we get only if a particular path doesn't
+     match the empty string.  */
+  boolean path_can_be_null = true;
+
+  /* We aren't doing a `succeed_n' to begin with.  */
+  boolean succeed_n_p = false;
+
+  assert (fastmap != NULL && p != NULL);
+  
+  INIT_FAIL_STACK ();
+  bzero (fastmap, 1 << BYTEWIDTH);  /* Assume nothing's valid.  */
+  bufp->fastmap_accurate = 1;      /* It will be when we're done.  */
+  bufp->can_be_null = 0;
+      
+  while (p != pend || !FAIL_STACK_EMPTY ())
+    {
+      if (p == pend)
+        {
+          bufp->can_be_null |= path_can_be_null;
+          
+          /* Reset for next path.  */
+          path_can_be_null = true;
+          
+          p = fail_stack.stack[--fail_stack.avail];
+       }
+
+      /* We should never be about to go beyond the end of the pattern.  */
+      assert (p < pend);
+      
+#ifdef SWITCH_ENUM_BUG
+      switch ((int) ((re_opcode_t) *p++))
+#else
+      switch ((re_opcode_t) *p++)
+#endif
+       {
+
+        /* I guess the idea here is to simply not bother with a fastmap
+           if a backreference is used, since it's too hard to figure out
+           the fastmap for the corresponding group.  Setting
+           `can_be_null' stops `re_search_2' from using the fastmap, so
+           that is all we do.  */
+       case duplicate:
+         bufp->can_be_null = 1;
+          return 0;
+
+
+      /* Following are the cases which match a character.  These end
+         with `break'.  */
+
+       case exactn:
+          fastmap[p[1]] = 1;
+         break;
+
+
+        case charset:
+          for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+           if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+              fastmap[j] = 1;
+         break;
+
+
+       case charset_not:
+         /* Chars beyond end of map must be allowed.  */
+         for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+            fastmap[j] = 1;
+
+         for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+           if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+              fastmap[j] = 1;
+          break;
+
+
+       case wordchar:
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) == Sword)
+             fastmap[j] = 1;
+         break;
+
+
+       case notwordchar:
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) != Sword)
+             fastmap[j] = 1;
+         break;
+
+
+        case anychar:
+          /* `.' matches anything ...  */
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+            fastmap[j] = 1;
+
+          /* ... except perhaps newline.  */
+          if (!(bufp->syntax & RE_DOT_NEWLINE))
+            fastmap['\n'] = 0;
+
+          /* Return if we have already set `can_be_null'; if we have,
+             then the fastmap is irrelevant.  Something's wrong here.  */
+         else if (bufp->can_be_null)
+           return 0;
+
+          /* Otherwise, have to check alternative paths.  */
+         break;
+
+
+#ifdef emacs
+        case syntaxspec:
+         k = *p++;
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) == (enum syntaxcode) k)
+             fastmap[j] = 1;
+         break;
+
+
+       case notsyntaxspec:
+         k = *p++;
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) != (enum syntaxcode) k)
+             fastmap[j] = 1;
+         break;
+
+
+      /* All cases after this match the empty string.  These end with
+         `continue'.  */
+
+
+       case before_dot:
+       case at_dot:
+       case after_dot:
+          continue;
+#endif /* not emacs */
+
+
+        case no_op:
+        case begline:
+        case endline:
+       case begbuf:
+       case endbuf:
+       case wordbound:
+       case notwordbound:
+       case wordbeg:
+       case wordend:
+        case push_dummy_failure:
+          continue;
+
+
+       case jump_n:
+        case pop_failure_jump:
+       case maybe_pop_jump:
+       case jump:
+        case jump_past_alt:
+       case dummy_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+         p += j;       
+         if (j > 0)
+           continue;
+            
+          /* Jump backward implies we just went through the body of a
+             loop and matched nothing.  Opcode jumped to should be
+             `on_failure_jump' or `succeed_n'.  Just treat it like an
+             ordinary jump.  For a * loop, it has pushed its failure
+             point already; if so, discard that as redundant.  */
+          if ((re_opcode_t) *p != on_failure_jump
+             && (re_opcode_t) *p != succeed_n)
+           continue;
+
+          p++;
+          EXTRACT_NUMBER_AND_INCR (j, p);
+          p += j;              
+         
+          /* If what's on the stack is where we are now, pop it.  */
+          if (!FAIL_STACK_EMPTY () 
+             && fail_stack.stack[fail_stack.avail - 1] == p)
+            fail_stack.avail--;
+
+          continue;
+
+
+        case on_failure_jump:
+        case on_failure_keep_string_jump:
+       handle_on_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+
+          /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+             end of the pattern.  We don't want to push such a point,
+             since when we restore it above, entering the switch will
+             increment `p' past the end of the pattern.  We don't need
+             to push such a point since we obviously won't find any more
+             fastmap entries beyond `pend'.  Such a pattern can match
+             the null string, though.  */
+          if (p + j < pend)
+            {
+              if (!PUSH_PATTERN_OP (p + j, fail_stack))
+                return -2;
+            }
+          else
+            bufp->can_be_null = 1;
+
+          if (succeed_n_p)
+            {
+              EXTRACT_NUMBER_AND_INCR (k, p);  /* Skip the n.  */
+              succeed_n_p = false;
+           }
+
+          continue;
+
+
+       case succeed_n:
+          /* Get to the number of times to succeed.  */
+          p += 2;              
+
+          /* Increment p past the n for when k != 0.  */
+          EXTRACT_NUMBER_AND_INCR (k, p);
+          if (k == 0)
+           {
+              p -= 4;
+             succeed_n_p = true;  /* Spaghetti code alert.  */
+              goto handle_on_failure_jump;
+            }
+          continue;
+
+
+       case set_number_at:
+          p += 4;
+          continue;
+
+
+       case start_memory:
+        case stop_memory:
+         p += 2;
+         continue;
+
+
+       default:
+          abort (); /* We have listed all the cases.  */
+        } /* switch *p++ */
+
+      /* Getting here means we have found the possible starting
+         characters for one path of the pattern -- and that the empty
+         string does not match.  We need not follow this path further.
+         Instead, look at the next alternative (remembered on the
+         stack), or quit if no more.  The test at the top of the loop
+         does these things.  */
+      path_can_be_null = false;
+      p = pend;
+    } /* while p */
+
+  /* Set `can_be_null' for the last path (also the first path, if the
+     pattern is empty).  */
+  bufp->can_be_null |= path_can_be_null;
+  return 0;
+} /* re_compile_fastmap */
+\f
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+    struct re_pattern_buffer *bufp;
+    struct re_registers *regs;
+    unsigned num_regs;
+    regoff_t *starts, *ends;
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t) 0;
+    }
+}
+\f
+/* Searching routines.  */
+
+/* Like re_search_2, below, but only one string is specified, and
+   doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, startpos, range;
+     struct re_registers *regs;
+{
+  return re_search_2 (bufp, NULL, 0, string, size, startpos, range, 
+                     regs, size);
+}
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+   virtual concatenation of STRING1 and STRING2, starting first at index
+   STARTPOS, then at STARTPOS + 1, and so on.
+   
+   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+   
+   RANGE is how far to scan while trying to match.  RANGE = 0 means try
+   only at STARTPOS; in general, the last start tried is STARTPOS +
+   RANGE.
+   
+   In REGS, return the indices of the virtual concatenation of STRING1
+   and STRING2 that matched the entire BUFP->buffer and its contained
+   subexpressions.
+   
+   Do not consider matching one past the index STOP in the virtual
+   concatenation of STRING1 and STRING2.
+
+   We return either the position in the strings at which the match was
+   found, -1 if no match, or -2 if error (such as failure
+   stack overflow).  */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int startpos;
+     int range;
+     struct re_registers *regs;
+     int stop;
+{
+  int val;
+  register char *fastmap = bufp->fastmap;
+  register char *translate = bufp->translate;
+  int total_size = size1 + size2;
+  int endpos = startpos + range;
+
+  /* Check for out-of-range STARTPOS.  */
+  if (startpos < 0 || startpos > total_size)
+    return -1;
+    
+  /* Fix up RANGE if it might eventually take us outside
+     the virtual concatenation of STRING1 and STRING2.  */
+  if (endpos < -1)
+    range = -1 - startpos;
+  else if (endpos > total_size)
+    range = total_size - startpos;
+
+  /* If the search isn't to be a backwards one, don't waste time in a
+     search for a pattern that must be anchored.  */
+  if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
+    {
+      if (startpos > 0)
+       return -1;
+      else
+       range = 1;
+    }
+
+  /* Update the fastmap now if not correct already.  */
+  if (fastmap && !bufp->fastmap_accurate)
+    if (re_compile_fastmap (bufp) == -2)
+      return -2;
+  
+  /* Loop through the string, looking for a place to start matching.  */
+  for (;;)
+    { 
+      /* If a fastmap is supplied, skip quickly over characters that
+         cannot be the start of a match.  If the pattern can match the
+         null string, however, we don't need to skip characters; we want
+         the first null string.  */
+      if (fastmap && startpos < total_size && !bufp->can_be_null)
+       {
+         if (range > 0)        /* Searching forwards.  */
+           {
+             register const char *d;
+             register int lim = 0;
+             int irange = range;
+
+              if (startpos < size1 && startpos + range >= size1)
+                lim = range - (size1 - startpos);
+
+             d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+   
+              /* Written out as an if-else to avoid testing `translate'
+                 inside the loop.  */
+             if (translate)
+                while (range > lim
+                       && !fastmap[(unsigned char)
+                                  translate[(unsigned char) *d++]])
+                  range--;
+             else
+                while (range > lim && !fastmap[(unsigned char) *d++])
+                  range--;
+
+             startpos += irange - range;
+           }
+         else                          /* Searching backwards.  */
+           {
+             register char c = (size1 == 0 || startpos >= size1
+                                 ? string2[startpos - size1] 
+                                 : string1[startpos]);
+
+             if (!fastmap[(unsigned char) TRANSLATE (c)])
+               goto advance;
+           }
+       }
+
+      /* If can't match the null string, and that's all we have left, fail.  */
+      if (range >= 0 && startpos == total_size && fastmap
+          && !bufp->can_be_null)
+       return -1;
+
+      val = re_match_2 (bufp, string1, size1, string2, size2,
+                       startpos, regs, stop);
+      if (val >= 0)
+       return startpos;
+        
+      if (val == -2)
+       return -2;
+
+    advance:
+      if (!range) 
+        break;
+      else if (range > 0) 
+        {
+          range--; 
+          startpos++;
+        }
+      else
+        {
+          range++; 
+          startpos--;
+        }
+    }
+  return -1;
+} /* re_search_2 */
+\f
+/* Declarations and macros for re_match_2.  */
+
+static int bcmp_translate ();
+static boolean alt_match_null_string_p (),
+               common_op_match_null_string_p (),
+               group_match_null_string_p ();
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+   and `string2' into an offset from the beginning of that string.  */
+#define POINTER_TO_OFFSET(ptr)                                         \
+  (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
+
+/* Macros for dealing with the split strings in re_match_2.  */
+
+#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)
+
+/* Call before fetching a character with *d.  This switches over to
+   string2 if necessary.  */
+#define PREFETCH()                                                     \
+  while (d == dend)                                                    \
+    {                                                                  \
+      /* End of string2 => fail.  */                                   \
+      if (dend == end_match_2)                                                 \
+        goto fail;                                                     \
+      /* End of string1 => advance to string2.  */                     \
+      d = string2;                                                     \
+      dend = end_match_2;                                              \
+    }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+   of `string1' and `string2'.  If only one string, it's `string2'.  */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)        
+
+
+/* Test if D points to a character which is word-constituent.  We have
+   two special cases to check for: if past the end of string1, look at
+   the first character in string2; and if before the beginning of
+   string2, look at the last character in string1.  */
+#define WORDCHAR_P(d)                                                  \
+  (SYNTAX ((d) == end1 ? *string2                                      \
+           : (d) == string2 - 1 ? *(end1 - 1) : *(d))                  \
+   == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+   to being word-constituent.  */
+#define AT_WORD_BOUNDARY(d)                                            \
+  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)                            \
+   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+
+
+/* Free everything we malloc.  */
+#ifdef MATCH_MAY_ALLOCATE
+#ifdef REGEX_MALLOC
+#define FREE_VAR(var) if (var) free (var); var = NULL
+#define FREE_VARIABLES()                                               \
+  do {                                                                 \
+    FREE_VAR (fail_stack.stack);                                       \
+    FREE_VAR (regstart);                                               \
+    FREE_VAR (regend);                                                 \
+    FREE_VAR (old_regstart);                                           \
+    FREE_VAR (old_regend);                                             \
+    FREE_VAR (best_regstart);                                          \
+    FREE_VAR (best_regend);                                            \
+    FREE_VAR (reg_info);                                               \
+    FREE_VAR (reg_dummy);                                              \
+    FREE_VAR (reg_info_dummy);                                         \
+  } while (0)
+#else /* not REGEX_MALLOC */
+/* Some MIPS systems (at least) want this to free alloca'd storage.  */
+#define FREE_VARIABLES() alloca (0)
+#endif /* not REGEX_MALLOC */
+#else
+#define FREE_VARIABLES() /* Do nothing!  */
+#endif /* not MATCH_MAY_ALLOCATE */
+
+/* These values must meet several constraints.  They must not be valid
+   register values; since we have a limit of 255 registers (because
+   we use only one byte in the pattern for the register number), we can
+   use numbers larger than 255.  They must differ by 1, because of
+   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
+   be larger than the value for the highest register, so we do not try
+   to actually save any registers when none are active.  */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+\f
+/* Matching routines.  */
+
+#ifndef emacs   /* Emacs never uses this.  */
+/* re_match is like re_match_2 except it takes only a single string.  */
+
+int
+re_match (bufp, string, size, pos, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, pos;
+     struct re_registers *regs;
+ {
+  return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size); 
+}
+#endif /* not emacs */
+
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+   and SIZE2, respectively).  We start matching at POS, and stop
+   matching at STOP.
+   
+   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+   store offsets for the substring each group matched in REGS.  See the
+   documentation for exactly how many groups we fill.
+
+   We return -1 if no match, -2 if an internal error (such as the
+   failure stack overflowing).  Otherwise, we return the length of the
+   matched substring.  */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int pos;
+     struct re_registers *regs;
+     int stop;
+{
+  /* General temporaries.  */
+  int mcnt;
+  unsigned char *p1;
+
+  /* Just past the end of the corresponding string.  */
+  const char *end1, *end2;
+
+  /* Pointers into string1 and string2, just past the last characters in
+     each to consider matching.  */
+  const char *end_match_1, *end_match_2;
+
+  /* Where we are in the data, and the end of the current string.  */
+  const char *d, *dend;
+  
+  /* Where we are in the pattern, and the end of the pattern.  */
+  unsigned char *p = bufp->buffer;
+  register unsigned char *pend = p + bufp->used;
+
+  /* We use this to map every character in the string.  */
+  char *translate = bufp->translate;
+
+  /* Failure point stack.  Each place that can handle a failure further
+     down the line pushes a failure point on this stack.  It consists of
+     restart, regend, and reg_info for all registers corresponding to
+     the subexpressions we're currently inside, plus the number of such
+     registers, and, finally, two char *'s.  The first char * is where
+     to resume scanning the pattern; the second one is where to resume
+     scanning the strings.  If the latter is zero, the failure point is
+     a ``dummy''; if a failure happens and the failure point is a dummy,
+     it gets discarded and the next next one is tried.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
+  fail_stack_type fail_stack;
+#endif
+#ifdef DEBUG
+  static unsigned failure_id = 0;
+  unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+  /* We fill all the registers internally, independent of what we
+     return, for use in backreferences.  The number here includes
+     an element for register zero.  */
+  unsigned num_regs = bufp->re_nsub + 1;
+  
+  /* The currently active registers.  */
+  unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+  unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+  /* Information on the contents of registers. These are pointers into
+     the input strings; they record just what was matched (on this
+     attempt) by a subexpression part of the pattern, that is, the
+     regnum-th regstart pointer points to where in the pattern we began
+     matching and the regnum-th regend points to right after where we
+     stopped matching the regnum-th subexpression.  (The zeroth register
+     keeps track of what the whole pattern matches.)  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **regstart, **regend;
+#endif
+
+  /* If a group that's operated upon by a repetition operator fails to
+     match anything, then the register for its start will need to be
+     restored because it will have been set to wherever in the string we
+     are when we last see its open-group operator.  Similarly for a
+     register's end.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **old_regstart, **old_regend;
+#endif
+
+  /* The is_active field of reg_info helps us keep track of which (possibly
+     nested) subexpressions we are currently in. The matched_something
+     field of reg_info[reg_num] helps us tell whether or not we have
+     matched any of the pattern so far this time through the reg_num-th
+     subexpression.  These two fields get reset each time through any
+     loop their register is in.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
+  register_info_type *reg_info; 
+#endif
+
+  /* The following record the register info as found in the above
+     variables when we find a match better than any we've seen before. 
+     This happens as we backtrack through the failure points, which in
+     turn happens only if we have not yet matched the entire string. */
+  unsigned best_regs_set = false;
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **best_regstart, **best_regend;
+#endif
+  
+  /* Logically, this is `best_regend[0]'.  But we don't want to have to
+     allocate space for that if we're not allocating space for anything
+     else (see below).  Also, we never need info about register 0 for
+     any of the other register vectors, and it seems rather a kludge to
+     treat `best_regend' differently than the rest.  So we keep track of
+     the end of the best match so far in a separate variable.  We
+     initialize this to NULL so that when we backtrack the first time
+     and need to test it, it's not garbage.  */
+  const char *match_end = NULL;
+
+  /* Used when we pop values we don't care about.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **reg_dummy;
+  register_info_type *reg_info_dummy;
+#endif
+
+#ifdef DEBUG
+  /* Counts the total number of registers pushed.  */
+  unsigned num_regs_pushed = 0;        
+#endif
+
+  DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+  
+  INIT_FAIL_STACK ();
+  
+#ifdef MATCH_MAY_ALLOCATE
+  /* Do not bother to initialize all the register variables if there are
+     no groups in the pattern, as it takes a fair amount of time.  If
+     there are groups, we include space for register 0 (the whole
+     pattern), even though we never use it, since it simplifies the
+     array indexing.  We should fix this.  */
+  if (bufp->re_nsub)
+    {
+      regstart = REGEX_TALLOC (num_regs, const char *);
+      regend = REGEX_TALLOC (num_regs, const char *);
+      old_regstart = REGEX_TALLOC (num_regs, const char *);
+      old_regend = REGEX_TALLOC (num_regs, const char *);
+      best_regstart = REGEX_TALLOC (num_regs, const char *);
+      best_regend = REGEX_TALLOC (num_regs, const char *);
+      reg_info = REGEX_TALLOC (num_regs, register_info_type);
+      reg_dummy = REGEX_TALLOC (num_regs, const char *);
+      reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+      if (!(regstart && regend && old_regstart && old_regend && reg_info 
+            && best_regstart && best_regend && reg_dummy && reg_info_dummy)) 
+        {
+          FREE_VARIABLES ();
+          return -2;
+        }
+    }
+#if defined (REGEX_MALLOC)
+  else
+    {
+      /* We must initialize all our variables to NULL, so that
+         `FREE_VARIABLES' doesn't try to free them.  */
+      regstart = regend = old_regstart = old_regend = best_regstart
+        = best_regend = reg_dummy = NULL;
+      reg_info = reg_info_dummy = (register_info_type *) NULL;
+    }
+#endif /* REGEX_MALLOC */
+#endif /* MATCH_MAY_ALLOCATE */
+
+  /* The starting position is bogus.  */
+  if (pos < 0 || pos > size1 + size2)
+    {
+      FREE_VARIABLES ();
+      return -1;
+    }
+    
+  /* Initialize subexpression text positions to -1 to mark ones that no
+     start_memory/stop_memory has been seen for. Also initialize the
+     register information struct.  */
+  for (mcnt = 1; mcnt < num_regs; mcnt++)
+    {
+      regstart[mcnt] = regend[mcnt] 
+        = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+        
+      REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+      IS_ACTIVE (reg_info[mcnt]) = 0;
+      MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+      EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+    }
+  
+  /* We move `string1' into `string2' if the latter's empty -- but not if
+     `string1' is null.  */
+  if (size2 == 0 && string1 != NULL)
+    {
+      string2 = string1;
+      size2 = size1;
+      string1 = 0;
+      size1 = 0;
+    }
+  end1 = string1 + size1;
+  end2 = string2 + size2;
+
+  /* Compute where to stop matching, within the two strings.  */
+  if (stop <= size1)
+    {
+      end_match_1 = string1 + stop;
+      end_match_2 = string2;
+    }
+  else
+    {
+      end_match_1 = end1;
+      end_match_2 = string2 + stop - size1;
+    }
+
+  /* `p' scans through the pattern as `d' scans through the data. 
+     `dend' is the end of the input string that `d' points within.  `d'
+     is advanced into the following input string whenever necessary, but
+     this happens before fetching; therefore, at the beginning of the
+     loop, `d' can be pointing at the end of a string, but it cannot
+     equal `string2'.  */
+  if (size1 > 0 && pos <= size1)
+    {
+      d = string1 + pos;
+      dend = end_match_1;
+    }
+  else
+    {
+      d = string2 + pos - size1;
+      dend = end_match_2;
+    }
+
+  DEBUG_PRINT1 ("The compiled pattern is: ");
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+  DEBUG_PRINT1 ("The string to match is: `");
+  DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+  DEBUG_PRINT1 ("'\n");
+  
+  /* This loops over pattern commands.  It exits by returning from the
+     function if the match is complete, or it drops through if the match
+     fails at this starting point in the input data.  */
+  for (;;)
+    {
+      DEBUG_PRINT2 ("\n0x%x: ", p);
+
+      if (p == pend)
+       { /* End of pattern means we might have succeeded.  */
+          DEBUG_PRINT1 ("end of pattern ... ");
+          
+         /* If we haven't matched the entire string, and we want the
+             longest match, try backtracking.  */
+          if (d != end_match_2)
+           {
+              DEBUG_PRINT1 ("backtracking.\n");
+              
+              if (!FAIL_STACK_EMPTY ())
+                { /* More failure points to try.  */
+                  boolean same_str_p = (FIRST_STRING_P (match_end) 
+                                       == MATCHING_IN_FIRST_STRING);
+
+                  /* If exceeds best match so far, save it.  */
+                  if (!best_regs_set
+                      || (same_str_p && d > match_end)
+                      || (!same_str_p && !MATCHING_IN_FIRST_STRING))
+                    {
+                      best_regs_set = true;
+                      match_end = d;
+                      
+                      DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+                      
+                      for (mcnt = 1; mcnt < num_regs; mcnt++)
+                        {
+                          best_regstart[mcnt] = regstart[mcnt];
+                          best_regend[mcnt] = regend[mcnt];
+                        }
+                    }
+                  goto fail;          
+                }
+
+              /* If no failure points, don't restore garbage.  */
+              else if (best_regs_set)   
+                {
+               restore_best_regs:
+                  /* Restore best match.  It may happen that `dend ==
+                     end_match_1' while the restored d is in string2.
+                     For example, the pattern `x.*y.*z' against the
+                     strings `x-' and `y-z-', if the two strings are
+                     not consecutive in memory.  */
+                  DEBUG_PRINT1 ("Restoring best registers.\n");
+                  
+                  d = match_end;
+                  dend = ((d >= string1 && d <= end1)
+                          ? end_match_1 : end_match_2);
+
+                 for (mcnt = 1; mcnt < num_regs; mcnt++)
+                   {
+                     regstart[mcnt] = best_regstart[mcnt];
+                     regend[mcnt] = best_regend[mcnt];
+                   }
+                }
+            } /* d != end_match_2 */
+
+          DEBUG_PRINT1 ("Accepting match.\n");
+
+          /* If caller wants register contents data back, do it.  */
+          if (regs && !bufp->no_sub)
+           {
+              /* Have the register data arrays been allocated?  */
+              if (bufp->regs_allocated == REGS_UNALLOCATED)
+                { /* No.  So allocate them with malloc.  We need one
+                     extra element beyond `num_regs' for the `-1' marker
+                     GNU code uses.  */
+                  regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+                  regs->start = TALLOC (regs->num_regs, regoff_t);
+                  regs->end = TALLOC (regs->num_regs, regoff_t);
+                  if (regs->start == NULL || regs->end == NULL)
+                    return -2;
+                  bufp->regs_allocated = REGS_REALLOCATE;
+                }
+              else if (bufp->regs_allocated == REGS_REALLOCATE)
+                { /* Yes.  If we need more elements than were already
+                     allocated, reallocate them.  If we need fewer, just
+                     leave it alone.  */
+                  if (regs->num_regs < num_regs + 1)
+                    {
+                      regs->num_regs = num_regs + 1;
+                      RETALLOC (regs->start, regs->num_regs, regoff_t);
+                      RETALLOC (regs->end, regs->num_regs, regoff_t);
+                      if (regs->start == NULL || regs->end == NULL)
+                        return -2;
+                    }
+                }
+              else
+               {
+                 /* These braces fend off a "empty body in an else-statement"
+                    warning under GCC when assert expands to nothing.  */
+                 assert (bufp->regs_allocated == REGS_FIXED);
+               }
+
+              /* Convert the pointer data in `regstart' and `regend' to
+                 indices.  Register zero has to be set differently,
+                 since we haven't kept track of any info for it.  */
+              if (regs->num_regs > 0)
+                {
+                  regs->start[0] = pos;
+                  regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
+                                 : d - string2 + size1);
+                }
+              
+              /* Go through the first `min (num_regs, regs->num_regs)'
+                 registers, since that is all we initialized.  */
+             for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+               {
+                  if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+                    regs->start[mcnt] = regs->end[mcnt] = -1;
+                  else
+                    {
+                     regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
+                      regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+                    }
+               }
+              
+              /* If the regs structure we return has more elements than
+                 were in the pattern, set the extra elements to -1.  If
+                 we (re)allocated the registers, this is the case,
+                 because we always allocate enough to have at least one
+                 -1 at the end.  */
+              for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+                regs->start[mcnt] = regs->end[mcnt] = -1;
+           } /* regs && !bufp->no_sub */
+
+          FREE_VARIABLES ();
+          DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+                        nfailure_points_pushed, nfailure_points_popped,
+                        nfailure_points_pushed - nfailure_points_popped);
+          DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+          mcnt = d - pos - (MATCHING_IN_FIRST_STRING 
+                           ? string1 
+                           : string2 - size1);
+
+          DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+          return mcnt;
+        }
+
+      /* Otherwise match next pattern command.  */
+#ifdef SWITCH_ENUM_BUG
+      switch ((int) ((re_opcode_t) *p++))
+#else
+      switch ((re_opcode_t) *p++)
+#endif
+       {
+        /* Ignore these.  Used to ignore the n of succeed_n's which
+           currently have n == 0.  */
+        case no_op:
+          DEBUG_PRINT1 ("EXECUTING no_op.\n");
+          break;
+
+
+        /* Match the next n pattern characters exactly.  The following
+           byte in the pattern defines n, and the n bytes after that
+           are the characters to match.  */
+       case exactn:
+         mcnt = *p++;
+          DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+          /* This is written out as an if-else so we don't waste time
+             testing `translate' inside the loop.  */
+          if (translate)
+           {
+             do
+               {
+                 PREFETCH ();
+                 if (translate[(unsigned char) *d++] != (char) *p++)
+                    goto fail;
+               }
+             while (--mcnt);
+           }
+         else
+           {
+             do
+               {
+                 PREFETCH ();
+                 if (*d++ != (char) *p++) goto fail;
+               }
+             while (--mcnt);
+           }
+         SET_REGS_MATCHED ();
+          break;
+
+
+        /* Match any character except possibly a newline or a null.  */
+       case anychar:
+          DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+          PREFETCH ();
+
+          if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+              || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+           goto fail;
+
+          SET_REGS_MATCHED ();
+          DEBUG_PRINT2 ("  Matched `%d'.\n", *d);
+          d++;
+         break;
+
+
+       case charset:
+       case charset_not:
+         {
+           register unsigned char c;
+           boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+            DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+           PREFETCH ();
+           c = TRANSLATE (*d); /* The character to match.  */
+
+            /* Cast to `unsigned' instead of `unsigned char' in case the
+               bit list is a full 32 bytes long.  */
+           if (c < (unsigned) (*p * BYTEWIDTH)
+               && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+             not = !not;
+
+           p += 1 + *p;
+
+           if (!not) goto fail;
+            
+           SET_REGS_MATCHED ();
+            d++;
+           break;
+         }
+
+
+        /* The beginning of a group is represented by start_memory.
+           The arguments are the register number in the next byte, and the
+           number of groups inner to this one in the next.  The text
+           matched within the group is recorded (in the internal
+           registers data structure) under the register number.  */
+        case start_memory:
+         DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+          /* Find out if this group can match the empty string.  */
+         p1 = p;               /* To send to group_match_null_string_p.  */
+          
+          if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+            REG_MATCH_NULL_STRING_P (reg_info[*p]) 
+              = group_match_null_string_p (&p1, pend, reg_info);
+
+          /* Save the position in the string where we were the last time
+             we were at this open-group operator in case the group is
+             operated upon by a repetition operator, e.g., with `(a*)*b'
+             against `ab'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                             ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+                             : regstart[*p];
+         DEBUG_PRINT2 ("  old_regstart: %d\n", 
+                        POINTER_TO_OFFSET (old_regstart[*p]));
+
+          regstart[*p] = d;
+         DEBUG_PRINT2 ("  regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+          IS_ACTIVE (reg_info[*p]) = 1;
+          MATCHED_SOMETHING (reg_info[*p]) = 0;
+          
+          /* This is the new highest active register.  */
+          highest_active_reg = *p;
+          
+          /* If nothing was active before, this is the new lowest active
+             register.  */
+          if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+            lowest_active_reg = *p;
+
+          /* Move past the register number and inner group count.  */
+          p += 2;
+          break;
+
+
+        /* The stop_memory opcode represents the end of a group.  Its
+           arguments are the same as start_memory's: the register
+           number, and the number of inner groups.  */
+       case stop_memory:
+         DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+             
+          /* We need to save the string position the last time we were at
+             this close-group operator in case the group is operated
+             upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+             against `aba'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                           ? REG_UNSET (regend[*p]) ? d : regend[*p]
+                          : regend[*p];
+         DEBUG_PRINT2 ("      old_regend: %d\n", 
+                        POINTER_TO_OFFSET (old_regend[*p]));
+
+          regend[*p] = d;
+         DEBUG_PRINT2 ("      regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+          /* This register isn't active anymore.  */
+          IS_ACTIVE (reg_info[*p]) = 0;
+          
+          /* If this was the only register active, nothing is active
+             anymore.  */
+          if (lowest_active_reg == highest_active_reg)
+            {
+              lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+              highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+            }
+          else
+            { /* We must scan for the new highest active register, since
+                 it isn't necessarily one less than now: consider
+                 (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
+                 new highest active register is 1.  */
+              unsigned char r = *p - 1;
+              while (r > 0 && !IS_ACTIVE (reg_info[r]))
+                r--;
+              
+              /* If we end up at register zero, that means that we saved
+                 the registers as the result of an `on_failure_jump', not
+                 a `start_memory', and we jumped to past the innermost
+                 `stop_memory'.  For example, in ((.)*) we save
+                 registers 1 and 2 as a result of the *, but when we pop
+                 back to the second ), we are at the stop_memory 1.
+                 Thus, nothing is active.  */
+             if (r == 0)
+                {
+                  lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+                  highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+                }
+              else
+                highest_active_reg = r;
+            }
+          
+          /* If just failed to match something this time around with a
+             group that's operated on by a repetition operator, try to
+             force exit from the ``loop'', and restore the register
+             information for this group that we had before trying this
+             last match.  */
+          if ((!MATCHED_SOMETHING (reg_info[*p])
+               || (re_opcode_t) p[-3] == start_memory)
+             && (p + 2) < pend)              
+            {
+              boolean is_a_jump_n = false;
+              
+              p1 = p + 2;
+              mcnt = 0;
+              switch ((re_opcode_t) *p1++)
+                {
+                  case jump_n:
+                   is_a_jump_n = true;
+                  case pop_failure_jump:
+                 case maybe_pop_jump:
+                 case jump:
+                 case dummy_failure_jump:
+                    EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                   if (is_a_jump_n)
+                     p1 += 2;
+                    break;
+                  
+                  default:
+                    /* do nothing */ ;
+                }
+             p1 += mcnt;
+        
+              /* If the next operation is a jump backwards in the pattern
+                to an on_failure_jump right before the start_memory
+                 corresponding to this stop_memory, exit from the loop
+                 by forcing a failure after pushing on the stack the
+                 on_failure_jump's jump in the pattern, and d.  */
+              if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+                  && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+               {
+                  /* If this group ever matched anything, then restore
+                     what its registers were before trying this last
+                     failed match, e.g., with `(a*)*b' against `ab' for
+                     regstart[1], and, e.g., with `((a*)*(b*)*)*'
+                     against `aba' for regend[3].
+                     
+                     Also restore the registers for inner groups for,
+                     e.g., `((a*)(b*))*' against `aba' (register 3 would
+                     otherwise get trashed).  */
+                     
+                  if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+                   {
+                     unsigned r; 
+        
+                      EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+                      
+                     /* Restore this and inner groups' (if any) registers.  */
+                      for (r = *p; r < *p + *(p + 1); r++)
+                        {
+                          regstart[r] = old_regstart[r];
+
+                          /* xx why this test?  */
+                          if ((int) old_regend[r] >= (int) regstart[r])
+                            regend[r] = old_regend[r];
+                        }     
+                    }
+                 p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+                  goto fail;
+                }
+            }
+          
+          /* Move past the register number and the inner group count.  */
+          p += 2;
+          break;
+
+
+       /* \<digit> has been turned into a `duplicate' command which is
+           followed by the numeric value of <digit> as the register number.  */
+        case duplicate:
+         {
+           register const char *d2, *dend2;
+           int regno = *p++;   /* Get which register to match against.  */
+           DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+           /* Can't back reference a group which we've never matched.  */
+            if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+              goto fail;
+              
+            /* Where in input to try to start matching.  */
+            d2 = regstart[regno];
+            
+            /* Where to stop matching; if both the place to start and
+               the place to stop matching are in the same string, then
+               set to the place to stop, otherwise, for now have to use
+               the end of the first string.  */
+
+            dend2 = ((FIRST_STRING_P (regstart[regno]) 
+                     == FIRST_STRING_P (regend[regno]))
+                    ? regend[regno] : end_match_1);
+           for (;;)
+             {
+               /* If necessary, advance to next segment in register
+                   contents.  */
+               while (d2 == dend2)
+                 {
+                   if (dend2 == end_match_2) break;
+                   if (dend2 == regend[regno]) break;
+
+                    /* End of string1 => advance to string2. */
+                    d2 = string2;
+                    dend2 = regend[regno];
+                 }
+               /* At end of register contents => success */
+               if (d2 == dend2) break;
+
+               /* If necessary, advance to next segment in data.  */
+               PREFETCH ();
+
+               /* How many characters left in this segment to match.  */
+               mcnt = dend - d;
+                
+               /* Want how many consecutive characters we can match in
+                   one shot, so, if necessary, adjust the count.  */
+                if (mcnt > dend2 - d2)
+                 mcnt = dend2 - d2;
+                  
+               /* Compare that many; failure if mismatch, else move
+                   past them.  */
+               if (translate 
+                    ? bcmp_translate (d, d2, mcnt, translate) 
+                    : bcmp (d, d2, mcnt))
+                 goto fail;
+               d += mcnt, d2 += mcnt;
+             }
+         }
+         break;
+
+
+        /* begline matches the empty string at the beginning of the string
+           (unless `not_bol' is set in `bufp'), and, if
+           `newline_anchor' is set, after newlines.  */
+       case begline:
+          DEBUG_PRINT1 ("EXECUTING begline.\n");
+          
+          if (AT_STRINGS_BEG (d))
+            {
+              if (!bufp->not_bol) break;
+            }
+          else if (d[-1] == '\n' && bufp->newline_anchor)
+            {
+              break;
+            }
+          /* In all other cases, we fail.  */
+          goto fail;
+
+
+        /* endline is the dual of begline.  */
+       case endline:
+          DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+          if (AT_STRINGS_END (d))
+            {
+              if (!bufp->not_eol) break;
+            }
+          
+          /* We have to ``prefetch'' the next character.  */
+          else if ((d == end1 ? *string2 : *d) == '\n'
+                   && bufp->newline_anchor)
+            {
+              break;
+            }
+          goto fail;
+
+
+       /* Match at the very beginning of the data.  */
+        case begbuf:
+          DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+          if (AT_STRINGS_BEG (d))
+            break;
+          goto fail;
+
+
+       /* Match at the very end of the data.  */
+        case endbuf:
+          DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+         if (AT_STRINGS_END (d))
+           break;
+          goto fail;
+
+
+        /* on_failure_keep_string_jump is used to optimize `.*\n'.  It
+           pushes NULL as the value for the string on the stack.  Then
+           `pop_failure_point' will keep the current value for the
+           string, instead of restoring it.  To see why, consider
+           matching `foo\nbar' against `.*\n'.  The .* matches the foo;
+           then the . fails against the \n.  But the next thing we want
+           to do is match the \n against the \n; if we restored the
+           string value, we would be back at the foo.
+           
+           Because this is used only in specific cases, we don't need to
+           check all the things that `on_failure_jump' does, to make
+           sure the right things get saved on the stack.  Hence we don't
+           share its code.  The only reason to push anything on the
+           stack at all is that otherwise we would have to change
+           `anychar's code to do something besides goto fail in this
+           case; that seems worse than this.  */
+        case on_failure_keep_string_jump:
+          DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+          
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+          PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+          break;
+
+
+       /* Uses of on_failure_jump:
+        
+           Each alternative starts with an on_failure_jump that points
+           to the beginning of the next alternative.  Each alternative
+           except the last ends with a jump that in effect jumps past
+           the rest of the alternatives.  (They really jump to the
+           ending jump of the following alternative, because tensioning
+           these jumps is a hassle.)
+
+           Repeats start with an on_failure_jump that points past both
+           the repetition text and either the following jump or
+           pop_failure_jump back to this on_failure_jump.  */
+       case on_failure_jump:
+        on_failure:
+          DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+          /* If this on_failure_jump comes right before a group (i.e.,
+             the original * applied to a group), save the information
+             for that group and all inner ones, so that if we fail back
+             to this point, the group's information will be correct.
+             For example, in \(a*\)*\1, we need the preceding group,
+             and in \(\(a*\)b*\)\2, we need the inner group.  */
+
+          /* We can't use `p' to check ahead because we push
+             a failure point to `p + mcnt' after we do this.  */
+          p1 = p;
+
+          /* We need to skip no_op's before we look for the
+             start_memory in case this on_failure_jump is happening as
+             the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+             against aba.  */
+          while (p1 < pend && (re_opcode_t) *p1 == no_op)
+            p1++;
+
+          if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+            {
+              /* We have a new highest active register now.  This will
+                 get reset at the start_memory we are about to get to,
+                 but we will have saved all the registers relevant to
+                 this repetition op, as described above.  */
+              highest_active_reg = *(p1 + 1) + *(p1 + 2);
+              if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+                lowest_active_reg = *(p1 + 1);
+            }
+
+          DEBUG_PRINT1 (":\n");
+          PUSH_FAILURE_POINT (p + mcnt, d, -2);
+          break;
+
+
+        /* A smart repeat ends with `maybe_pop_jump'.
+          We change it to either `pop_failure_jump' or `jump'.  */
+        case maybe_pop_jump:
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+          {
+           register unsigned char *p2 = p;
+
+            /* Compare the beginning of the repeat with what in the
+               pattern follows its end. If we can establish that there
+               is nothing that they would both match, i.e., that we
+               would have to backtrack because of (as in, e.g., `a*a')
+               then we can change to pop_failure_jump, because we'll
+               never have to backtrack.
+               
+               This is not true in the case of alternatives: in
+               `(a|ab)*' we do need to backtrack to the `ab' alternative
+               (e.g., if the string was `ab').  But instead of trying to
+               detect that here, the alternative has put on a dummy
+               failure point which is what we will end up popping.  */
+
+           /* Skip over open/close-group commands.
+              If what follows this loop is a ...+ construct,
+              look at what begins its body, since we will have to
+              match at least one of that.  */
+           while (1)
+             {
+               if (p2 + 2 < pend
+                   && ((re_opcode_t) *p2 == stop_memory
+                       || (re_opcode_t) *p2 == start_memory))
+                 p2 += 3;
+               else if (p2 + 6 < pend
+                        && (re_opcode_t) *p2 == dummy_failure_jump)
+                 p2 += 6;
+               else
+                 break;
+             }
+
+           p1 = p + mcnt;
+           /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+              to the `maybe_finalize_jump' of this case.  Examine what 
+              follows.  */
+
+            /* If we're at the end of the pattern, we can change.  */
+            if (p2 == pend)
+             {
+               /* Consider what happens when matching ":\(.*\)"
+                  against ":/".  I don't really understand this code
+                  yet.  */
+               p[-3] = (unsigned char) pop_failure_jump;
+                DEBUG_PRINT1
+                  ("  End of pattern: change to `pop_failure_jump'.\n");
+              }
+
+            else if ((re_opcode_t) *p2 == exactn
+                    || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+             {
+               register unsigned char c
+                  = *p2 == (unsigned char) endline ? '\n' : p2[2];
+
+                if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+                  {
+                   p[-3] = (unsigned char) pop_failure_jump;
+                    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
+                                  c, p1[5]);
+                  }
+                  
+               else if ((re_opcode_t) p1[3] == charset
+                        || (re_opcode_t) p1[3] == charset_not)
+                 {
+                   int not = (re_opcode_t) p1[3] == charset_not;
+                    
+                   if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+                       && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+                     not = !not;
+
+                    /* `not' is equal to 1 if c would match, which means
+                        that we can't change to pop_failure_jump.  */
+                   if (!not)
+                      {
+                       p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+                 }
+             }
+            else if ((re_opcode_t) *p2 == charset)
+             {
+               register unsigned char c
+                  = *p2 == (unsigned char) endline ? '\n' : p2[2];
+
+                if ((re_opcode_t) p1[3] == exactn
+                   && ! (p2[1] * BYTEWIDTH > p1[4]
+                         && (p2[1 + p1[4] / BYTEWIDTH]
+                             & (1 << (p1[4] % BYTEWIDTH)))))
+                  {
+                   p[-3] = (unsigned char) pop_failure_jump;
+                    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
+                                  c, p1[5]);
+                  }
+                  
+               else if ((re_opcode_t) p1[3] == charset_not)
+                 {
+                   int idx;
+                   /* We win if the charset_not inside the loop
+                      lists every character listed in the charset after.  */
+                   for (idx = 0; idx < p2[1]; idx++)
+                     if (! (p2[2 + idx] == 0
+                            || (idx < p1[4]
+                                && ((p2[2 + idx] & ~ p1[5 + idx]) == 0))))
+                       break;
+
+                   if (idx == p2[1])
+                      {
+                       p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+                 }
+               else if ((re_opcode_t) p1[3] == charset)
+                 {
+                   int idx;
+                   /* We win if the charset inside the loop
+                      has no overlap with the one after the loop.  */
+                   for (idx = 0; idx < p2[1] && idx < p1[4]; idx++)
+                     if ((p2[2 + idx] & p1[5 + idx]) != 0)
+                       break;
+
+                   if (idx == p2[1] || idx == p1[4])
+                      {
+                       p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+                 }
+             }
+         }
+         p -= 2;               /* Point at relative address again.  */
+         if ((re_opcode_t) p[-1] != pop_failure_jump)
+           {
+             p[-1] = (unsigned char) jump;
+              DEBUG_PRINT1 ("  Match => jump.\n");
+             goto unconditional_jump;
+           }
+        /* Note fall through.  */
+
+
+       /* The end of a simple repeat has a pop_failure_jump back to
+           its matching on_failure_jump, where the latter will push a
+           failure point.  The pop_failure_jump takes off failure
+           points put on by this pop_failure_jump's matching
+           on_failure_jump; we got through the pattern to here from the
+           matching on_failure_jump, so didn't fail.  */
+        case pop_failure_jump:
+          {
+            /* We need to pass separate storage for the lowest and
+               highest registers, even though we don't care about the
+               actual values.  Otherwise, we will restore only one
+               register from the stack, since lowest will == highest in
+               `pop_failure_point'.  */
+            unsigned dummy_low_reg, dummy_high_reg;
+            unsigned char *pdummy;
+            const char *sdummy;
+
+            DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+            POP_FAILURE_POINT (sdummy, pdummy,
+                               dummy_low_reg, dummy_high_reg,
+                               reg_dummy, reg_dummy, reg_info_dummy);
+          }
+          /* Note fall through.  */
+
+          
+        /* Unconditionally jump (without popping any failure points).  */
+        case jump:
+       unconditional_jump:
+         EXTRACT_NUMBER_AND_INCR (mcnt, p);    /* Get the amount to jump.  */
+          DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+         p += mcnt;                            /* Do the jump.  */
+          DEBUG_PRINT2 ("(to 0x%x).\n", p);
+         break;
+
+       
+        /* We need this opcode so we can detect where alternatives end
+           in `group_match_null_string_p' et al.  */
+        case jump_past_alt:
+          DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+          goto unconditional_jump;
+
+
+        /* Normally, the on_failure_jump pushes a failure point, which
+           then gets popped at pop_failure_jump.  We will end up at
+           pop_failure_jump, also, and with a pattern of, say, `a+', we
+           are skipping over the on_failure_jump, so we have to push
+           something meaningless for pop_failure_jump to pop.  */
+        case dummy_failure_jump:
+          DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+          /* It doesn't matter what we push for the string here.  What
+             the code at `fail' tests is the value for the pattern.  */
+          PUSH_FAILURE_POINT (0, 0, -2);
+          goto unconditional_jump;
+
+
+        /* At the end of an alternative, we need to push a dummy failure
+           point in case we are followed by a `pop_failure_jump', because
+           we don't want the failure point for the alternative to be
+           popped.  For example, matching `(a|ab)*' against `aab'
+           requires that we match the `ab' alternative.  */
+        case push_dummy_failure:
+          DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+          /* See comments just above at `dummy_failure_jump' about the
+             two zeroes.  */
+          PUSH_FAILURE_POINT (0, 0, -2);
+          break;
+
+        /* Have to succeed matching what follows at least n times.
+           After that, handle like `on_failure_jump'.  */
+        case succeed_n: 
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+          assert (mcnt >= 0);
+          /* Originally, this is how many times we HAVE to succeed.  */
+          if (mcnt > 0)
+            {
+               mcnt--;
+              p += 2;
+               STORE_NUMBER_AND_INCR (p, mcnt);
+               DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p, mcnt);
+            }
+         else if (mcnt == 0)
+            {
+              DEBUG_PRINT2 ("  Setting two bytes from 0x%x to no_op.\n", p+2);
+             p[2] = (unsigned char) no_op;
+              p[3] = (unsigned char) no_op;
+              goto on_failure;
+            }
+          break;
+        
+        case jump_n: 
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+          /* Originally, this is how many times we CAN jump.  */
+          if (mcnt)
+            {
+               mcnt--;
+               STORE_NUMBER (p + 2, mcnt);
+              goto unconditional_jump;      
+            }
+          /* If don't have to jump any more, skip over the rest of command.  */
+         else      
+           p += 4;                  
+          break;
+        
+       case set_number_at:
+         {
+            DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+            p1 = p + mcnt;
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+            DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p1, mcnt);
+           STORE_NUMBER (p1, mcnt);
+            break;
+          }
+
+        case wordbound:
+          DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+          if (AT_WORD_BOUNDARY (d))
+           break;
+          goto fail;
+
+       case notwordbound:
+          DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+         if (AT_WORD_BOUNDARY (d))
+           goto fail;
+          break;
+
+       case wordbeg:
+          DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+         if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+           break;
+          goto fail;
+
+       case wordend:
+          DEBUG_PRINT1 ("EXECUTING wordend.\n");
+         if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+              && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+           break;
+          goto fail;
+
+#ifdef emacs
+#ifdef emacs19
+       case before_dot:
+          DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+           goto fail;
+         break;
+  
+       case at_dot:
+          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) != point)
+           goto fail;
+         break;
+  
+       case after_dot:
+          DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+          if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+           goto fail;
+         break;
+#else /* not emacs19 */
+       case at_dot:
+          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
+           goto fail;
+         break;
+#endif /* not emacs19 */
+
+       case syntaxspec:
+          DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+         mcnt = *p++;
+         goto matchsyntax;
+
+        case wordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+         mcnt = (int) Sword;
+        matchsyntax:
+         PREFETCH ();
+         if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
+            goto fail;
+          SET_REGS_MATCHED ();
+         break;
+
+       case notsyntaxspec:
+          DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+         mcnt = *p++;
+         goto matchnotsyntax;
+
+        case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+         mcnt = (int) Sword;
+        matchnotsyntax:
+         PREFETCH ();
+         if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
+            goto fail;
+         SET_REGS_MATCHED ();
+          break;
+
+#else /* not emacs */
+       case wordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+         PREFETCH ();
+          if (!WORDCHAR_P (d))
+            goto fail;
+         SET_REGS_MATCHED ();
+          d++;
+         break;
+         
+       case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+         PREFETCH ();
+         if (WORDCHAR_P (d))
+            goto fail;
+          SET_REGS_MATCHED ();
+          d++;
+         break;
+#endif /* not emacs */
+          
+        default:
+          abort ();
+       }
+      continue;  /* Successfully executed one pattern command; keep going.  */
+
+
+    /* We goto here if a matching operation fails. */
+    fail:
+      if (!FAIL_STACK_EMPTY ())
+       { /* A restart point is known.  Restore to that state.  */
+          DEBUG_PRINT1 ("\nFAIL:\n");
+          POP_FAILURE_POINT (d, p,
+                             lowest_active_reg, highest_active_reg,
+                             regstart, regend, reg_info);
+
+          /* If this failure point is a dummy, try the next one.  */
+          if (!p)
+           goto fail;
+
+          /* If we failed to the end of the pattern, don't examine *p.  */
+         assert (p <= pend);
+          if (p < pend)
+            {
+              boolean is_a_jump_n = false;
+              
+              /* If failed to a backwards jump that's part of a repetition
+                 loop, need to pop this failure point and use the next one.  */
+              switch ((re_opcode_t) *p)
+                {
+                case jump_n:
+                  is_a_jump_n = true;
+                case maybe_pop_jump:
+                case pop_failure_jump:
+                case jump:
+                  p1 = p + 1;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  p1 += mcnt;  
+
+                  if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+                      || (!is_a_jump_n
+                          && (re_opcode_t) *p1 == on_failure_jump))
+                    goto fail;
+                  break;
+                default:
+                  /* do nothing */ ;
+                }
+            }
+
+          if (d >= string1 && d <= end1)
+           dend = end_match_1;
+        }
+      else
+        break;   /* Matching at this starting point really fails.  */
+    } /* for (;;) */
+
+  if (best_regs_set)
+    goto restore_best_regs;
+
+  FREE_VARIABLES ();
+
+  return -1;                           /* Failure to match.  */
+} /* re_match_2 */
+\f
+/* Subroutine definitions for re_match_2.  */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+   
+   Return true if the pattern up to the corresponding stop_memory can
+   match the empty string, and false otherwise.
+   
+   If we find the matching stop_memory, sets P to point to one past its number.
+   Otherwise, sets P to an undefined byte less than or equal to END.
+
+   We don't handle duplicates properly (yet).  */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  /* Point to after the args to the start_memory.  */
+  unsigned char *p1 = *p + 2;
+  
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and return true or
+        false, as appropriate, when we get to one that can't, or to the
+         matching stop_memory.  */
+      
+      switch ((re_opcode_t) *p1)
+        {
+        /* Could be either a loop or a series of alternatives.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          
+          /* If the next operation is not a jump backwards in the
+            pattern.  */
+
+         if (mcnt >= 0)
+           {
+              /* Go through the on_failure_jumps of the alternatives,
+                 seeing if any of the alternatives cannot match nothing.
+                 The last alternative starts with only a jump,
+                 whereas the rest start with on_failure_jump and end
+                 with a jump, e.g., here is the pattern for `a|b|c':
+
+                 /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+                 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+                 /exactn/1/c                                           
+
+                 So, we have to first go through the first (n-1)
+                 alternatives and then deal with the last one separately.  */
+
+
+              /* Deal with the first (n-1) alternatives, which start
+                 with an on_failure_jump (see above) that jumps to right
+                 past a jump_past_alt.  */
+
+              while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+                {
+                  /* `mcnt' holds how many bytes long the alternative
+                     is, including the ending `jump_past_alt' and
+                     its number.  */
+
+                  if (!alt_match_null_string_p (p1, p1 + mcnt - 3, 
+                                                     reg_info))
+                    return false;
+
+                  /* Move to right after this alternative, including the
+                    jump_past_alt.  */
+                  p1 += mcnt;  
+
+                  /* Break if it's the beginning of an n-th alternative
+                     that doesn't begin with an on_failure_jump.  */
+                  if ((re_opcode_t) *p1 != on_failure_jump)
+                    break;
+               
+                 /* Still have to check that it's not an n-th
+                    alternative that starts with an on_failure_jump.  */
+                 p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+                    {
+                     /* Get to the beginning of the n-th alternative.  */
+                      p1 -= 3;
+                      break;
+                    }
+                }
+
+              /* Deal with the last alternative: go back and get number
+                 of the `jump_past_alt' just before it.  `mcnt' contains
+                 the length of the alternative.  */
+              EXTRACT_NUMBER (mcnt, p1 - 2);
+
+              if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+                return false;
+
+              p1 += mcnt;      /* Get past the n-th alternative.  */
+            } /* if mcnt > 0 */
+          break;
+
+          
+        case stop_memory:
+         assert (p1[1] == **p);
+          *p = p1 + 2;
+          return true;
+
+        
+        default: 
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    } /* while p1 < end */
+
+  return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+   It expects P to be the first byte of a single alternative and END one
+   byte past the last. The alternative can contain groups.  */
+   
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+    unsigned char *p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  unsigned char *p1 = p;
+  
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and break when we get 
+         to one that can't.  */
+      
+      switch ((re_opcode_t) *p1)
+        {
+       /* It's a loop.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+          break;
+          
+       default: 
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    }  /* while p1 < end */
+
+  return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+   alt_match_null_string_p.  
+   
+   Sets P to one after the op and its arguments, if any.  */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  boolean ret;
+  int reg_no;
+  unsigned char *p1 = *p;
+
+  switch ((re_opcode_t) *p1++)
+    {
+    case no_op:
+    case begline:
+    case endline:
+    case begbuf:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case wordbound:
+    case notwordbound:
+#ifdef emacs
+    case before_dot:
+    case at_dot:
+    case after_dot:
+#endif
+      break;
+
+    case start_memory:
+      reg_no = *p1;
+      assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+      ret = group_match_null_string_p (&p1, end, reg_info);
+      
+      /* Have to set this here in case we're checking a group which
+         contains a group and a back reference to it.  */
+
+      if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+        REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+      if (!ret)
+        return false;
+      break;
+          
+    /* If this is an optimized succeed_n for zero times, make the jump.  */
+    case jump:
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+      if (mcnt >= 0)
+        p1 += mcnt;
+      else
+        return false;
+      break;
+
+    case succeed_n:
+      /* Get to the number of times to succeed.  */
+      p1 += 2;         
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+      if (mcnt == 0)
+        {
+          p1 -= 4;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+        }
+      else
+        return false;
+      break;
+
+    case duplicate: 
+      if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+        return false;
+      break;
+
+    case set_number_at:
+      p1 += 4;
+
+    default:
+      /* All other opcodes mean we cannot match the empty string.  */
+      return false;
+  }
+
+  *p = p1;
+  return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+   bytes; nonzero otherwise.  */
+   
+static int
+bcmp_translate (s1, s2, len, translate)
+     unsigned char *s1, *s2;
+     register int len;
+     char *translate;
+{
+  register unsigned char *p1 = s1, *p2 = s2;
+  while (len)
+    {
+      if (translate[*p1++] != translate[*p2++]) return 1;
+      len--;
+    }
+  return 0;
+}
+\f
+/* Entry points for GNU code.  */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length SIZE) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+   
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.
+   
+   We call regex_compile to do the actual compilation.  */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+     const char *pattern;
+     int length;
+     struct re_pattern_buffer *bufp;
+{
+  reg_errcode_t ret;
+  
+  /* GNU code is written to assume at least RE_NREGS registers will be set
+     (and at least one extra will be -1).  */
+  bufp->regs_allocated = REGS_UNALLOCATED;
+  
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub.  */
+  bufp->no_sub = 0;
+  
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+  
+  ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+  return re_error_msg[(int) ret];
+}     
+\f
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them if this is an Emacs or POSIX compilation.  */
+
+#if !defined (emacs) && !defined (_POSIX_SOURCE)
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+    const char *s;
+{
+  reg_errcode_t ret;
+  
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+       return "No previous regular expression";
+      return 0;
+    }
+
+  if (!re_comp_buf.buffer)
+    {
+      re_comp_buf.buffer = (unsigned char *) malloc (200);
+      if (re_comp_buf.buffer == NULL)
+        return "Memory exhausted";
+      re_comp_buf.allocated = 200;
+
+      re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+      if (re_comp_buf.fastmap == NULL)
+       return "Memory exhausted";
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+  
+  /* Yes, we're discarding `const' here.  */
+  return (char *) re_error_msg[(int) ret];
+}
+
+
+int
+re_exec (s)
+    const char *s;
+{
+  const int len = strlen (s);
+  return
+    0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+#endif /* not emacs and not _POSIX_SOURCE */
+\f
+/* POSIX.2 functions.  Don't define these for Emacs.  */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' and `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (preg, pattern, cflags)
+    regex_t *preg;
+    const char *pattern; 
+    int cflags;
+{
+  reg_errcode_t ret;
+  unsigned syntax
+    = (cflags & REG_EXTENDED) ?
+      RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+  /* regex_compile will allocate the space for the compiled pattern.  */
+  preg->buffer = 0;
+  preg->allocated = 0;
+  preg->used = 0;
+  
+  /* Don't bother to use a fastmap when searching.  This simplifies the
+     REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+     characters after newlines into the fastmap.  This way, we just try
+     every character.  */
+  preg->fastmap = 0;
+  
+  if (cflags & REG_ICASE)
+    {
+      unsigned i;
+      
+      preg->translate = (char *) malloc (CHAR_SET_SIZE);
+      if (preg->translate == NULL)
+        return (int) REG_ESPACE;
+
+      /* Map uppercase characters to corresponding lowercase ones.  */
+      for (i = 0; i < CHAR_SET_SIZE; i++)
+        preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
+    }
+  else
+    preg->translate = NULL;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+
+  preg->no_sub = !!(cflags & REG_NOSUB);
+
+  /* POSIX says a null character in the pattern terminates it, so we 
+     can use strlen here in compiling the pattern.  */
+  ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+  
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN) ret = REG_EPAREN;
+  
+  return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+   
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+   
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+   
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+    const regex_t *preg;
+    const char *string; 
+    size_t nmatch; 
+    regmatch_t pmatch[]; 
+    int eflags;
+{
+  int ret;
+  struct re_registers regs;
+  regex_t private_preg;
+  int len = strlen (string);
+  boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+  private_preg = *preg;
+  
+  private_preg.not_bol = !!(eflags & REG_NOTBOL);
+  private_preg.not_eol = !!(eflags & REG_NOTEOL);
+  
+  /* The user has told us exactly how many registers to return
+     information about, via `nmatch'.  We have to pass that on to the
+     matching routines.  */
+  private_preg.regs_allocated = REGS_FIXED;
+  
+  if (want_reg_info)
+    {
+      regs.num_regs = nmatch;
+      regs.start = TALLOC (nmatch, regoff_t);
+      regs.end = TALLOC (nmatch, regoff_t);
+      if (regs.start == NULL || regs.end == NULL)
+        return (int) REG_NOMATCH;
+    }
+
+  /* Perform the searching operation.  */
+  ret = re_search (&private_preg, string, len,
+                   /* start: */ 0, /* range: */ len,
+                   want_reg_info ? &regs : (struct re_registers *) 0);
+  
+  /* Copy the register information to the POSIX structure.  */
+  if (want_reg_info)
+    {
+      if (ret >= 0)
+        {
+          unsigned r;
+
+          for (r = 0; r < nmatch; r++)
+            {
+              pmatch[r].rm_so = regs.start[r];
+              pmatch[r].rm_eo = regs.end[r];
+            }
+        }
+
+      /* If we needed the temporary register info, free the space now.  */
+      free (regs.start);
+      free (regs.end);
+    }
+
+  /* We want zero return to mean success, unlike `re_search'.  */
+  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+    int errcode;
+    const regex_t *preg;
+    char *errbuf;
+    size_t errbuf_size;
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (errcode < 0
+      || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
+    /* Only error codes returned by the rest of the code should be passed 
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = re_error_msg[errcode];
+
+  /* POSIX doesn't require that we do anything in this case, but why
+     not be nice.  */
+  if (! msg)
+    msg = "Success";
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+  
+  if (errbuf_size != 0)
+    {
+      if (msg_size > errbuf_size)
+        {
+          strncpy (errbuf, msg, errbuf_size - 1);
+          errbuf[errbuf_size - 1] = 0;
+        }
+      else
+        strcpy (errbuf, msg);
+    }
+
+  return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (preg)
+    regex_t *preg;
+{
+  if (preg->buffer != NULL)
+    free (preg->buffer);
+  preg->buffer = NULL;
+  
+  preg->allocated = 0;
+  preg->used = 0;
+
+  if (preg->fastmap != NULL)
+    free (preg->fastmap);
+  preg->fastmap = NULL;
+  preg->fastmap_accurate = 0;
+
+  if (preg->translate != NULL)
+    free (preg->translate);
+  preg->translate = NULL;
+}
+
+#endif /* not emacs  */
+\f
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/Src/diffutils/lib/REGEX.H b/Src/diffutils/lib/REGEX.H
new file mode 100644 (file)
index 0000000..e154bd5
--- /dev/null
@@ -0,0 +1,498 @@
+/* Definitions for data structures and routines for the regular
+   expression library, version 0.12.
+
+   Copyright (C) 1985, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+   <regex.h>.  */
+
+#ifdef VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+   should be there.  */
+#include <stddef.h>
+#endif
+
+
+/* The following bits are used to determine the regexp syntax we
+   recognize.  The set/not-set meanings are chosen so that Emacs syntax
+   remains the value 0.  The bits are given in alphabetical order, and
+   the definitions shifted by one from the previous bit; thus, when we
+   add or remove a bit, only one other definition need change.  */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+   If set, then such a \ quotes the following character.  */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+     literals. 
+   If set, then \+ and \? are operators and + and ? are literals.  */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported.  They are:
+     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
+     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+   If not set, then character classes are not supported.  */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+     expressions, of course).
+   If this bit is not set, then it depends:
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.  
+
+   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+   POSIX draft 11.2 says that * etc. in leading positions is undefined.
+   We already implemented a previous draft which made those constructs
+   invalid, though, so we haven't changed the code back.  */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+     regardless of where they are in the pattern.
+   If this bit is not set, then special characters are special only in
+     some contexts; otherwise they are ordinary.  Specifically, 
+     * + ? and intervals are only special when not after the beginning,
+     open-group, or alternation operator.  */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+     immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+   If not set, then it doesn't.  */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+   If not set, then it does.  */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+   If not set, they do.  */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+     interval, depending on RE_NO_BK_BRACES. 
+   If not set, \{, \}, {, and } are literals.  */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+   If not set, they are.  */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+   If not set, newline is literal.  */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+     are literals.
+  If not set, then `\{...\}' defines an interval.  */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+   If not set, \(...\) defines a group, and ( and ) are literals.  */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+   If not set, then \<digit> is a back-reference.  */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal. 
+   If not set, then \| is an alternation operator, and | is literal.  */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+     than the starting range point, as in [z-a], is invalid.
+   If not set, then when ending range point collates higher than the
+     starting range point, the range is ignored.  */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+   If not set, then an unmatched ) is invalid.  */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+   some interfaces).  When a regexp is compiled, the syntax used is
+   stored in the pattern buffer, so changing this does not affect
+   already-compiled regexps.  */
+extern reg_syntax_t re_syntax_options;
+\f
+/* Define combinations of the above bits for the standard possibilities.
+   (The [[[ comments delimit what gets put into the Texinfo file, so
+   don't delete them!)  */ 
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK                                                  \
+  (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL                      \
+   | RE_NO_BK_PARENS            | RE_NO_BK_REFS                                \
+   | RE_NO_BK_VBAR               | RE_NO_EMPTY_RANGES                  \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK                                            \
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP                                                 \
+  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES                                \
+   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS                           \
+   | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP                                                        \
+  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS                   \
+   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE                   \
+   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS                            \
+   | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP                                          \
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax.  */
+#define _RE_SYNTAX_POSIX_COMMON                                                \
+  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL             \
+   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC                                          \
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
+   isn't minimal, since other operators, such as \`, aren't disabled.  */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC                                  \
+  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED                                       \
+  (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS                  \
+   | RE_CONTEXT_INDEP_OPS  | RE_NO_BK_BRACES                           \
+   | RE_NO_BK_PARENS       | RE_NO_BK_VBAR                             \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+   replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added.  */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                               \
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS                 \
+   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES                          \
+   | RE_NO_BK_PARENS        | RE_NO_BK_REFS                            \
+   | RE_NO_BK_VBAR         | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+\f
+/* Maximum number of duplicates an interval can allow.  Some systems
+   (erroneously) define this in other header files, but we want our
+   value, so remove any previous define.  */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1) 
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+
+/* If this bit is set, then use extended regular expression syntax.
+   If not set, then use basic regular expression syntax.  */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define REG_ICASE (REG_EXTENDED << 1)
+/* If this bit is set, then anchors do not match at newline
+     characters in the string.
+   If not set, then anchors do match at newlines.  */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+   If not set, then returns differ between not matching and errors.  */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec).  */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+     the beginning of the string (presumably because it's not the
+     beginning of a line).
+   If not set, then the beginning-of-line operator does match the
+     beginning of the string.  */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.  */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+   `re_error_msg' table in regex.c.  */
+typedef enum
+{
+  REG_NOERROR = 0,     /* Success.  */
+  REG_NOMATCH,         /* Didn't find a match (for regexec).  */
+
+  /* POSIX regcomp return error codes.  (In the order listed in the
+     standard.)  */
+  REG_BADPAT,          /* Invalid pattern.  */
+  REG_ECOLLATE,                /* Not implemented.  */
+  REG_ECTYPE,          /* Invalid character class name.  */
+  REG_EESCAPE,         /* Trailing backslash.  */
+  REG_ESUBREG,         /* Invalid back reference.  */
+  REG_EBRACK,          /* Unmatched left bracket.  */
+  REG_EPAREN,          /* Parenthesis imbalance.  */ 
+  REG_EBRACE,          /* Unmatched \{.  */
+  REG_BADBR,           /* Invalid contents of \{\}.  */
+  REG_ERANGE,          /* Invalid range end.  */
+  REG_ESPACE,          /* Ran out of memory.  */
+  REG_BADRPT,          /* No preceding re for repetition op.  */
+
+  /* Error codes we've added.  */
+  REG_EEND,            /* Premature end.  */
+  REG_ESIZE,           /* Compiled pattern bigger than 2^16 bytes.  */
+  REG_ERPAREN          /* Unmatched ) or \); not returned from regcomp.  */
+} reg_errcode_t;
+\f
+/* This data structure represents a compiled pattern.  Before calling
+   the pattern compiler, the fields `buffer', `allocated', `fastmap',
+   `translate', and `no_sub' can be set.  After the pattern has been
+   compiled, the `re_nsub' field is available.  All other fields are
+   private to the regex routines.  */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+       /* Space that holds the compiled pattern.  It is declared as
+          `unsigned char *' because its elements are
+           sometimes used as array indexes.  */
+  unsigned char *buffer;
+
+       /* Number of bytes to which `buffer' points.  */
+  unsigned long allocated;
+
+       /* Number of bytes actually used in `buffer'.  */
+  unsigned long used;  
+
+        /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t syntax;
+
+        /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
+           the fastmap, if there is one, to skip over impossible
+           starting points for matches.  */
+  char *fastmap;
+
+        /* Either a translate table to apply to all characters before
+           comparing them, or zero for no translation.  The translation
+           is applied to a pattern when it is compiled and to a string
+           when it is matched.  */
+  char *translate;
+
+       /* Number of subexpressions found by the compiler.  */
+  size_t re_nsub;
+
+        /* Zero if this pattern cannot match the empty string, one else.
+           Well, in truth it's used only in `re_search_2', to see
+           whether or not we should use the fastmap, so we don't set
+           this absolutely perfectly; see `re_compile_fastmap' (the
+           `duplicate' case).  */
+  unsigned can_be_null : 1;
+
+        /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+             for `max (RE_NREGS, re_nsub + 1)' groups.
+           If REGS_REALLOCATE, reallocate space if necessary.
+           If REGS_FIXED, use what's there.  */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+  unsigned regs_allocated : 2;
+
+        /* Set to zero when `regex_compile' compiles a pattern; set to one
+           by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned fastmap_accurate : 1;
+
+        /* If set, `re_match_2' does not return information about
+           subexpressions.  */
+  unsigned no_sub : 1;
+
+        /* If set, a beginning-of-line anchor doesn't match at the
+           beginning of the string.  */ 
+  unsigned not_bol : 1;
+
+        /* Similarly for an end-of-line anchor.  */
+  unsigned not_eol : 1;
+
+        /* If true, an anchor at a newline matches.  */
+  unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value.  It is
+   defined both in `regex.c' and here.  */
+#define RE_EXACTN_VALUE 1
+\f
+/* Type for byte offsets within the string.  POSIX mandates this.  */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in.  See
+   regex.texinfo for a full description of what registers match.  */
+struct re_registers
+{
+  unsigned num_regs;
+  regoff_t *start;
+  regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+   `re_match_2' returns information about at least this many registers
+   the first time a `regs' structure is passed.  */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers.  Aside from the different names than
+   `re_registers', POSIX uses an array of structures, instead of a
+   structure of arrays.  */
+typedef struct
+{
+  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
+  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
+} regmatch_t;
+\f
+/* Declarations for routines.  */
+
+/* To avoid duplicating every routine declaration -- once with a
+   prototype (if we are ANSI), and once without (if we aren't) -- we
+   use the following macro to declare argument types.  This
+   unfortunately clutters up the declarations a bit, but I think it's
+   worth it.  */
+
+#if __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+   You can also simply assign to the `re_syntax_options' variable.  */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+   and syntax given by the global `re_syntax_options', into the buffer
+   BUFFER.  Return NULL if successful, and an error string if not.  */
+extern const char *re_compile_pattern
+  _RE_ARGS ((const char *pattern, int length,
+             struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+   accelerate searches.  Return 0 if successful and -2 if was an
+   internal error.  */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+   compiled into BUFFER.  Start searching at position START, for RANGE
+   characters.  Return the starting position of the match, -1 for no
+   match, or -2 for an internal error.  Also return register
+   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
+extern int re_search
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+            int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+   STRING2.  Also, stop searching at index START + STOP.  */
+extern int re_search_2
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+   in BUFFER matched, starting at position START.  */
+extern int re_match
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+             int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
+extern int re_match_2 
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
+   for recording register information.  STARTS and ENDS must be
+   allocated with malloc, and must each be at least `NUM_REGS * sizeof
+   (regoff_t)' bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+extern void re_set_registers
+  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+             unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+/* 4.2 bsd compatibility.  */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+
+/* POSIX compatibility.  */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+  _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+             regmatch_t pmatch[], int eflags));
+extern size_t regerror
+  _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+             size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+\f
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/Src/diffutils/lib/XMALLOC.C b/Src/diffutils/lib/XMALLOC.C
new file mode 100644 (file)
index 0000000..bce4325
--- /dev/null
@@ -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.  */
+
+#if 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, "virtual 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, "virtual memory exhausted");
+  return p;
+}
diff --git a/Src/diffutils/src/CMP.C b/Src/diffutils/src/CMP.C
new file mode 100644 (file)
index 0000000..907c2f6
--- /dev/null
@@ -0,0 +1,543 @@
+/* cmp -- compare two files.
+   Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Written by Torbjorn Granlund and David MacKenzie. */
+\f
+#include "system.h"
+#include <stdio.h>
+#include "getopt.h"
+#include "cmpbuf.h"
+
+extern char const version_string[];
+
+#if __STDC__ && defined (HAVE_VPRINTF)
+void error (int, int, char const *, ...);
+#else
+void error ();
+#endif
+VOID *xmalloc PARAMS((size_t));
+
+static int cmp PARAMS((void));
+static off_t file_position PARAMS((int));
+static size_t block_compare PARAMS((char const *, char const *));
+static size_t block_compare_and_count PARAMS((char const *, char const *, long *));
+static size_t block_read PARAMS((int, char *, size_t));
+static void printc PARAMS((int, unsigned));
+static void usage PARAMS((char const *));
+
+/* Name under which this program was invoked.  */
+char const *program_name;
+
+/* Filenames of the compared files.  */
+static char const *file[2];
+
+/* File descriptors of the files.  */
+static int file_desc[2];
+
+/* Read buffers for the files.  */
+static char *buffer[2];
+
+/* Optimal block size for the files.  */
+static size_t buf_size;
+
+/* Initial prefix to ignore for each file.  */
+static off_t ignore_initial;
+
+/* Output format:
+   type_first_diff
+     to print the offset and line number of the first differing bytes
+   type_all_diffs
+     to print the (decimal) offsets and (octal) values of all differing bytes
+   type_status
+     to only return an exit status indicating whether the files differ */
+static enum
+  {
+    type_first_diff, type_all_diffs, type_status
+  } comparison_type;
+
+/* If nonzero, print values of bytes quoted like cat -t does. */
+static int opt_print_chars;
+
+static struct option const long_options[] =
+{
+  {"print-chars", 0, 0, 'c'},
+  {"ignore-initial", 1, 0, 'i'},
+  {"verbose", 0, 0, 'l'},
+  {"silent", 0, 0, 's'},
+  {"quiet", 0, 0, 's'},
+  {"version", 0, 0, 'v'},
+  {0, 0, 0, 0}
+};
+\f
+static void
+usage (reason)
+     char const *reason;
+{
+  if (reason)
+    fprintf (stderr, "%s: %s\n", program_name, reason);
+
+  fprintf (stderr, "\
+Usage: %s [-clsv] [-i chars] [--ignore-initial=bytes]\n\
+       [--print-chars] [--quiet] [--silent] [--verbose] [--version]\n\
+       from-file [to-file]\n", program_name);
+
+  exit (2);
+}
+\f
+int
+main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  int c, i, exit_status;
+  struct stat stat_buf[2];
+
+  program_name = argv[0];
+
+  /* Parse command line options.  */
+
+  while ((c = getopt_long (argc, argv, "ci:ls", long_options, 0))
+        != EOF)
+    switch (c)
+      {
+      case 'c':
+       opt_print_chars = 1;
+       break;
+
+      case 'i':
+       ignore_initial = 0;
+       while (*optarg)
+         {
+           /* Don't use `atol', because `off_t' may be longer than `long'.  */
+           unsigned digit = *optarg++ - '0';
+           if (9 < digit)
+             usage ("--ignore-initial value must be a nonnegative integer");
+           ignore_initial = 10 * ignore_initial + digit;
+         }
+       break;
+
+      case 'l':
+       comparison_type = type_all_diffs;
+       break;
+
+      case 's':
+       comparison_type = type_status;
+       break;
+
+      case 'v':
+       fprintf (stderr, "GNU cmp version %s\n", version_string);
+       break;
+
+      default:
+       usage (0);
+      }
+
+  if (optind == argc)
+    usage (0);
+
+  file[0] = argv[optind++];
+  file[1] = optind < argc ? argv[optind++] : "-";
+
+  if (optind < argc)
+    usage ("extra arguments");
+
+  for (i = 0; i < 2; i++)
+    {
+      /* If file[1] is "-", treat it first; this avoids a misdiagnostic if
+        stdin is closed and opening file[0] yields file descriptor 0.  */
+      int i1 = i ^ (strcmp (file[1], "-") == 0);
+
+      /* Two files with the same name are identical.
+        But wait until we open the file once, for proper diagnostics.  */
+      if (i && strcmp (file[0], file[1]) == 0)
+       exit (0);
+
+      if (strcmp (file[i1], "-") == 0)
+       file_desc[i1] = STDIN_FILENO;
+      else
+       {
+         file_desc[i1] = open (file[i1], O_RDONLY);
+         if (file_desc[i1] < 0)
+           {
+             if (comparison_type == type_status)
+               exit (2);
+             else
+               error (2, errno, "%s", file[i1]);
+           }
+       }
+      if (fstat (file_desc[i1], &stat_buf[i1]) != 0)
+       error (2, errno, "%s", file[i1]);
+    }
+
+  /* If the files are links to the same inode and have the same file position,
+     they are identical.  */
+
+  if (stat_buf[0].st_dev == stat_buf[1].st_dev
+      && stat_buf[0].st_ino == stat_buf[1].st_ino
+      && file_position (0) == file_position (1))
+    exit (0);
+
+  /* If output is redirected to "/dev/null", we may assume `-s'.  */
+
+  if (comparison_type != type_status)
+    {
+      struct stat outstat, nullstat;
+
+      if (fstat (STDOUT_FILENO, &outstat) == 0
+         && stat ("/dev/null", &nullstat) == 0
+         && outstat.st_dev == nullstat.st_dev
+         && outstat.st_ino == nullstat.st_ino)
+       comparison_type = type_status;
+    }
+
+  /* If only a return code is needed,
+     and if both input descriptors are associated with plain files,
+     conclude that the files differ if they have different sizes.  */
+
+  if (comparison_type == type_status
+      && S_ISREG (stat_buf[0].st_mode)
+      && S_ISREG (stat_buf[1].st_mode))
+    {
+      off_t s0 = stat_buf[0].st_size - file_position (0);
+      off_t s1 = stat_buf[1].st_size - file_position (1);
+
+      if (max (0, s0) != max (0, s1))
+       exit (1);
+    }
+
+  /* Get the optimal block size of the files.  */
+
+  buf_size = buffer_lcm (STAT_BLOCKSIZE (stat_buf[0]),
+                        STAT_BLOCKSIZE (stat_buf[1]));
+
+  /* Allocate buffers, with space for sentinels at the end.  */
+
+  for (i = 0; i < 2; i++)
+    buffer[i] = xmalloc (buf_size + sizeof (long));
+
+  exit_status = cmp ();
+
+  for (i = 0; i < 2; i++)
+    if (close (file_desc[i]) != 0)
+      error (2, errno, "%s", file[i]);
+  if (comparison_type != type_status)
+    {
+      if (ferror (stdout))
+       error (2, 0, "write error");
+      else if (fclose (stdout) != 0)
+       error (2, errno, "write error");
+    }
+  exit (exit_status);
+  return exit_status;
+}
+\f
+/* Compare the two files already open on `file_desc[0]' and `file_desc[1]',
+   using `buffer[0]' and `buffer[1]'.
+   Return 0 if identical, 1 if different, >1 if error. */
+
+static int
+cmp ()
+{
+  long line_number = 1;                /* Line number (1...) of first difference. */
+  long char_number = ignore_initial + 1;
+                               /* Offset (1...) in files of 1st difference. */
+  size_t read0, read1;         /* Number of chars read from each file. */
+  size_t first_diff;           /* Offset (0...) in buffers of 1st diff. */
+  size_t smaller;              /* The lesser of `read0' and `read1'. */
+  char *buf0 = buffer[0];
+  char *buf1 = buffer[1];
+  int ret = 0;
+  int i;
+
+  if (ignore_initial)
+    for (i = 0; i < 2; i++)
+      if (file_position (i) == -1)
+       {
+         /* lseek failed; read and discard the ignored initial prefix.  */
+         off_t ig = ignore_initial;
+         do
+           {
+             size_t r = read (file_desc[i], buf0, (size_t) min (ig, buf_size));
+             if (!r)
+               break;
+             if (r == -1)
+               error (2, errno, "%s", file[i]);
+             ig -= r;
+           }
+         while (ig);
+       }
+
+  do
+    {
+      read0 = block_read (file_desc[0], buf0, buf_size);
+      if (read0 == -1)
+       error (2, errno, "%s", file[0]);
+      read1 = block_read (file_desc[1], buf1, buf_size);
+      if (read1 == -1)
+       error (2, errno, "%s", file[1]);
+
+      /* Insert sentinels for the block compare.  */
+
+      buf0[read0] = ~buf1[read0];
+      buf1[read1] = ~buf0[read1];
+
+      /* If the line number should be written for differing files,
+        compare the blocks and count the number of newlines
+        simultaneously.  */
+      first_diff = (comparison_type == type_first_diff
+                   ? block_compare_and_count (buf0, buf1, &line_number)
+                   : block_compare (buf0, buf1));
+
+      char_number += first_diff;
+      smaller = min (read0, read1);
+
+      if (first_diff < smaller)
+       {
+         switch (comparison_type)
+           {
+           case type_first_diff:
+             /* See Posix.2 section 4.10.6.1 for this format.  */
+             printf ("%s %s differ: char %ld, line %ld",
+                     file[0], file[1], char_number, line_number);
+             if (opt_print_chars)
+               {
+                 unsigned char c0 = buf0[first_diff];
+                 unsigned char c1 = buf1[first_diff];
+                 printf (" is %3o ", c0);
+                 printc (0, c0);
+                 printf (" %3o ", c1);
+                 printc (0, c1);
+               }
+             putchar ('\n');
+             /* Fall through. */
+           case type_status:
+             return 1;
+
+           case type_all_diffs:
+             do
+               {
+                 unsigned char c0 = buf0[first_diff];
+                 unsigned char c1 = buf1[first_diff];
+                 if (c0 != c1)
+                   {
+                     if (opt_print_chars)
+                       {
+                         printf ("%6lu %3o ", char_number, c0);
+                         printc (4, c0);
+                         printf (" %3o ", c1);
+                         printc (0, c1);
+                         putchar ('\n');
+                       }
+                     else
+                       /* See Posix.2 section 4.10.6.1 for this format.  */
+                       printf ("%6ld %3o %3o\n", char_number, c0, c1);
+                   }
+                 char_number++;
+                 first_diff++;
+               }
+             while (first_diff < smaller);
+             ret = 1;
+             break;
+           }
+       }
+
+      if (read0 != read1)
+       {
+         if (comparison_type != type_status)
+           /* See Posix.2 section 4.10.6.2 for this format.  */
+           fprintf (stderr, "cmp: EOF on %s\n", file[read1 < read0]);
+
+         return 1;
+       }
+    }
+  while (read0 == buf_size);
+  return ret;
+}
+\f
+/* Compare two blocks of memory P0 and P1 until they differ,
+   and count the number of '\n' occurrences in the common
+   part of P0 and P1.
+   Assumes that P0 and P1 are aligned at long addresses!
+   If the blocks are not guaranteed to be different, put sentinels at the ends
+   of the blocks before calling this function.
+
+   Return the offset of the first byte that differs.
+   Increment *COUNT by the count of '\n' occurrences.  */
+
+static size_t
+block_compare_and_count (p0, p1, count)
+     char const *p0, *p1;
+     long *count;
+{
+  long l;              /* One word from first buffer. */
+  long const *l0, *l1; /* Pointers into each buffer. */
+  char const *c0, *c1; /* Pointers for finding exact address. */
+  long cnt = 0;                /* Number of '\n' occurrences. */
+  long nnnn;           /* Newline, sizeof (long) times.  */
+  int i;
+
+  l0 = (long const *) p0;
+  l1 = (long const *) p1;
+
+  nnnn = 0;
+  for (i = 0; i < sizeof (long); i++)
+    nnnn = (nnnn << CHAR_BIT) | '\n';
+
+  /* Find the rough position of the first difference by reading long ints,
+     not bytes.  */
+
+  while ((l = *l0++) == *l1++)
+    {
+      l ^= nnnn;
+      for (i = 0; i < sizeof (long); i++)
+       {
+         cnt += ! (unsigned char) l;
+         l >>= CHAR_BIT;
+       }
+    }
+
+  /* Find the exact differing position (endianness independent).  */
+
+  c0 = (char const *) (l0 - 1);
+  c1 = (char const *) (l1 - 1);
+  while (*c0 == *c1)
+    {
+      cnt += *c0 == '\n';
+      c0++;
+      c1++;
+    }
+
+  *count += cnt;
+  return c0 - p0;
+}
+\f
+/* Compare two blocks of memory P0 and P1 until they differ.
+   Assumes that P0 and P1 are aligned at long addresses!
+   If the blocks are not guaranteed to be different, put sentinels at the ends
+   of the blocks before calling this function.
+
+   Return the offset of the first byte that differs.  */
+
+static size_t
+block_compare (p0, p1)
+     char const *p0, *p1;
+{
+  long const *l0, *l1;
+  char const *c0, *c1;
+
+  l0 = (long const *) p0;
+  l1 = (long const *) p1;
+
+  /* Find the rough position of the first difference by reading long ints,
+     not bytes.  */
+
+  while (*l0++ == *l1++)
+    ;
+
+  /* Find the exact differing position (endianness independent).  */
+
+  c0 = (char const *) (l0 - 1);
+  c1 = (char const *) (l1 - 1);
+  while (*c0 == *c1)
+    {
+      c0++;
+      c1++;
+    }
+
+  return c0 - p0;
+}
+
+/* Read NCHARS bytes from descriptor FD into BUF.
+   Return the number of characters successfully read.
+   The number returned is always NCHARS unless end-of-file or error.  */
+
+static size_t
+block_read (fd, buf, nchars)
+     int fd;
+     char *buf;
+     size_t nchars;
+{
+  char *bp = buf;
+
+  do
+    {
+      size_t nread = read (fd, bp, nchars);
+      if (nread == -1)
+       return -1;
+      if (nread == 0)
+       break;
+      bp += nread;
+      nchars -= nread;
+    }
+  while (nchars != 0);
+
+  return bp - buf;
+}
+
+/* Print character C, making nonvisible characters
+   visible by quoting like cat -t does.
+   Pad with spaces on the right to WIDTH characters.  */
+
+static void
+printc (width, c)
+     int width;
+     unsigned c;
+{
+  register FILE *fs = stdout;
+
+  if (c >= 128)
+    {
+      putc ('M', fs);
+      putc ('-', fs);
+      c -= 128;
+      width -= 2;
+    }
+  if (c < 32)
+    {
+      putc ('^', fs);
+      c += 64;
+      --width;
+    }
+  else if (c == 127)
+    {
+      putc ('^', fs);
+      c = '?';
+      --width;
+    }
+
+  putc (c, fs);
+  while (--width > 0)
+    putc (' ', fs);
+}
+\f
+/* Position file I to `ignore_initial' bytes from its initial position,
+   and yield its new position.  Don't try more than once.  */
+
+static off_t
+file_position (i)
+     int i;
+{
+  static int positioned[2];
+  static off_t position[2];
+
+  if (! positioned[i])
+    {
+      positioned[i] = 1;
+      position[i] = lseek (file_desc[i], ignore_initial, SEEK_CUR);
+    }
+  return position[i];
+}
diff --git a/Src/diffutils/src/CONTEXT.C b/Src/diffutils/src/CONTEXT.C
new file mode 100644 (file)
index 0000000..e20ff86
--- /dev/null
@@ -0,0 +1,464 @@
+/* Context-format output routines 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 "diff.h"
+
+static struct change *find_hunk PARAMS((struct change *));
+static void find_function PARAMS((struct file_data const *, int, char const HUGE **, 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;
+\f
+/* 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
+    /* See Posix.2 section 4.17.6.1.4 for this format.  */
+    fprintf (outfile, "%s %s\t%s",
+            mark, inf->name, ctime (&inf->stat.st_mtime));
+}
+
+/* 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);
+}
+\f
+/* 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);
+}
+\f
+/* 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 HUGE *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]);
+       }
+    }
+}
+\f
+/* 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);
+}
+\f
+/* 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 HUGE *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;
+       }
+    }
+}
+\f
+/* 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;
+    }
+}
+\f
+/* 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 HUGE **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 HUGE *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/Src/diffutils/src/DIFF.C b/Src/diffutils/src/DIFF.C
new file mode 100644 (file)
index 0000000..ef7ac77
--- /dev/null
@@ -0,0 +1,999 @@
+/* GNU DIFF main routine.
+   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.  */
+
+/* GNU DIFF was written by Mike Haertel, David Hayes,
+   Richard Stallman, Len Tower, and Paul Eggert.  */
+
+#define GDIFF_MAIN
+#include "diff.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 *));
+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 usage PARAMS((void));
+
+/* Nonzero for -r: if comparing two directories,
+   compare their common subdirectories recursively.  */
+
+int recursive;
+
+/* For debugging: don't do discard_confusing_lines.  */
+
+int no_discards;
+
+/* 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;
+}
+\f
+/* 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;
+}
+\f
+/* 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 = (char *)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);
+}
+\f
+/* 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'},
+  {"binary", 0, 0, 'q'},       /* An alias, no longer recommended */
+  {"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},
+  {0, 0, 0, 0}
+};
+
+/*
+int
+main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  int val;
+  int c;
+  int prev = -1;
+  int width = DEFAULT_WIDTH;
+
+  // Do our initializations.  
+  program = argv[0];
+  output_style = OUTPUT_NORMAL;
+  context = -1;
+  line_end_char = '\n';
+
+  // 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;
+         setmode(0, O_BINARY);
+         setmode(1, O_BINARY);
+
+         break;
+
+       case 'b':
+         // Ignore changes in amount of white space.  
+         ignore_space_change_flag = 1;
+         length_varies = 1;
+         ignore_some_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;
+         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;
+         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.  
+         specify_style (OUTPUT_CONTEXT);
+         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':
+         fprintf (stderr, "GNU diff version %s\n", version_string);
+         break;
+
+       case 'w':
+         // Ignore horizontal white space when comparing lines.  
+         ignore_all_space_flag = 1;
+         ignore_some_changes = 1;
+         length_varies = 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;
+
+       default:
+         usage ();
+       }
+      prev = c;
+    }
+
+  if (optind != argc - 2)
+    usage ();
+
+
+  {
+    //
+    //*        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 (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)
+    {
+      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 ();
+
+  if (ferror (stdout) || fclose (stdout) != 0)
+    fatal ("write error");
+  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
+usage ()
+{
+  fprintf (stderr, "Usage: %s [options] from-file to-file\n", program);
+  fprintf (stderr, "Options:\n\
+       [-abBcdefhHilnNpPqrstTuvwy] [-C lines] [-D name] [-F regexp]\n\
+       [-I regexp] [-L from-label [-L to-label]] [-S starting-file] [-U lines]\n\
+       [-W columns] [-x pattern] [-X pattern-file]\n");
+  fprintf (stderr, "\
+       [--brief] [--changed-group-format=format] [--context[=lines]] [--ed]\n\
+       [--exclude=pattern] [--exclude-from=pattern-file] [--expand-tabs]\n\
+       [--forward-ed] [--horizon-lines=lines] [--ifdef=name]\n\
+       [--ignore-all-space] [--ignore-blank-lines] [--ignore-case]\n");
+  fprintf (stderr, "\
+       [--ignore-matching-lines=regexp] [--ignore-space-change]\n\
+       [--initial-tab] [--label=from-label [--label=to-label]]\n\
+       [--left-column] [--minimal] [--new-file] [--new-group-format=format]\n\
+       [--new-line-format=format] [--old-group-format=format]\n");
+  fprintf (stderr, "\
+       [--old-line-format=format] [--paginate] [--rcs] [--recursive]\n\
+       [--report-identical-files] [--sdiff-merge-assist] [--show-c-function]\n\
+       [--show-function-line=regexp] [--side-by-side] [--speed-large-files]\n\
+       [--starting-file=starting-file] [--suppress-common-lines] [--text]\n");
+  fprintf (stderr, "\
+       [--unchanged-group-format=format] [--unchanged-line-format=format]\n\
+       [--unidirectional-new-file] [--unified[=lines]] [--version]\n\
+       [--width=columns]\n");
+  exit (2);
+}
+
+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;
+}
+\f
+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 popular file types */
+  /* S_ISLNK is impossible with `stat'.  */
+#ifdef S_ISSOCK
+  if (S_ISSOCK (st->st_mode)) return "socket";
+#endif
+
+  return "weird file";
+}
+\f
+/* 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.  */
+
+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;
+    }
+
+  /* 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++)
+    {
+      bzero (&inf[i].stat, sizeof (struct stat));
+      inf[i].dir_p = 0;
+
+      if (inf[i].desc != -1)
+       {
+         int stat_result;
+
+         if (i && strcmp (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 = strrchr (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;
+
+    }
+#if defined(__MSDOS__) || defined(__NT__) || defined(WIN32)
+  else if (same_files = 0) /* yes, only ONE equal sign intended! hmo11apr93 */
+    ;
+#else
+  else if ((same_files =    inf[0].stat.st_ino == inf[1].stat.st_ino
+                        && inf[0].stat.st_dev == inf[1].stat.st_dev
+                        && inf[0].stat.st_size == inf[1].stat.st_size
+                        && inf[0].desc != -1
+                        && inf[1].desc != -1)
+          && 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;
+    }
+#endif /*__MSDOS__||__NT__*/
+  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.  */
+      int o_binary = always_text_flag ? O_BINARY : 0;
+      /* Open the files and record their descriptors.  */
+
+      if (inf[0].desc == -2)
+       if ((inf[0].desc = open (inf[0].name, O_RDONLY|o_binary, 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|o_binary, 0)) < 0)
+         {
+           perror_with_name (inf[1].name);
+           failed = 1;
+         }
+
+      /* 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/Src/diffutils/src/DIFF.H b/Src/diffutils/src/DIFF.H
new file mode 100644 (file)
index 0000000..18ee9e0
--- /dev/null
@@ -0,0 +1,358 @@
+/* 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 <ctype.h>
+#include <stdio.h>
+#include "regex.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef PR_FILE_NAME
+#define PR_FILE_NAME "/bin/pr"
+#endif
+
+#define TAB_WIDTH 8
+\f
+/* 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 lengths are different.
+   This depends on various options.  */
+EXTERN int      length_varies;
+
+/* 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;
+
+/* character that ends a line.  Currently this is always `\n'.  */
+EXTERN char     line_end_char;
+
+/* 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;
+\f
+/* 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 */
+};
+\f
+/* 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 HUGE *            buffer;
+    /* Allocated size of buffer.  */
+    FSIZE          bufsize;
+    /* Number of valid characters now in the buffer. */
+    FSIZE          buffered_chars;
+
+    /* Array of pointers to lines in the file.  */
+    char const HUGE **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 HUGE *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 HUGE *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];
+\f
+/* Stdio stream to output diffs to.  */
+
+EXTERN FILE *outfile;
+\f
+/* Declare various functions.  */
+
+/* analyze.c */
+struct change * 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 (CDiffContext*);
+
+/* 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 HUGE *, size_t, char const HUGE *, size_t));
+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 HUGE *, char const HUGE *, 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 HUGE * 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 *));
+void cleanup_file_buffers(struct file_data fd[]);
+int FileIsBinary(int fd);
+
+/* version.c */
+extern char const version_string[];
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/Src/diffutils/src/DIR.C b/Src/diffutils/src/DIR.C
new file mode 100644 (file)
index 0000000..5c30d92
--- /dev/null
@@ -0,0 +1,216 @@
+/* Read, sort and compare two directories.  Used 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.  */
+
+#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;
+
+         /* 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;
+
+         d_size = strlen (d_name) + 1;
+         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 VOID_CLOSEDIR
+      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 stricmp (* (char const *const *) file1, * (char const *const *) file2);
+}
+\f
+/* 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)
+    {
+      char const * const *names0 = dirdata[0].names;
+      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 && stricmp (*names0, dir_start_file) < 0)
+           names0++;
+         while (*names1 && stricmp (*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
+                          : stricmp (*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/Src/diffutils/src/DIRENT.C b/Src/diffutils/src/DIRENT.C
new file mode 100644 (file)
index 0000000..9914c3d
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ *  dirent.c - POSIX directory access routines for MS-DOS, OS/2 and Windows/NT
+ *
+ *  Author: Frank Whaley (few@autodesk.com)
+ *
+ *  Copyright Frank Whaley 1993.  All rights reserved.
+ *
+ *  Permission to use, copy, modify, distribute, and sell this software
+ *  and its documentation for any purpose is hereby granted without fee,
+ *  provided that the above copyright notice appears in all copies of the
+ *  source code.  The name of the author may not be used to endorse or
+ *  promote products derived from this software without specific prior
+ *  written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *  CAVEATS:
+ *    The associated 'dirent.h' file should be copied into your system
+ *    include directory if the '#include <dirent.h>' syntax will be used,
+ *    otherwise a '-I.' switch must be added to command lines.
+ *
+ *    This code was originally developed with Turbo C, and continues to
+ *    use TC's function and structure names.  Numerous macros make the
+ *    code palatable to MSC 5.1/6.0 for MS-DOS and OS/2.  The macros
+ *    depend on four defines: __TURBOC__, __MSC__, __MSDOS__, and __OS2__.
+ *    The TC and BC compilers provide __TURBOC__ and __MSDOS__ as
+ *    appropriate; MSC doesn't provide any of these flags so they must
+ *    be given on the command line.  Sample commands for building test
+ *    programs (see '#ifdef TEST' below):
+ *      tcc -DTEST dirent.c
+ *      bcc -DTEST dirent.c
+ *      cl -DTEST -D__MSC__ -D__MSDOS__ dirent.c
+ *      cl -Lp -DTEST -D__MSC__ -D__OS2__ dirent.c
+ *      cl -DTEST -D__MSC__ -D__NT__ dirent.c
+ *
+ *    This code reads an entire directory into memory, and thus is not
+ *    a good choice for scanning very large directories.  The maximum
+ *    number of names allowed is controlled by MAXNAMES, defined below.
+ *    This value is used to allocate an array of pointers, so making it
+ *    ridiculously large may cause the code to fail silently.  The array
+ *    of pointers could be realloc()'ed in the loadDir() function, but
+ *    this can be dangerous with some 'weak' memory allocation packages.
+ *
+ *    POSIX requires that the rewinddir() function re-scan the directory,
+ *    so this code must preserve the original directory name.  If the
+ *    name given is a relative path (".", "..", etc.) and the current
+ *    directory is changed between opendir() and rewinddir(), a different
+ *    directory will be scanned by rewinddir().  (The directory name
+ *    could be "qualified" by opendir(), but this process yields unusable
+ *    names for network drives).
+ *
+ *    This code provides only file names, as that is all that is required
+ *    by POSIX.  Considerable other information is available from the
+ *    MS-DOS and OS/2 directory search functions.  This package should not
+ *    be considered as a general-purpose directory scanner, but rather as
+ *    a tool to simplify porting other programs.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <io.h>
+#include <string.h>
+#include <dirent.h>
+
+#define MAXNAMES       1024            /*  max names in dir list  */
+#define MAXLEN         260             /*  max len of any path/filename  */
+
+#define NULLP(type)    ((type *)0)     /*  readability macro  */
+
+#ifdef __TURBOC__
+#include <alloc.h>
+#include <dir.h>
+#include <mem.h>
+typedef struct ffblk   FIND_T;
+#define findclose(f)
+#define FA_RDONLY      0x01
+#define FA_HIDDEN      0x02
+#define FA_SYSTEM      0x04
+#define FA_LABEL       0x08
+#define FA_DIREC       0x10
+#define FA_ARCH                0x20
+#endif /*__TURBOC__*/
+
+#if defined(__MSC__) && defined(__MSDOS__)
+#include <dos.h>
+#include <malloc.h>
+#include <memory.h>
+typedef struct find_t  FIND_T;
+#define findfirst(n,f,a)       _dos_findfirst(n,a,f)
+#define findnext(f)            _dos_findnext(f)
+#define findclose(f)
+#define ff_name                        name
+#define FA_RDONLY              _A_RDONLY
+#define FA_HIDDEN              _A_HIDDEN
+#define FA_SYSTEM              _A_SYSTEM
+#define FA_LABEL               _A_VOLID
+#define FA_DIREC               _A_SUBDIR
+#define FA_ARCH                        _A_ARCH
+#endif /*__MSC__&&__MSDOS__*/
+
+#if defined(__MSC__) && defined(__OS2__)
+#define INCL_DOS
+#include <os2.h>
+#include <malloc.h>
+#include <memory.h>
+typedef struct
+{
+       HDIR ft_handle;
+       FILEFINDBUF ft_ffb;
+       int ft_count;
+} FIND_T;
+#define findfirst(n,f,a)       ((f)->ft_handle=0xFFFF,(f)->ft_count=1,\
+                                       DosFindFirst(n,&(f)->ft_handle,a,\
+                                       &(f)->ft_ffb,sizeof((f)->ft_ffb),\
+                                       &(f)->ft_count,0L))
+#define findnext(f)            DosFindNext((f)->ft_handle,&(f)->ft_ffb,\
+                                       sizeof((f)->ft_ffb),&(f)->ft_count)
+#define findclose(f)           DosFindClose((f)->ft_handle);
+#define ff_name                        ft_ffb.achName
+#define FA_RDONLY              0x01
+#define FA_HIDDEN              0x02
+#define FA_SYSTEM              0x04
+#define FA_LABEL               0x08
+#define FA_DIREC               0x10
+#define FA_ARCH                        0x20
+#endif /*__MSC__&&__OS2__*/
+
+#if defined(__MSC__) && defined(__NT__)
+#include <windows.h>
+typedef struct
+{
+       long ft_hdl;
+       struct _finddata_t ft_ffb;
+} FIND_T;
+
+#define findfirst(n,f,a)       (((f)->ft_hdl=_findfirst(n,&(f)->ft_ffb))==-1)
+#define findnext(f)            _findnext((f)->ft_hdl,&(f)->ft_ffb)
+#define findclose(f)           _findclose((f)->ft_hdl)
+#define ff_name                        ft_ffb.name
+#define FA_RDONLY              0x01
+#define FA_HIDDEN              0x02
+#define FA_SYSTEM              0x04
+#define FA_LABEL               0x08
+#define FA_DIREC               0x10
+#define FA_ARCH                        0x20
+#endif /*__MSC__&&__NT__*/
+
+       /*  mask for all interesting files  */
+#define ALL    (FA_RDONLY+FA_HIDDEN+FA_SYSTEM+FA_DIREC)
+
+typedef struct __DIRENT
+{
+       char path[MAXLEN];      /*  directory name  */
+       char **names;           /*  array of ptrs to names  */
+       int count;              /*  number of entries  */
+       int current;            /*  current entry  */
+} DIRENT;
+
+       /*  forward declarations  */
+static int loadDir(DIRENT *dir);
+
+/*
+-*     opendir - open a directory for reading
+ */
+       DIR *
+opendir(char const *name)
+{
+       DIRENT *dir;
+
+       /*  worth looking at ??  */
+       if ( (name == NULL) || (*name == '\0') )
+       {
+               errno = ENOENT;
+               return ( NULLP(DIR) );
+       }
+
+       /*  get space for DIRENT struct  */
+       if ( (dir = malloc(sizeof(DIRENT))) == NULLP(DIRENT) )
+       {
+               errno = ENOMEM;
+               return ( NULLP(DIR) );
+       }
+
+       /*  load the names  */
+       strcpy(dir->path, name);
+       if ( !loadDir(dir) )
+       {
+               free(dir);
+               /*  errno already set  */
+               return ( NULLP(DIR) );
+       }
+
+       return ( (DIR *)dir );
+}
+
+/*
+-*     closedir - close a directory
+ */
+       int
+closedir(DIR *dir)
+{
+       char **names = ((DIRENT *)dir)->names;
+       int count = ((DIRENT *)dir)->count;
+
+       while ( count )
+               free(names[--count]);
+       free(names);
+       free(dir);
+       return ( 0 );
+}
+
+/*
+-*     readdir - return ptr to next directory entry
+ */
+       struct dirent *
+readdir(DIR *dir)
+{
+       static struct dirent dp;
+       DIRENT *de = (DIRENT *)dir;
+
+       if ( de->current >= de->count )
+               return ( NULLP(struct dirent) );
+
+       strcpy(dp.d_name, de->names[de->current++]);
+       return ( &dp );
+}
+
+/*
+-*     rewinddir - rewind directory (re-open)
+ */
+       void
+rewinddir(DIR *dir)
+{
+       char **names = ((DIRENT *)dir)->names;
+       int count = ((DIRENT *)dir)->count;
+
+       /*  free existing names  */
+       while ( count )
+               free(names[--count]);
+       free(names);
+       /*  reload  */
+       loadDir((DIRENT *)dir);
+}
+
+/*
+-*     __seekdir - change directory position
+ */
+       void
+__seekdir(DIR *dir, long off)
+{
+       DIRENT *de = (DIRENT *)dir;
+
+       if ( (off < 0) || (off > de->count) )
+               return;
+       de->current = (int)off;
+}
+
+/*
+-*     __telldir - return current directory position
+ */
+       long
+__telldir(DIR *dir)
+{
+       return ( (long)((DIRENT *)dir)->current );
+}
+
+       /*  LOCAL ROUTINES  */
+/*  load a directory list  */
+       int
+loadDir(DIRENT *dir)
+{
+       char pattern[MAXLEN];
+       char **names;
+       int count = 0;
+       int mode;
+       FIND_T ff;
+
+       /*  do we have just a drive name ??  */
+       if ( (dir->path[1] == ':') && (dir->path[2] == '\0') )
+               strcat(dir->path, ".");
+
+       /*  is it a directory ??  */
+#ifdef __MSDOS__
+#ifdef __TURBOC__
+       if ( ((mode = _chmod(dir->path, 0)) < 0) ||
+#endif /*__TURBOC__*/
+#ifdef __MSC__
+       if ( _dos_getfileattr(dir->path, &mode) ||
+#endif /*__MSC__*/
+#endif /*__MSDOS__*/
+#ifdef __OS2__
+       if ( DosQFileMode(dir->path, &mode, 0L) ||
+#endif /*__OS2__*/
+#ifdef __NT__
+       if ( ((mode = GetFileAttributes(dir->path)) == 0xFFFFFFFF) ||
+#endif /*__NT__*/
+            !(mode & FA_DIREC) )
+       {
+               errno = ENOTDIR;
+               return ( 0 );
+       }
+
+       /*  get space for array of ptrs  */
+       if ( (names = (char **)malloc(MAXNAMES * sizeof(char *))) ==
+                                                               NULLP(char *) )
+       {
+               errno = ENOMEM;
+               return ( 0 );
+       }
+
+       /*  build pattern string  */
+       strcpy(pattern, dir->path);
+       if ( strchr("\\/:", pattern[strlen(pattern) - 1]) == NULL )
+               strcat(pattern, "/");
+       strcat(pattern, "*.*");
+
+       if ( !findfirst(pattern, &ff, ALL) )
+               do
+               {
+                       /*  add name if not "." or ".."  */
+                       if ( ff.ff_name[0] != '.' )
+                       {
+                               /*  make a copy of the name  */
+                               if ( (names[count] = strdup(ff.ff_name))
+                                                                    == NULL )
+                               {
+                                       /*  free all if error (out of mem)  */
+                                       while ( count )
+                                               free(names[--count]);
+                                       free(names);
+                                       errno = ENOMEM;
+                                       return ( 0 );
+                               }
+                               count++;
+                       }
+               }
+               while ( !findnext(&ff) && (count < MAXNAMES) );
+       findclose(&ff);
+       dir->names = names;
+       dir->count = count;
+       dir->current = 0;
+       return ( 1 );
+}
+
+#ifdef TEST
+       int
+main(int argc, char *argv[])
+{
+       DIR *dir;
+       struct dirent *d;
+       long pos;
+
+       /*  check arguments  */
+       if ( argc != 2 )
+       {
+               fprintf(stderr, "Usage: dirent <directory>\n");
+               return ( 1 );
+       }
+
+       /*  try to open the given directory  */
+       if ( (dir = opendir(argv[1])) == (DIR *)0 )
+       {
+               fprintf(stderr, "cannot open %s\n", argv[1]);
+               return ( 1 );
+       }
+
+       /*  walk the directory once forward  */
+       while ( (d = readdir(dir)) != NULLP(struct dirent) )
+               printf("%s\n", d->d_name);
+
+       /*  rewind  */
+       rewinddir(dir);
+
+       /*  scan to the end again  */
+       while ( (d = readdir(dir)) != NULLP(struct dirent) )
+               ;
+
+       /*  seek backwards to beginning  */
+       for ( pos = __telldir(dir); pos >= 0; pos-- )
+       {
+               __seekdir(dir, pos);
+               printf("%ld=%ld\n", __telldir(dir), pos);
+       }
+
+       /*  close and exit  */
+       printf("closedir() returns %d\n", closedir(dir));
+       return ( 0 );
+}
+#endif /*TEST*/
+
+/*  END of dirent.c  */
diff --git a/Src/diffutils/src/DIRENT.H b/Src/diffutils/src/DIRENT.H
new file mode 100644 (file)
index 0000000..fcc9ef7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  dirent.h - POSIX directory access routines for MS-DOS, OS/2 and Windows/NT
+ *
+ *  Author: Frank Whaley (few@autodesk.com)
+ *
+ *  Copyright Frank Whaley 1993.  All rights reserved.
+ *
+ *  Permission to use, copy, modify, distribute, and sell this software
+ *  and its documentation for any purpose is hereby granted without fee,
+ *  provided that the above copyright notice appears in all copies of the
+ *  source code.  The name of the author may not be used to endorse or
+ *  promote products derived from this software without specific prior
+ *  written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ *  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *  Read the "CAVEATS" section in dirent.c.
+ */
+
+#ifndef __DIRENT_H
+#define __DIRENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__OS2__) || defined(__NT__)
+#define d_MAXNAMLEN 255
+#else
+#define d_MAXNAMLEN 12
+#endif
+
+typedef struct __DIRENT DIR;
+
+struct dirent
+{
+ char d_name[d_MAXNAMLEN + 1];
+};
+
+DIR *opendir(char const *__name);
+struct dirent *readdir(DIR *__dir);
+void rewinddir(DIR *__dir);
+int closedir(DIR *__dir);
+
+void __seekdir(DIR *__dir, long __pos);
+long __telldir(DIR *__dir);
+
+#ifndef _POSIX_SOURCE
+#define seekdir __seekdir
+#define telldir __telldir
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__DIRENT_H*/
+
+/*  END of dirent.h  */
diff --git a/Src/diffutils/src/ED.C b/Src/diffutils/src/ED.C
new file mode 100644 (file)
index 0000000..717ef35
--- /dev/null
@@ -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 *));
+\f
+/* 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");
+    }
+}
+\f
+/* 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");
+}
+\f
+/* 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/Src/diffutils/src/IFDEF.C b/Src/diffutils/src/IFDEF.C
new file mode 100644 (file)
index 0000000..22669c3
--- /dev/null
@@ -0,0 +1,428 @@
+/* #ifdef-format output routines for GNU DIFF.
+   Copyright (C) 1989, 91, 92, 93 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 HUGE * 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/Src/diffutils/src/IO.C b/Src/diffutils/src/IO.C
new file mode 100644 (file)
index 0000000..4f42de8
--- /dev/null
@@ -0,0 +1,717 @@
+/* File I/O 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.  */
+
+#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.  */
+typedef unsigned word;
+\f
+/* 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 HUGE *line;       /* A line that fits this class. */
+  size_t length;       /* The length of that line.  */
+};
+
+/* Hash-table: array of buckets, each being a chain of equivalence classes.  */
+static int *buckets;
+  
+/* Number of buckets in the hash table array. */
+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 HUGE *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 *));
+\f
+/* 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) (size != 0 && 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->buffer = xmalloc (sizeof (word));
+      current->bufsize = sizeof (word);
+      current->buffered_chars = 0;
+    }
+  else
+    {
+      current->bufsize = current->buffered_chars
+       = STAT_BLOCKSIZE (current->stat);
+
+      if (S_ISREG (current->stat.st_mode))
+       /* Get the size out of the stat block.
+          Allocate enough room for appended newline and sentinel.
+          Allocate at least one block, to prevent overrunning the buffer
+          when comparing growing binary files.  */
+       current->bufsize = max (current->bufsize,
+                               current->stat.st_size + sizeof (word) + 1);
+
+#ifdef __MSDOS__
+      if ((current->buffer = (char HUGE *) farmalloc (current->bufsize)) == NULL)
+       fatal ("far memory exhausted");
+#else
+      current->buffer = xmalloc (current->bufsize);
+#endif /*__MSDOS__*/
+
+      if (skip_test)
+       current->buffered_chars = 0;
+      else
+       {
+         /* Check first part of file to see if it's a binary file.  */
+         current->buffered_chars = read (current->desc,
+                                         current->buffer,
+#ifdef __MSDOS__
+                       (unsigned int)
+#endif /*__MSDOS__*/
+                                         current->buffered_chars);
+         if (current->buffered_chars == -1)
+           pfatal_with_name (current->name);
+         return binary_file_p (current->buffer, current->buffered_chars);
+       }
+    }
+  
+  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.  */
+#ifdef __MSDOS__
+      /* read inside of segment boundaries */
+      while ((current->stat.st_size - current->buffered_chars) > 0)
+      {
+        unsigned nr;
+        char HUGE *rp;
+
+        rp = current->buffer + current->buffered_chars;
+        nr = 0 - (unsigned)((long)rp & 0x0000FFFF);
+        if (nr == 0)
+          nr = 0xFFF0;
+        nr = read (current->desc, rp, nr);
+        if (nr == 0)
+          break;
+        if (nr == 0xFFFF)
+          pfatal_with_name (current->name);
+        current->buffered_chars += nr ;
+      }
+#else
+      cc = current->stat.st_size - current->buffered_chars;
+      if (cc)
+       {
+         cc = read (current->desc,
+                    current->buffer + current->buffered_chars,
+                    cc);
+         if (cc == -1)
+           pfatal_with_name (current->name);
+         current->buffered_chars += cc;
+       }
+#endif /*__MSDOS__*/
+    }
+  /* 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)
+           {
+#ifdef __MSDOS__
+             current->bufsize += 4096;
+             current->buffer = (char HUGE *) farrealloc (current->buffer, current->bufsize);
+#else
+             current->bufsize = current->bufsize * 2;
+             current->buffer = xrealloc (current->buffer, current->bufsize);
+#endif /*__MSDOS__*/
+           }
+         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;
+       }
+#ifndef __MSDOS__
+      /* Allocate just enough room for appended newline and sentinel.  */
+      current->bufsize = current->buffered_chars + sizeof (word) + 1;
+      current->buffer = xrealloc (current->buffer, current->bufsize);
+#endif /*!__MSDOS__*/
+    }
+}
+\f
+/* 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 HUGE *p = (unsigned char const HUGE *) 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 HUGE **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 HUGE *eqs = equivs;
+  int eqs_index = equivs_index;
+  int eqs_alloc = equivs_alloc;
+  char const HUGE *suffix_begin = current->suffix_begin;
+  char const HUGE *bufend = current->buffer + current->buffered_chars;
+  char const HUGE *incomplete_tail
+    = current->missing_newline && ROBUST_OUTPUT_STYLE (output_style)
+      ? bufend : (char const HUGE *) 0;
+  int varies = length_varies;
+
+  while ((char const HUGE *) p < suffix_begin)
+    {
+      char const HUGE *ip = (char const HUGE *) 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))
+                 {
+                   while (isspace (c = *p++))
+                     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))
+                 {
+                   while (isspace (c = *p++))
+                     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 HUGE *) p - ip - ((char const HUGE *) p == incomplete_tail);
+      for (i = *bucket;  ;  i = eqs[i].next)
+       if (!i)
+         {
+           /* Create a new equivalence class in this bucket. */
+           i = eqs_index++;
+           if (i == eqs_alloc)
+#ifdef __MSDOS__
+          if ((eqs = (struct equivclass HUGE *) farrealloc (eqs, (long) (eqs_alloc*=2) * sizeof(*eqs))) == NULL)
+            fatal ("far memory exhausted");
+#else
+             eqs = (struct equivclass *)
+                     xrealloc (eqs, (eqs_alloc*=2) * sizeof(*eqs));
+#endif /*__MSDOS__*/
+           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
+                && (eqs[i].length == length || varies)
+                && ! line_cmp (eqs[i].line, eqs[i].length, ip, length))
+         /* Reuse existing equivalence class.  */
+           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 HUGE **) 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 HUGE **) xrealloc (linbuf + linbuf_base,
+                                            (alloc_lines - linbuf_base)
+                                            * sizeof (*linbuf))
+                  - linbuf_base;
+       }
+      linbuf[line] = (char const HUGE *) p;
+    
+      if ((char const HUGE *) p == bufend)
+       {
+         linbuf[line]  -=  (char const HUGE *) p == incomplete_tail;
+         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;
+}
+\f
+/* 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 unless -B is in effect.  */
+
+static void
+prepare_text_end (current)
+     struct file_data *current;
+{
+  FSIZE buffered_chars = current->buffered_chars;
+  char HUGE *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 = ! ignore_blank_lines_flag;
+    }
+  
+  /* Don't use uninitialized storage when planting or using sentinels.  */
+  if (p)
+    bzero (p + buffered_chars, sizeof (word));
+}
+\f
+/* 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 HUGE *w0, HUGE *w1;
+  char HUGE *p0, HUGE *p1, HUGE *buffer0, HUGE *buffer1;
+  char const HUGE *end0, HUGE *beg0;
+  char const HUGE **linbuf0, HUGE **linbuf1;
+  int i, lines;
+  FSIZE n0, n1;
+  FSIZE tem;
+  FSIZE alloc_lines0, alloc_lines1;
+  int buffered_prefix, prefix_count, prefix_mask;
+  int ttt;
+
+  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 - (char HUGE *)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;
+      ttt = p0 - (char HUGE *)filevec[0].prefix_end;
+      alloc_lines0
+       = prefix_count
+         + GUESS_LINES (0, 0, ttt)
+         + context;
+    }
+  else
+    {
+      prefix_count = 0;
+      prefix_mask = ~0;
+      alloc_lines0 = GUESS_LINES (0, 0, n0);
+    }
+
+  lines = 0;
+  linbuf0 = (char const HUGE **) 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 HUGE **) 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;
+  ttt = filevec[1].prefix_end - buffer1;
+  alloc_lines1
+    = (buffered_prefix
+       + GUESS_LINES (lines, ttt, tem)
+       + context);
+  linbuf1 = (char const HUGE **) 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;
+}
+\f
+/* 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)
+    return 1;
+
+  find_identical_ends (filevec);
+
+  equivs_alloc = filevec[0].alloc_lines + filevec[1].alloc_lines + 1;
+#ifdef __MSDOS__
+  if ((equivs = (struct equivclass HUGE *) farmalloc ((long) equivs_alloc * sizeof(struct equivclass))) == NULL)
+    fatal ("far memory exhausted");
+#else
+  equivs = (struct equivclass *) xmalloc (equivs_alloc * sizeof (struct equivclass));
+#endif /*__MSDOS__*/
+  /* 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 * sizeof (*buckets));
+  bzero (buckets, nbuckets * 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);
+
+  return 0;
+}
diff --git a/Src/diffutils/src/NORMAL.C b/Src/diffutils/src/NORMAL.C
new file mode 100644 (file)
index 0000000..058f6f6
--- /dev/null
@@ -0,0 +1,77 @@
+/* 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;
+  int trans_a, trans_b;
+  //int trans_c, trans_d;
+
+  /* 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");
+
+  //translate_range (&files[0], first0, last0, &trans_a, &trans_b);
+  //translate_range (&files[1], first1, last1, &trans_c, &trans_d);
+  //printf("left=%d,%d   right=%d,%d\n", trans_a, trans_b, trans_c, trans_d);
+
+  /* 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/Src/diffutils/src/SDIFF.C b/Src/diffutils/src/SDIFF.C
new file mode 100644 (file)
index 0000000..b0fa980
--- /dev/null
@@ -0,0 +1,1195 @@
+/* SDIFF -- interactive merge front end to diff
+   Copyright (C) 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.  */
+
+/* GNU SDIFF was written by Thomas Lord. */
+\f
+#include "system.h"
+#include <stdio.h>
+#include <ctype.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
+#define DEFAULT_EDITOR "ed"
+#endif
+
+extern char version_string[];
+static char const *prog;
+static char const *diffbin = DIFF_PROGRAM;
+static char const *edbin = DEFAULT_EDITOR;
+
+static char *tmpname;
+static int volatile tmpmade;
+static pid_t volatile diffpid;
+
+struct line_filter;
+
+static FILE *ck_fdopen PARAMS((int, char const *));
+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((int, char const *, char const *, char const *));
+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 untrapsig PARAMS((int));
+static void usage PARAMS((void));
+
+/* this lossage until the gnu libc conquers the universe */
+#define PVT_tmpdir "/tmp"
+static char *private_tempnam PARAMS((char const *, char const *, int, size_t *));
+static int diraccess PARAMS((char const *));
+static int exists 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'},
+  {0, 0, 0, 0}
+};
+
+/* prints usage message and quits */
+static void
+usage ()
+{
+  fprintf (stderr, "Usage: %s [options] from-file to-file\n", prog);
+  fprintf (stderr, "Options:\n\
+       [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
+       [--text] [--minimal] [--speed-large-files] [--expand-tabs]\n\
+       [--ignore-case] [--ignore-matching-lines=regexp]\n\
+       [--ignore-space-change] [--ignore-blank-lines] [--ignore-all-space]\n\
+       [--suppress-common-lines] [--left-column] [--output=outfile]\n\
+       [--version] [--width=columns]\n");
+  exit (2);
+}
+
+static void
+cleanup ()
+{
+  if (0 < diffpid)
+    kill (diffpid, SIGPIPE);
+  if (tmpmade)
+    unlink (tmpname);
+}
+
+static void
+exiterr ()
+{
+  cleanup ();
+  untrapsig (0);
+  checksigs ();
+  exit (2);
+}
+
+static void
+fatal (msg)
+     char const *msg;
+{
+  fprintf (stderr, "%s: %s\n", prog, msg);
+  exiterr ();
+}
+
+static void
+perror_fatal (msg)
+     char const *msg;
+{
+  int e = errno;
+  checksigs ();
+  fprintf (stderr, "%s: ", prog);
+  errno = e;
+  perror (msg);
+  exiterr ();
+}
+
+
+/* malloc freely or DIE! */
+static VOID *
+xmalloc (size)
+     size_t size;
+{
+  VOID *r = (VOID *) malloc (size);
+  if (!r)
+    fatal ("virtual 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 FILE *
+ck_fdopen (fd, type)
+     int fd;
+     char const *type;
+{
+  FILE *r = fdopen (fd, type);
+  if (!r)
+    perror_fatal ("fdopen");
+  return r;
+}
+
+static void
+ck_fclose (f)
+     FILE *f;
+{
+  if (fclose (f))
+    perror_fatal ("input/output error");
+}
+
+static size_t
+ck_fread (buf, size, f)
+     char *buf;
+     size_t size;
+     FILE *f;
+{
+  size_t r = fread (buf, sizeof (char), size, f);
+  if (r == 0 && ferror (f))
+    perror_fatal ("input error");
+  return r;
+}
+
+static void
+ck_fwrite (buf, size, f)
+     char 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");
+}
+
+#if !HAVE_MEMCHR
+char *
+memchr (s, c, n)
+     char const *s;
+     int c;
+     size_t n;
+{
+  unsigned char const *p = (unsigned char const *) s, *lim = p + n;
+  for (;  p < lim;  p++)
+    if (*p == c)
+      return (char *) p;
+  return 0;
+}
+#endif
+
+#ifndef HAVE_WAITPID
+/* Emulate waitpid well enough for sdiff, which has at most two children.  */
+static pid_t
+waitpid (pid, stat_loc, options)
+     pid_t pid;
+     int *stat_loc;
+     int options;
+{
+  static int ostatus;
+  static pid_t opid;
+  int npid, status;
+
+  if (pid == opid)
+    {
+      opid = 0;
+      status = ostatus;
+    }
+  else
+    while ((npid = wait (&status)) != pid)
+      {
+       if (npid < 0)
+         return npid;
+       opid = npid;
+       ostatus = status;
+      }
+  *stat_loc = status;
+  return pid;
+}
+#endif
+
+static char const *
+expand_name (name, isdir, other_name)
+     char *name;
+     int isdir;
+     char const *other_name;
+{
+  if (strcmp (name, "-") == 0)
+    fatal ("cannot interactively merge standard input");
+  if (!isdir)
+    return name;
+  else
+    {
+      /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
+      char const
+       *p = strrchr (other_name, '/'),
+       *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;
+    }
+}
+
+
+\f
+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;
+    }
+}
+
+\f
+
+int
+main (argc, argv)
+     int argc;
+     char *argv[];
+{
+  int opt;
+  int version_requested = 0;
+  char *editor = getenv ("EDITOR");
+  char *differ = getenv ("DIFF");
+
+  prog = argv[0];
+  if (editor)
+    edbin = editor;
+  if (differ)
+    diffbin = differ;
+
+  diffarg ("diff");
+
+  /* parse command line args */
+  while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
+        != EOF)
+    {
+      switch (opt)
+       {
+       case 'a':
+         diffarg ("-a");
+         break;
+
+       case 'b':
+         diffarg ("-b");
+         break;
+
+       case 'B':
+         diffarg ("-B");
+         break;
+
+       case 'd':
+         diffarg ("-d");
+         break;
+
+       case 'H':
+         diffarg ("-H");
+         break;
+
+       case 'i':
+         diffarg ("-i");
+         break;
+
+       case 'I':
+         diffarg ("-I");
+         diffarg (optarg);
+         break;
+
+       case 'l':
+         diffarg ("--left-column");
+         break;
+
+       case 'o':
+         out_file = optarg;
+         break;
+
+       case 's':
+         suppress_common_flag = 1;
+         break;
+
+       case 't':
+         diffarg ("-t");
+         break;
+
+       case 'v':
+         version_requested = 1;
+         fprintf (stderr, "GNU sdiff version %s\n", version_string);
+         ck_fflush (stderr);
+         break;
+
+       case 'w':
+         diffarg ("-W");
+         diffarg (optarg);
+         break;
+
+       case 'W':
+         diffarg ("-w");
+         break;
+
+       default:
+         usage ();
+       }
+    }
+
+  /* check: did user just want version message? if so exit. */
+  if (version_requested && argc - optind == 0)
+    exit (0);
+
+  if (argc - optind != 2)
+    usage ();
+
+  if (! out_file)
+    /* easy case: diff does everything for us */
+    execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1]);
+  else
+    {
+      FILE *left, *right, *out, *diffout;
+      int diff_fds[2];
+      int interact_ok;
+      pid_t pid;
+      struct line_filter lfilt;
+      struct line_filter rfilt;
+      struct line_filter diff_filt;
+      int leftdir = diraccess (argv[optind]);
+      int rightdir = diraccess (argv[optind + 1]);
+
+      if (leftdir && rightdir)
+       fatal ("both files to be compared are directories");
+
+      left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
+      ;
+      right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
+      out = ck_fopen (out_file, "w");
+
+      if (pipe (diff_fds))
+       perror_fatal ("pipe");
+
+      trapsigs ();
+
+      diffpid = pid = vfork ();
+
+      if (pid == 0)
+       {
+         signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
+         signal (SIGPIPE, SIG_DFL);
+
+         close (diff_fds[0]);
+         if (diff_fds[1] != STDOUT_FILENO)
+           {
+             dup2 (diff_fds[1], STDOUT_FILENO);
+             close (diff_fds[1]);
+           }
+
+         execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1]);
+       }
+
+      if (pid < 0)
+       perror_fatal ("fork failed");
+
+      close (diff_fds[1]);
+      diffout = ck_fdopen (diff_fds[0], "r");
+
+      lf_init (&diff_filt, diffout);
+      lf_init (&lfilt, left);
+      lf_init (&rfilt, right);
+
+      interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
+
+      ck_fclose (diffout);
+      ck_fclose (left);
+      ck_fclose (right);
+      ck_fclose (out);
+
+      {
+       int wstatus;
+
+       while (waitpid (pid, &wstatus, 0) < 0)
+         if (errno == EINTR)
+           checksigs ();
+         else
+           perror_fatal ("wait failed");
+       diffpid = 0;
+
+       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 char const **diffargv;
+
+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 (differences_only, option, file1, file2)
+     int differences_only;
+     char const *option, *file1, *file2;
+{
+  if (differences_only)
+    diffarg ("--suppress-common-lines");
+  diffarg (option);
+  diffarg ("--");
+  diffarg (file1);
+  diffarg (file2);
+  diffarg (0);
+
+  execvp (diffbin, (char **) diffargv);
+  write (STDERR_FILENO, diffbin, strlen (diffbin));
+  write (STDERR_FILENO, ": not found\n", 12);
+  _exit (2);
+}
+
+\f
+
+
+/* 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 */
+  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);
+    }
+}
+
+\f
+
+static void
+give_help ()
+{
+  fprintf (stderr,"l:\tuse the left version\n");
+  fprintf (stderr,"r:\tuse the right version\n");
+  fprintf (stderr,"e l:\tedit then use the left version\n");
+  fprintf (stderr,"e r:\tedit then use the right version\n");
+  fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
+  fprintf (stderr,"e:\tedit a new version\n");
+  fprintf (stderr,"s:\tsilently include common lines\n");
+  fprintf (stderr,"v:\tverbosely include common lines\n");
+  fprintf (stderr,"q:\tquit\n");
+}
+
+static int
+skip_white ()
+{
+  int c;
+  while (isspace (c = getchar ()) && c != '\n')
+    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 (0, "sdiff", 1, 0)))
+           perror_fatal ("temporary file name");
+
+         tmpmade = 1;
+
+         {
+           FILE *tmp = ck_fopen (tmpname, "w+");
+
+           if (cmd1 == 'l' || cmd1 == 'b')
+             lf_copy (left, lenl, tmp);
+           else
+             lf_skip (left, lenl);
+
+           if (cmd1 == 'r' || cmd1 == 'b')
+             lf_copy (right, lenr, tmp);
+           else
+             lf_skip (right, lenr);
+
+           ck_fflush (tmp);
+
+           {
+             pid_t pid;
+             int wstatus;
+
+             ignore_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;
+
+             if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1))
+               fatal ("Subsidiary editor failed");
+           }
+
+           if (fseek (tmp, 0L, SEEK_SET) != 0)
+             perror_fatal ("fseek");
+           {
+             /* SDIFF_BUFSIZE is too big for a local var
+                in some compilers, so we allocate it dynamically.  */
+             char *buf = 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;
+       }
+    }
+}
+
+\f
+
+/* 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);
+}
+
+/* Return nonzero if FILE exists.  */
+static int
+exists (file)
+     char const *file;
+{
+  struct stat buf;
+  return stat (file, &buf) == 0;
+}
+
+/* These are the characters used in temporary filenames.  */
+static char const letters[] =
+  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/* Generate a temporary filename.
+   If DIR_SEARCH is nonzero, DIR and PFX are used as
+   described for tempnam.  If not, a temporary filename
+   in P_tmpdir with no special prefix is generated.  If LENPTR
+   is not 0, *LENPTR is set the to length (including the
+   terminating '\0') of the resultant filename, which is returned.
+   This goes through a cyclic pattern of all possible filenames
+   consisting of five decimal digits of the current pid and three
+   of the characters in `letters'.  Data for tempnam and tmpnam
+   is kept separate, but when tempnam is using P_tmpdir and no
+   prefix (i.e, it is identical to tmpnam), the same data is used.
+   Each potential filename is tested for an already-existing file of
+   the same name, and no name of an existing file will be returned.
+   When the cycle reaches its end (12345ZZZ), 0 is returned.  */
+
+
+static char *
+private_tempnam (dir, pfx, dir_search, lenptr)
+     char const *dir;
+     char const *pfx;
+     int dir_search;
+     size_t *lenptr;
+{
+  static char const tmpdir[] = PVT_tmpdir;
+  static struct
+    {
+      char buf[3];
+      char *s;
+      size_t i;
+    } infos[2], *info;
+  static char *buf;
+  static size_t bufsize = 1;
+  static pid_t oldpid = 0;
+  pid_t pid = getpid ();
+  register size_t len, plen;
+
+  if (dir_search)
+    {
+      register char const *d = getenv ("TMPDIR");
+      if (d && !diraccess (d))
+       d = 0;
+      if (!d && dir && diraccess (dir))
+       d = dir;
+      if (!d && diraccess (tmpdir))
+       d = tmpdir;
+      if (!d && diraccess ("/tmp"))
+       d = "/tmp";
+      if (!d)
+       {
+         errno = ENOENT;
+         return 0;
+       }
+      dir = d;
+    }
+  else
+    dir = tmpdir;
+
+  if (pfx && *pfx)
+    {
+      plen = strlen (pfx);
+      if (plen > 5)
+       plen = 5;
+    }
+  else
+    plen = 0;
+
+  if (dir != tmpdir && !strcmp (dir, tmpdir))
+    dir = tmpdir;
+  info = &infos[(plen == 0 && dir == tmpdir) ? 1 : 0];
+
+  if (pid != oldpid)
+    {
+      oldpid = pid;
+      info->buf[0] = info->buf[1] = info->buf[2] = '0';
+      info->s = &info->buf[0];
+      info->i = 0;
+    }
+
+  len = strlen (dir) + 1 + plen + 8;
+  if (bufsize <= len)
+    {
+      do
+       {
+         bufsize *= 2;
+       }
+      while (bufsize <= len);
+
+      if (buf)
+       free (buf);
+      buf = xmalloc (bufsize);
+    }
+  for (;;)
+    {
+      *info->s = letters[info->i];
+      sprintf (buf, "%s/%.*s%.5lu%.3s", dir, (int) plen, pfx,
+              (unsigned long) pid % 100000, info->buf);
+      if (!exists (buf))
+       break;
+      ++info->i;
+      if (info->i > sizeof (letters) - 1)
+       {
+         info->i = 0;
+         if (info->s == &info->buf[2])
+           {
+             errno = EEXIST;
+             return 0;
+           }
+         ++info->s;
+       }
+    }
+
+  if (lenptr)
+    *lenptr = len;
+  return buf;
+}
diff --git a/Src/diffutils/src/SIDE.C b/Src/diffutils/src/SIDE.C
new file mode 100644 (file)
index 0000000..7ba8f3b
--- /dev/null
@@ -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 HUGE * const *, unsigned, unsigned));
+static unsigned tab_from_to PARAMS((unsigned, unsigned));
+static void print_1sdiff_line PARAMS((char const HUGE * const *, int, char const HUGE * 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 HUGE * const *line;
+     unsigned indent, out_bound;
+{
+  FILE *out = outfile;
+  register unsigned in_position = 0, out_position = 0;
+  register char const
+       HUGE *text_pointer = line[0],
+       HUGE *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 HUGE * const *left;
+     int sep;
+     char const HUGE * 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/Src/diffutils/src/SYSTEM.H b/Src/diffutils/src/SYSTEM.H
new file mode 100644 (file)
index 0000000..a076f77
--- /dev/null
@@ -0,0 +1,237 @@
+/* System dependent declarations.
+   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.  */
+
+/* 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__ || defined(__MSDOS__) || defined(__NT__) || defined(WIN32)
+#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
+
+#ifndef S_IXOTH
+#define S_IXOTH 1
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP (S_IXOTH << 3)
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR (S_IXGRP << 3)
+#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
+#include <sys/file.h>
+#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
+#ifndef _POSIX_VERSION
+/* Prevent the NeXT prototype using union wait from causing problems.  */
+#define wait system_wait
+#endif
+#include <sys/wait.h>
+#ifndef _POSIX_VERSION
+#undef wait
+#endif
+#endif /* HAVE_SYS_WAIT_H */
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#undef WIFEXITED               /* Avoid 4.3BSD incompatibility with Posix.  */
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#if HAVE_ST_BLKSIZE
+#define STAT_BLOCKSIZE(s) (s).st_blksize
+#else
+#define STAT_BLOCKSIZE(s) (8 * 1024)
+#endif
+
+#if DIRENT || defined (_POSIX_VERSION)
+#include <dirent.h>
+#else /* ! (DIRENT || defined (_POSIX_VERSION)) */
+#if SYSNDIR
+#include <sys/ndir.h>
+#else
+#if SYSDIR
+#include <sys/dir.h>
+#else
+#include <ndir.h>
+#endif
+#endif
+#ifdef dirent
+#undef dirent
+#endif
+#define dirent direct
+#endif /* ! (DIRENT || defined (_POSIX_VERSION)) */
+
+#if HAVE_VFORK_H
+#include <vfork.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
+#endif
+#ifndef getenv
+extern 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 HAVE_STRING_H
+#include <string.h>
+#ifndef bzero
+#define bzero(s,n) memset (s,0,n)
+#endif
+#else /* !HAVE_STRING_H */
+#include <strings.h>
+#ifndef strchr
+#define strchr index
+#endif
+#ifndef strrchr
+#define strrchr rindex
+#endif
+#ifndef memcpy
+#define memcpy(d,s,n) bcopy (s,d,n)
+#endif
+#ifndef memcmp
+#define memcmp(s1,s2,n) bcmp (s1,s2,n)
+#endif
+#endif /* !HAVE_STRING_H */
+#if !HAVE_MEMCHR
+char *memchr ();
+#endif
+
+#include <errno.h>
+#if !STDC_HEADERS
+extern int errno;
+#endif
+
+#ifndef min
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+#endif
+
+#ifdef __MSDOS__
+typedef int pid_t;
+typedef long off_t;
+#include <stdio.h>
+#include <malloc.h>
+FILE *popen(char *, char *);
+int pclose(FILE *);
+#define HUGE huge
+#define FSIZE long
+#else
+#define HUGE
+#define FSIZE size_t
+#endif
+
+#ifdef __NT__
+typedef int pid_t;
+#define popen  _popen
+#define pclose _pclose
+#endif
diff --git a/Src/diffutils/src/UTIL.C b/Src/diffutils/src/UTIL.C
new file mode 100644 (file)
index 0000000..7e13d18
--- /dev/null
@@ -0,0 +1,770 @@
+/* Support routines 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.  */
+
+#include "diff.h"
+
+/* 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;
+\f
+/* 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);
+  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);
+  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);
+  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);
+}
+\f
+/* 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);
+}
+\f
+/* 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;
+}
+
+static pid_t pr_pid;
+
+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 are some
+     bugs 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,
+     and it requires two spaces after "diff" if there are no options.
+     These requirements are silly and do not match historical practice.  */
+  sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
+
+  if (paginate_flag)
+    {
+#if defined(__MSDOS__) || defined(__NT__) || defined(WIN32)
+      char command[120];
+
+      sprintf(command, "%s -f -h \"%s\"", PR_FILE_NAME, name);
+      if ((outfile = popen(command, "w")) == NULL)
+        pfatal_with_name ("popen");
+#else
+      int pipes[2];
+
+      /* Fork a `pr' and make OUTFILE a pipe to it.  */
+      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_FILE_NAME, PR_FILE_NAME, "-f", "-h", name, 0);
+         pfatal_with_name (PR_FILE_NAME);
+       }
+      else
+       {
+         close (pipes[0]);
+         outfile = fdopen (pipes[1], "w");
+       }
+#endif /*__MSDOS__||__NT__*/
+    }
+  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)
+    {
+#if defined(__MSDOS__) || defined(__NT__) || defined(WIN32)
+      if (pclose (outfile))
+       pfatal_with_name ("write error");
+#else
+      int wstatus;
+      if (ferror (outfile))
+       fatal ("write error");
+      if (fclose (outfile) != 0)
+       pfatal_with_name ("write error");
+#if HAVE_WAITPID
+      if (waitpid (pr_pid, &wstatus, 0) < 0)
+       pfatal_with_name ("waitpid");
+#else
+      for (;;) {
+       pid_t w = wait (&wstatus);
+       if (w < 0)
+         pfatal_with_name ("wait");
+       if (w == pr_pid)
+         break;
+      }
+#endif
+      if (! WIFEXITED (wstatus) || WEXITSTATUS (wstatus) != 0)
+       fatal ("subsidiary pr failed");
+#endif /*__MSDOS__||__NT__*/
+    }
+
+  outfile = 0;
+}
+\f
+/* Compare two lines (typically one from each input file)
+   according to the command line options.
+   Return 1 if the lines differ, like `memcmp'.  */
+
+int
+line_cmp (s1, len1, s2, len2)
+     char const HUGE *s1, HUGE *s2;
+     size_t len1, len2;
+{
+  register unsigned char const *t1, *t2;
+  register unsigned char end_char = line_end_char;
+
+  /* Check first for exact identity.
+     If that is true, return 0 immediately.
+     This detects the common case of exact identity
+     faster than complete comparison would.  */
+
+  if (len1 == len2 && memcmp (s1, s2, len1) == 0)
+    return 0;
+
+  /* Not exactly identical, but perhaps they match anyway
+     when case or white space is ignored.  */
+
+  if (ignore_case_flag | ignore_space_change_flag | ignore_all_space_flag)
+    {
+      t1 = (unsigned char const *) s1;
+      t2 = (unsigned char const *) s2;
+
+      while (1)
+       {
+         register unsigned char c1 = *t1++;
+         register unsigned char c2 = *t2++;
+
+         /* 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 != end_char) c1 = *t1++;
+             while (isspace (c2) && c2 != end_char) 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 != end_char)
+                   {
+                     c1 = *t1++;
+                     if (! isspace (c1))
+                       {
+                         --t1;
+                         c1 = ' ';
+                         break;
+                       }
+                   }
+               }
+
+             /* Likewise for line 2.  */
+             if (isspace (c2))
+               {
+                 while (c2 != end_char)
+                   {
+                     c2 = *t2++;
+                     if (! isspace (c2))
+                       {
+                         --t2;
+                         c2 = ' ';
+                         break;
+                       }
+                   }
+               }
+           }
+
+         /* Upcase all letters if -i is specified.  */
+
+         if (ignore_case_flag)
+           {
+             if (islower (c1))
+               c1 = toupper (c1);
+             if (islower (c2))
+               c2 = toupper (c2);
+           }
+
+         if (c1 != c2)
+           break;
+         if (c1 == end_char)
+           return 0;
+       }
+    }
+
+  return (1);
+}
+\f
+/* 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;
+}
+\f
+/* 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;
+    }
+}
+\f
+/* 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 HUGE * const *line;
+{
+  char const HUGE *text = line[0], HUGE *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'
+      && line_end_char == '\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 HUGE *text, HUGE *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 HUGE *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';
+}
+\f
+/* 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)
+     char 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);
+}
+\f
+/* 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 HUGE *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 HUGE *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;
+}
+\f
+/* 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)
+#ifdef __MSDOS__
+    fatal ("real memory exhausted");
+#else
+    fatal ("virtual memory exhausted");
+#endif
+  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)
+#ifdef __MSDOS__
+    fatal ("real memory exhausted");
+#else
+    fatal ("virtual memory exhausted");
+#endif
+  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;
+{
+#if defined(__MSDOS__) || defined(__NT__) || defined(WIN32)
+  char sep = dir[strlen(dir) - 1];
+  return concat (dir, "/" + (*dir && ((sep == '/') || (sep == '\\'))), file);
+#else
+  return concat (dir, "/" + (*dir && dir[strlen (dir) - 1] == '/'), file);
+#endif /*__MSDOS__||__NT__*/
+}
+\f
+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);
+}
+
+#if !HAVE_MEMCHR
+char *
+memchr (s, c, n)
+     char const *s;
+     int c;
+     size_t n;
+{
+  unsigned char const *p = (unsigned char const *) s, *lim = p + n;
+  for (;  p < lim;  p++)
+    if (*p == c)
+      return (char *) p;
+  return 0;
+}
+#endif
diff --git a/Src/diffutils/src/analyze.c b/Src/diffutils/src/analyze.c
new file mode 100644 (file)
index 0000000..e34029f
--- /dev/null
@@ -0,0 +1,1112 @@
+/* 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"
+
+int no_discards;
+int need_free_buffers=0;
+
+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;
+       }
+    }
+}
+\f
+/* 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);
+       }
+    }
+}
+\f
+/* 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 (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]);
+}
+\f
+/* 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 (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 *newob = (struct change *) xmalloc (sizeof (struct change));
+
+  newob->line0 = line0;
+  newob->line1 = line1;
+  newob->inserted = inserted;
+  newob->deleted = deleted;
+  newob->link = old;
+  return newob;
+}
+
+/* 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;
+}
+\f
+/* 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. 
+struct change * diff_2_files (struct file_data filevec[], int depth)
+{
+       int diags;
+       int i;
+       struct change *e, *p;
+       struct change *script=NULL;
+       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 = (char *)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 = (char *)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 *thisob, *end;
+                               int first0, last0, first1, last1, deletes, inserts;
+                               
+                               //  Find a set of changes that belong together.  
+                               thisob = 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 thisob hunk is really a difference.  
+                               analyze_hunk (thisob, &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==0 && ignore_blank_lines_flag)
+                       {
+                               // determined that there were no changes after considering flags
+                               for (e = script; e; e = p)
+                               {
+                                       p = e->link;
+                                       free (e);
+                               }
+                               script=NULL;
+                       }
+                       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);
+               
+               
+               /*cleanup the script
+               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 script;
+}
+
+void cleanup_file_buffers(struct file_data fd[])
+{
+       int i;
+       free (fd[0].undiscarded);
+       
+       if (fd[0].changed_flag != NULL)
+               free (fd[0].changed_flag - 1);
+       
+       for (i = 1; i >= 0; --i)
+               free (fd[i].equivs);
+       
+       for (i = 0; i < 2; ++i)
+               free (fd[i].linbuf + fd[i].linbuf_base);
+
+       if (fd[0].buffer != fd[1].buffer)
+               free (fd[0].buffer);
+       free (fd[1].buffer);
+}
diff --git a/Src/res/Merge.ico b/Src/res/Merge.ico
new file mode 100644 (file)
index 0000000..dfeca88
Binary files /dev/null and b/Src/res/Merge.ico differ
diff --git a/Src/res/Merge.rc2 b/Src/res/Merge.rc2
new file mode 100644 (file)
index 0000000..35654dd
--- /dev/null
@@ -0,0 +1,13 @@
+//
+// MERGE.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+       #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Src/res/MergeDoc.ico b/Src/res/MergeDoc.ico
new file mode 100644 (file)
index 0000000..3672482
Binary files /dev/null and b/Src/res/MergeDoc.ico differ
diff --git a/Src/res/Toolbar.bmp b/Src/res/Toolbar.bmp
new file mode 100644 (file)
index 0000000..51d1caf
Binary files /dev/null and b/Src/res/Toolbar.bmp differ
diff --git a/Src/res/binary.bmp b/Src/res/binary.bmp
new file mode 100644 (file)
index 0000000..4f6ce66
Binary files /dev/null and b/Src/res/binary.bmp differ
diff --git a/Src/res/equal.bmp b/Src/res/equal.bmp
new file mode 100644 (file)
index 0000000..9761d55
Binary files /dev/null and b/Src/res/equal.bmp differ
diff --git a/Src/res/notequal.bmp b/Src/res/notequal.bmp
new file mode 100644 (file)
index 0000000..d747084
Binary files /dev/null and b/Src/res/notequal.bmp differ
diff --git a/Src/res/rfolder.bmp b/Src/res/rfolder.bmp
new file mode 100644 (file)
index 0000000..09f8095
Binary files /dev/null and b/Src/res/rfolder.bmp differ
diff --git a/Src/res/splash1.bmp b/Src/res/splash1.bmp
new file mode 100644 (file)
index 0000000..dfa5f80
Binary files /dev/null and b/Src/res/splash1.bmp differ
diff --git a/Src/res/unknown.bmp b/Src/res/unknown.bmp
new file mode 100644 (file)
index 0000000..eddf957
Binary files /dev/null and b/Src/res/unknown.bmp differ
diff --git a/Src/resource.h b/Src/resource.h
new file mode 100644 (file)
index 0000000..6cc788a
--- /dev/null
@@ -0,0 +1,232 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by Merge.rc
+//
+#define IDC_CUT                         3
+#define IDSAVEAS                        3
+#define IDC_COPY                        4
+#define IDC_PASTE                       5
+#define IDC_UNDO                        6
+#define IDD_ABOUTBOX                    100
+#define IDR_POPUP_DIFFVIEW              102
+#define IDB_SPLASH                      103
+#define IDR_POPUP_DIRVIEW               104
+#define IDD_PROP_VSS                    107
+#define IDR_MAINFRAME                   128
+#define IDR_MERGETYPE                   129
+#define IDD_OPEN                        130
+#define IDB_WINMERGE                    130
+#define IDS_VERSION_FMT                 130
+#define IDD_EDITFILE                    131
+#define IDB_OLDSPLASH                   131
+#define IDS_ALLFILES                    131
+#define IDD_VSS                         132
+#define IDS_CONFIRM_ALL_LEFT            132
+#define IDD_PROPPAGE_LARGE              133
+#define IDS_CONFIRM_ALL_RIGHT           133
+#define IDS_COPY2DIR_FMT                134
+#define IDS_CONFIRM_COPY2DIR            135
+#define IDS_FONT_CHANGE                 136
+#define IDS_DIRECTORY_WINDOW_TITLE      137
+#define IDS_DIRECTORY_WINDOW_STATUS_FMT 138
+#define IDS_FILES_ARE_DIFFERENT         139
+#define IDB_LFOLDER                     140
+#define IDS_BIN_FILES_DIFF              140
+#define IDS_ONLY_IN_FMT                 141
+#define IDS_IDENTICAL                   142
+#define IDS_CANT_COMPARE_FILES          143
+#define IDS_SAVE_AS_TITLE               144
+#define IDS_SELECT_FILES_OR_FOLDERS     145
+#define IDS_BACKUP_FAILED_PROMPT        146
+#define IDS_PROPERTIES_TITLE            147
+#define IDS_OPEN_TITLE                  148
+#define IDS_EDIT_WINDOW_TITLE_FMT       150
+#define IDS_FILESSAME                   151
+#define IDS_FILEUNIQUE                  152
+#define IDS_FILEERROR                   153
+#define IDS_SAVEREADONLY_FMT            154
+#define IDS_SAVE_FMT                    155
+#define IDS_FILEBINARY                  156
+#define IDS_SAVEVSS_FMT                 157
+#define IDS_VSSERROR                    158
+#define IDS_NOPROJECT                   159
+#define IDS_FILENAME_HEADER             160
+#define IDS_DIR_HEADER                  161
+#define IDS_RESULT_HEADER               162
+#define IDS_FILE_COMPARISON_TITLE       163
+#define IDB_EQUAL                       213
+#define IDB_NOTEQUAL                    214
+#define IDB_RFOLDER                     215
+#define IDB_UNKNOWN                     216
+#define IDB_BINARY                      217
+#define IDC_LEFT_EDIT                   1000
+#define IDC_FILE_EDIT                   1000
+#define IDC_LEFT_BUTTON                 1001
+#define IDC_RIGHT_EDIT                  1002
+#define IDC_LEFT_COMBO                  1002
+#define IDC_RIGHT_BUTTON                1003
+#define IDC_RIGHT_COMBO                 1004
+#define IDC_PROJECT_COMBO               1004
+#define IDC_MESSAGE                     1005
+#define IDC_EXT_COMBO                   1005
+#define IDC_DOVSS_CHECK                 1006
+#define IDC_PATH_EDIT                   1007
+#define IDC_BROWSE_BUTTON               1008
+#define IDC_BACKUP_CHECK                1009
+#define IDC_SCROLL_CHECK                1010
+#define IDC_VSS_L1                      1010
+#define IDC_WHITESPACE_CHECK            1011
+#define IDC_RECURS_CHECK                1011
+#define IDC_VERSION                     1012
+#define IDC_IGNBLANKS_CHECK             1012
+#define IDC_TAB_EDIT                    1013
+#define IDC_WWW                         1014
+#define IDC_IGNCASE_CHECK               1014
+#define IDC_EMAIL                       1015
+#define IDC_COMPANY                     1016
+#define IDD_LANGUAGE_SELECT             30000
+#define IDS_AFRIKAANS                   30003
+#define IDS_ALBANIAN                    30004
+#define IDS_ARABIC_SAUDI                30005
+#define IDS_ARABIC_IRAQ                 30006
+#define IDS_ARABIC_EGYPT                30007
+#define IDS_ARABIC_LIBYA                30008
+#define IDS_ARABIC_ALGERIA              30009
+#define IDS_ARABIC_MOROCCO              30010
+#define IDS_ARABIC_TUNISIA              30011
+#define IDS_ARABIC_OMAN                 30012
+#define IDS_ARABIC_YEMEN                30013
+#define IDS_ARABIC_SYRIA                30014
+#define IDS_ARABIC_JORDAN               30015
+#define IDS_ARABIC_LEBANON              30016
+#define IDS_ARABIC_KUWAIT               30017
+#define IDS_ARABIC_UAE                  30018
+#define IDS_ARABIC_BAHRAIN              30019
+#define IDS_ARABIC_QATAR                30020
+#define IDS_BASQUE                      30021
+#define IDS_BELARUSIAN                  30022
+#define IDS_BULGARIAN                   30023
+#define IDS_CATALAN                     30024
+#define IDS_CHINESE_TRADITIONAL         30025
+#define IDS_CHINESE_SIMPLIFIED          30026
+#define IDS_CHINESE_HONGKONG            30027
+#define IDS_CHINESE_SINGAPORE           30028
+#define IDS_CROATIAN                    30029
+#define IDS_CZECH                       30030
+#define IDS_DANISH                      30031
+#define IDS_DUTCH                       30032
+#define IDS_ENGLISH_US                  30033
+#define IDS_ENGLISH_UK                  30034
+#define IDS_ENGLISH_AUS                 30035
+#define IDS_ENGLISH_CAN                 30036
+#define IDS_ENGLISH_NZ                  30037
+#define IDS_ENGLISH_EIRE                30038
+#define IDS_ENGLISH_SOUTH_AFRICA        30039
+#define IDS_ENGLISH_JAMAICA             30040
+#define IDS_ENGLISH_CARIBBEAN           30041
+#define IDS_ENGLISH_BELIZE              30042
+#define IDS_ENGLISH_TRINIDAD            30043
+#define IDS_ESTONIAN                    30044
+#define IDS_FAEROESE                    30045
+#define IDS_FARSI                       30046
+#define IDS_FINNISH                     30047
+#define IDS_FRENCH                      30048
+#define IDS_FRENCH_BELGIAN              30049
+#define IDS_FRENCH_CANADIAN             30050
+#define IDS_FRENCH_SWISS                30051
+#define IDS_FRENCH_LUXEMBOURG           30052
+#define IDS_GERMAN                      30053
+#define IDS_GERMAN_SWISS                30054
+#define IDS_GERMAN_AUSTRIAN             30055
+#define IDS_GERMAN_LUXEMBOURG           30056
+#define IDS_GERMAN_LIECHTENSTEIN        30057
+#define IDS_GREEK                       30058
+#define IDS_HEBREW                      30059
+#define IDS_HUNGARIAN                   30060
+#define IDS_ICELANDIC                   30061
+#define IDS_INDONESIAN                  30062
+#define IDS_ITALIAN                     30063
+#define IDS_ITALIAN_SWISS               30064
+#define IDS_JAPANESE                    30065
+#define IDS_KOREAN                      30066
+#define IDS_KOREAN_JOHAB                30067
+#define IDS_LATVIAN                     30068
+#define IDS_LITHUANIAN                  30069
+#define IDS_NORWEGIAN_BOKMAL            30070
+#define IDS_NORWEGIAN_NYNORSK           30071
+#define IDS_POLISH                      30072
+#define IDS_PORTUGUESE                  30073
+#define IDS_PORTUGUESE_BRAZILIAN        30074
+#define IDS_ROMANIAN                    30075
+#define IDS_RUSSIAN                     30076
+#define IDS_SERBIAN_LATIN               30077
+#define IDS_SERBIAN_CYRILLIC            30078
+#define IDS_SLOVAK                      30079
+#define IDS_SLOVENIAN                   30080
+#define IDS_SPANISH                     30081
+#define IDS_SPANISH_MEXICAN             30082
+#define IDS_SPANISH_MODERN              30083
+#define IDS_SPANISH_GUATEMALA           30084
+#define IDS_SPANISH_COSTA_RICA          30085
+#define IDS_SPANISH_PANAMA              30086
+#define IDS_SPANISH_DOMINICAN           30087
+#define IDS_SPANISH_VENEZUELA           30088
+#define IDS_SPANISH_COLOMBIA            30089
+#define IDS_SPANISH_PERU                30090
+#define IDS_SPANISH_ARGENTINA           30091
+#define IDS_SPANISH_ECUADOR             30092
+#define IDS_SPANISH_CHILE               30093
+#define IDS_SPANISH_URUGUAY             30094
+#define IDS_SPANISH_PARAGUAY            30095
+#define IDS_SPANISH_BOLIVIA             30096
+#define IDS_SPANISH_EL_SALVADOR         30097
+#define IDS_SPANISH_HONDURAS            30098
+#define IDS_SPANISH_NICARAGUA           30099
+#define IDS_SPANISH_PUERTO_RICO         30100
+#define IDS_SWEDISH                     30101
+#define IDS_SWEDISH_FINLAND             30102
+#define IDS_THAI                        30103
+#define IDS_TURKISH                     30104
+#define IDS_UKRANIAN                    30105
+#define IDS_VIETNAMESE                  30106
+#define ID_L2R                          32771
+#define ID_R2L                          32772
+#define ID_UNDO                         32773
+#define ID_OPTIONS_SHOWIDENTICAL        32774
+#define ID_OPTIONS_SHOWDIFFERENT        32775
+#define ID_OPTIONS_SHOWUNIQUE           32776
+#define ID_POPUP_COPYTOOTHERSIDE        32777
+#define ID_POPUP_COPYFROMOTHERSIDE      32778
+#define ID_PREVDIFF                     32779
+#define ID_POPUP_SAVE                   32780
+#define ID_NEXTDIFF                     32781
+#define ID_OPTIONS_BACKUPORIGINALFILE   32782
+#define ID_OPTIONS_SCROLLTOFIRSTDIFFERENCE 32784
+#define ID_POPUP_EDITFILE               32787
+#define ID_OPTIONS_IGNOREWHITESPACE     32788
+#define ID_HIDE_BACKUP_FILES            32789
+#define ID_HELP_GNULICENSE              32790
+#define ID_USEVSS                       32791
+#define ID_PROPERTIES                   32792
+#define ID_ALL_LEFT                     32793
+#define ID_ALL_RIGHT                    32794
+#define ID_VIEW_SELECTFONT              32795
+#define ID_VIEW_USEDEFAULTFONT          32796
+#define ID_POPUP_COPYALLDIFFSTOOTHERSIDE 32797
+#define ID_POPUP_COPYALLDIFFSFROMOTHERSIDE 32798
+#define ID_DIR_COPY_FILE_TO_LEFT        32799
+#define ID_DIR_COPY_FILE_TO_RIGHT       32800
+#define ID_VIEW_LANGUAGE                32801
+#define ID_HELP_CONTENTS                32802
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_3D_CONTROLS                     1
+#define _APS_NEXT_RESOURCE_VALUE        134
+#define _APS_NEXT_COMMAND_VALUE         32803
+#define _APS_NEXT_CONTROL_VALUE         1017
+#define _APS_NEXT_SYMED_VALUE           105
+#endif
+#endif
diff --git a/Src/ssauto.h b/Src/ssauto.h
new file mode 100644 (file)
index 0000000..902687a
--- /dev/null
@@ -0,0 +1,600 @@
+/* This header file machine-generated by mktyplib.exe */
+/* Interface to type library: SourceSafeTypeLib */
+
+#ifndef _SourceSafeTypeLib_H_
+#define _SourceSafeTypeLib_H_
+
+DEFINE_GUID(LIBID_SourceSafeTypeLib,0x783CD4E0L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+#ifndef BEGIN_INTERFACE
+#define BEGIN_INTERFACE
+#endif
+
+typedef enum _VSSFlags {
+    VSSFLAG_USERRONO = 1,
+    VSSFLAG_USERROYES = 2,
+    VSSFLAG_TIMENOW = 4,
+    VSSFLAG_TIMEMOD = 8,
+    VSSFLAG_TIMEUPD = 12,
+    VSSFLAG_EOLCR = 16,
+    VSSFLAG_EOLLF = 32,
+    VSSFLAG_EOLCRLF = 48,
+    VSSFLAG_REPASK = 64,
+    VSSFLAG_REPREPLACE = 128,
+    VSSFLAG_REPSKIP = 192,
+    VSSFLAG_REPMERGE = 256,
+    VSSFLAG_CMPFULL = 512,
+    VSSFLAG_CMPTIME = 1024,
+    VSSFLAG_CMPCHKSUM = 1536,
+    VSSFLAG_CMPFAIL = 2048,
+    VSSFLAG_RECURSNO = 4096,
+    VSSFLAG_RECURSYES = 8192,
+    VSSFLAG_FORCEDIRNO = 16384,
+    VSSFLAG_FORCEDIRYES = 32768,
+    VSSFLAG_KEEPNO = 65536,
+    VSSFLAG_KEEPYES = 131072,
+    VSSFLAG_DELNO = 262144,
+    VSSFLAG_DELYES = 524288,
+    VSSFLAG_DELNOREPLACE = 786432,
+    VSSFLAG_BINTEST = 1048576,
+    VSSFLAG_BINBINARY = 2097152,
+    VSSFLAG_BINTEXT = 3145728,
+    VSSFLAG_DELTAYES = 4194304,
+    VSSFLAG_DELTANO = 8388608,
+    VSSFLAG_UPDASK = 16777216,
+    VSSFLAG_UPDUPDATE = 33554432,
+    VSSFLAG_UPDUNCH = 50331648,
+    VSSFLAG_GETYES = 67108864,
+    VSSFLAG_GETNO = 134217728,
+    VSSFLAG_CHKEXCLUSIVEYES = 268435456,
+    VSSFLAG_CHKEXCLUSIVENO = 536870912,
+    VSSFLAG_HISTIGNOREFILES = 1073741824
+} VSSFlags;
+
+typedef enum _VSSFileStatus {
+    VSSFILE_NOTCHECKEDOUT = 0,
+    VSSFILE_CHECKEDOUT = 1,
+    VSSFILE_CHECKEDOUT_ME = 2
+} VSSFileStatus;
+
+typedef enum _VSSItemType {
+    VSSITEM_PROJECT = 0,
+    VSSITEM_FILE = 1
+} VSSItemType;
+
+interface IVSSItems;
+
+interface IVSSVersions;
+
+interface IVSSVersion;
+
+interface IVSSCheckouts;
+
+interface IVSSCheckout;
+
+DEFINE_GUID(IID_IVSSItem,0x783CD4E1L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSItem */
+#undef INTERFACE
+#define INTERFACE IVSSItem
+
+DECLARE_INTERFACE_(IVSSItem, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSSItem methods */
+    STDMETHOD(get_Spec)(THIS_ BSTR FAR* pSpec) PURE;
+    STDMETHOD(get_Binary)(THIS_ VARIANT_BOOL FAR* pbBinary) PURE;
+    STDMETHOD(put_Binary)(THIS_ VARIANT_BOOL bBinary) PURE;
+    STDMETHOD(get_Deleted)(THIS_ VARIANT_BOOL FAR* pbDeleted) PURE;
+    STDMETHOD(put_Deleted)(THIS_ VARIANT_BOOL bDeleted) PURE;
+    STDMETHOD(get_Type)(THIS_ int FAR* piType) PURE;
+    STDMETHOD(get_LocalSpec)(THIS_ BSTR FAR* pLocal) PURE;
+    STDMETHOD(put_LocalSpec)(THIS_ BSTR Local) PURE;
+    STDMETHOD(get_Name)(THIS_ BSTR FAR* pName) PURE;
+    STDMETHOD(put_Name)(THIS_ BSTR Name) PURE;
+    STDMETHOD(get_Parent)(THIS_ IVSSItem FAR* FAR* ppIParent) PURE;
+    STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE;
+    STDMETHOD(get_Items)(THIS_ VARIANT_BOOL IncludeDeleted, IVSSItems FAR* FAR* ppIItems) PURE;
+    STDMETHOD(Get)(THIS_ BSTR FAR* Local, long iFlags) PURE;
+    STDMETHOD(Checkout)(THIS_ BSTR Comment, BSTR Local, long iFlags) PURE;
+    STDMETHOD(Checkin)(THIS_ BSTR Comment, BSTR Local, long iFlags) PURE;
+    STDMETHOD(UndoCheckout)(THIS_ BSTR Local, long iFlags) PURE;
+    STDMETHOD(get_IsCheckedOut)(THIS_ long FAR* piStatus) PURE;
+    STDMETHOD(get_Checkouts)(THIS_ IVSSCheckouts FAR* FAR* ppICheckouts) PURE;
+    STDMETHOD(get_IsDifferent)(THIS_ BSTR Local, VARIANT_BOOL FAR* pbDifferent) PURE;
+    STDMETHOD(Add)(THIS_ BSTR Local, BSTR Comment, long iFlags, IVSSItem FAR* FAR* ppIItem) PURE;
+    STDMETHOD(NewSubproject)(THIS_ BSTR Name, BSTR Comment, IVSSItem FAR* FAR* ppIItem) PURE;
+    STDMETHOD(Share)(THIS_ IVSSItem FAR* pIItem, BSTR Comment, long iFlags) PURE;
+    STDMETHOD(Destroy)(THIS) PURE;
+    STDMETHOD(Move)(THIS_ IVSSItem FAR* pINewParent) PURE;
+    STDMETHOD(Label)(THIS_ BSTR Label, BSTR Comment) PURE;
+    STDMETHOD(get_Versions)(THIS_ long iFlags, IVSSVersions FAR* FAR* pIVersions) PURE;
+    STDMETHOD(get_Version)(THIS_ VARIANT Version, IVSSItem FAR* FAR* ppIItem) PURE;
+};
+
+DEFINE_GUID(IID_IVSSVersions,0x783CD4E7L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSVersions */
+#undef INTERFACE
+#define INTERFACE IVSSVersions
+
+DECLARE_INTERFACE_(IVSSVersions, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSSVersions methods */
+    STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE;
+};
+
+DEFINE_GUID(IID_IVSSVersion,0x783CD4E8L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSVersion */
+#undef INTERFACE
+#define INTERFACE IVSSVersion
+
+DECLARE_INTERFACE_(IVSSVersion, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSSVersion methods */
+    STDMETHOD(get_Username)(THIS_ BSTR FAR* pUsername) PURE;
+    STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE;
+    STDMETHOD(get_Action)(THIS_ BSTR FAR* pAction) PURE;
+    STDMETHOD(get_Date)(THIS_ DATE FAR* pDate) PURE;
+    STDMETHOD(get_Comment)(THIS_ BSTR FAR* pComment) PURE;
+    STDMETHOD(get_Label)(THIS_ BSTR FAR* pLabel) PURE;
+    STDMETHOD(get_VSSItem)(THIS_ IVSSItem FAR* FAR* ppIItem) PURE;
+};
+
+DEFINE_GUID(IID_IVSSItems,0x783CD4E5L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSItems */
+#undef INTERFACE
+#define INTERFACE IVSSItems
+
+DECLARE_INTERFACE_(IVSSItems, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSSItems methods */
+    STDMETHOD(get_Count)(THIS_ long FAR* piCount) PURE;
+    STDMETHOD(get_Item)(THIS_ VARIANT sItem, IVSSItem FAR* FAR* ppIItem) PURE;
+    STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE;
+};
+
+DEFINE_GUID(IID_IVSSCheckouts,0x8903A770L,0xF55F,0x11CF,0x92,0x27,0x00,0xAA,0x00,0xA1,0xEB,0x95);
+
+/* Definition of interface: IVSSCheckouts */
+#undef INTERFACE
+#define INTERFACE IVSSCheckouts
+
+DECLARE_INTERFACE_(IVSSCheckouts, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSSCheckouts methods */
+    STDMETHOD(get_Count)(THIS_ long FAR* piCount) PURE;
+    STDMETHOD(get_Item)(THIS_ VARIANT sItem, IVSSCheckout FAR* FAR* ppICheckout) PURE;
+    STDMETHOD(_NewEnum)(THIS_ IUnknown * FAR* ppIEnum) PURE;
+};
+
+DEFINE_GUID(IID_IVSSCheckout,0x783CD4E6L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSCheckout */
+#undef INTERFACE
+#define INTERFACE IVSSCheckout
+
+DECLARE_INTERFACE_(IVSSCheckout, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSSCheckout methods */
+    STDMETHOD(get_Username)(THIS_ BSTR FAR* pUsername) PURE;
+    STDMETHOD(get_Date)(THIS_ DATE FAR* pDate) PURE;
+    STDMETHOD(get_LocalSpec)(THIS_ BSTR FAR* pLocal) PURE;
+    STDMETHOD(get_Machine)(THIS_ BSTR FAR* pMachine) PURE;
+    STDMETHOD(get_Project)(THIS_ BSTR FAR* pProject) PURE;
+    STDMETHOD(get_Comment)(THIS_ BSTR FAR* pComment) PURE;
+    STDMETHOD(get_VersionNumber)(THIS_ long FAR* piVersion) PURE;
+};
+
+DEFINE_GUID(IID_IVSSDatabase,0x783CD4E2L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSDatabase */
+#undef INTERFACE
+#define INTERFACE IVSSDatabase
+
+DECLARE_INTERFACE_(IVSSDatabase, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSSDatabase methods */
+    STDMETHOD(Open)(THIS_ BSTR SrcSafeIni, BSTR Username, BSTR Password) PURE;
+    STDMETHOD(get_SrcSafeIni)(THIS_ BSTR FAR* pSrcSafeIni) PURE;
+    STDMETHOD(get_DatabaseName)(THIS_ BSTR FAR* pDatabaseName) PURE;
+    STDMETHOD(get_UserName)(THIS_ BSTR FAR* pUsername) PURE;
+    STDMETHOD(get_CurrentProject)(THIS_ BSTR FAR* pPrj) PURE;
+    STDMETHOD(put_CurrentProject)(THIS_ BSTR Prj) PURE;
+    STDMETHOD(get_VSSItem)(THIS_ BSTR Spec, VARIANT_BOOL Deleted, IVSSItem FAR* FAR* ppIVSSItem) PURE;
+};
+
+DEFINE_GUID(CLSID_VSSItem,0x783CD4E3L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+#ifdef __cplusplus
+class VSSItem;
+#endif
+
+DEFINE_GUID(CLSID_VSSVersion,0x783CD4ECL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+#ifdef __cplusplus
+class VSSVersion;
+#endif
+
+DEFINE_GUID(CLSID_VSSCheckout,0x2A0DE0E0L,0x2E9F,0x11D0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95);
+
+#ifdef __cplusplus
+class VSSCheckout;
+#endif
+
+DEFINE_GUID(CLSID_VSSDatabase,0x783CD4E4L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+#ifdef __cplusplus
+class VSSDatabase;
+#endif
+
+DEFINE_GUID(IID_IVSSEvents,0x783CD4E9L,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSEvents */
+#undef INTERFACE
+#define INTERFACE IVSSEvents
+
+DECLARE_INTERFACE_(IVSSEvents, IUnknown)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+#endif
+
+    /* IVSSEvents methods */
+    STDMETHOD(BeforeAdd)(THIS_ IVSSItem FAR* pIPrj, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE;
+    STDMETHOD(AfterAdd)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE;
+    STDMETHOD(BeforeCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE;
+    STDMETHOD(AfterCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE;
+    STDMETHOD(BeforeCheckin)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE;
+    STDMETHOD(AfterCheckin)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE;
+    STDMETHOD(BeforeUndoCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local, VARIANT_BOOL FAR* pbContinue) PURE;
+    STDMETHOD(AfterUndoCheckout)(THIS_ IVSSItem FAR* pIItem, BSTR Local) PURE;
+    STDMETHOD(BeforeRename)(THIS_ IVSSItem FAR* pIItem, BSTR NewName, VARIANT_BOOL FAR* pbContinue) PURE;
+    STDMETHOD(AfterRename)(THIS_ IVSSItem FAR* pIItem, BSTR OldName) PURE;
+    STDMETHOD(BeforeBranch)(THIS_ IVSSItem FAR* pIItem, VARIANT_BOOL FAR* pbContinue) PURE;
+    STDMETHOD(AfterBranch)(THIS_ IVSSItem FAR* pIItem) PURE;
+    STDMETHOD(BeforeEvent)(THIS_ long iEvent, IVSSItem FAR* pIItem, BSTR Str, VARIANT var, VARIANT_BOOL FAR* pbContinue) PURE;
+    STDMETHOD(AfterEvent)(THIS_ long iEvent, IVSSItem FAR* pIItem, BSTR Str, VARIANT var) PURE;
+};
+
+DEFINE_GUID(IID_IVSS,0x783CD4EBL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSS */
+#undef INTERFACE
+#define INTERFACE IVSS
+
+DECLARE_INTERFACE_(IVSS, IDispatch)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+
+    /* IDispatch methods */
+    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE;
+
+    STDMETHOD(GetTypeInfo)(
+      THIS_
+      UINT itinfo,
+      LCID lcid,
+      ITypeInfo FAR* FAR* pptinfo) PURE;
+
+    STDMETHOD(GetIDsOfNames)(
+      THIS_
+      REFIID riid,
+      OLECHAR FAR* FAR* rgszNames,
+      UINT cNames,
+      LCID lcid,
+      DISPID FAR* rgdispid) PURE;
+
+    STDMETHOD(Invoke)(
+      THIS_
+      DISPID dispidMember,
+      REFIID riid,
+      LCID lcid,
+      WORD wFlags,
+      DISPPARAMS FAR* pdispparams,
+      VARIANT FAR* pvarResult,
+      EXCEPINFO FAR* pexcepinfo,
+      UINT FAR* puArgErr) PURE;
+#endif
+
+    /* IVSS methods */
+    STDMETHOD(get_VSSDatabase)(THIS_ IVSSDatabase FAR* FAR* ppIVSSDatabase) PURE;
+};
+
+DEFINE_GUID(IID_IVSSEventHandler,0x783CD4EAL,0x9D54,0x11CF,0xB8,0xEE,0x00,0x60,0x8C,0xC9,0xA7,0x1F);
+
+/* Definition of interface: IVSSEventHandler */
+#undef INTERFACE
+#define INTERFACE IVSSEventHandler
+
+DECLARE_INTERFACE_(IVSSEventHandler, IUnknown)
+{
+BEGIN_INTERFACE
+#ifndef NO_BASEINTERFACE_FUNCS
+
+    /* IUnknown methods */
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
+    STDMETHOD_(ULONG, AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG, Release)(THIS) PURE;
+#endif
+
+    /* IVSSEventHandler methods */
+    STDMETHOD(Init)(THIS_ IVSS FAR* pIVSS) PURE;
+};
+
+DEFINE_GUID(CLSID_VSSApp,0x2A0DE0E1L,0x2E9F,0x11D0,0x92,0x36,0x00,0xAA,0x00,0xA1,0xEB,0x95);
+
+#ifdef __cplusplus
+class VSSApp;
+#endif
+
+#endif