summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am77
-rw-r--r--src/Makefile.in1680
-rw-r--r--src/analyze.c707
-rw-r--r--src/cmp.c661
-rw-r--r--src/context.c537
-rw-r--r--src/diff.c1472
-rw-r--r--src/diff.h423
-rw-r--r--src/diff3.c1791
-rw-r--r--src/dir.c385
-rw-r--r--src/ed.c175
-rw-r--r--src/ifdef.c430
-rw-r--r--src/io.c830
-rw-r--r--src/normal.c91
-rw-r--r--src/sdiff.c1173
-rw-r--r--src/side.c335
-rw-r--r--src/system.h214
-rw-r--r--src/util.c1577
17 files changed, 12558 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..167f7fd
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,77 @@
+# Automakefile for GNU diffutils programs.
+
+# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2016 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 3 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, see <http://www.gnu.org/licenses/>.
+
+bin_PROGRAMS = cmp diff diff3 sdiff
+
+localedir = $(datadir)/locale
+
+AM_CPPFLAGS = -I../lib -I$(top_srcdir)/lib
+AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
+
+LDADD = \
+ libver.a \
+ ../lib/libdiffutils.a \
+ $(LIBCSTACK) \
+ $(LIBINTL) \
+ $(LIBICONV) \
+ $(LIBSIGSEGV) \
+ $(LIB_CLOCK_GETTIME)
+
+diff_LDADD = $(LDADD)
+cmp_LDADD = $(LDADD)
+sdiff_LDADD = $(LDADD)
+diff3_LDADD = $(LDADD)
+
+cmp_SOURCES = cmp.c
+diff3_SOURCES = diff3.c
+sdiff_SOURCES = sdiff.c
+diff_SOURCES = \
+ analyze.c context.c diff.c dir.c ed.c ifdef.c io.c \
+ normal.c side.c util.c
+noinst_HEADERS = diff.h system.h
+
+MOSTLYCLEANFILES = paths.h paths.ht
+
+cmp.$(OBJEXT) diff3.$(OBJEXT) diff.$(OBJEXT) sdiff.$(OBJEXT): paths.h
+
+gdiff = `echo diff|sed '$(transform)'`
+BUILT_SOURCES = paths.h
+paths.h: Makefile.am
+ $(AM_V_GEN)(echo '#define DEFAULT_DIFF_PROGRAM "'$(gdiff)'"' && \
+ echo '#define LOCALEDIR "$(localedir)"') >$@t && mv $@t $@
+
+noinst_LIBRARIES = libver.a
+nodist_libver_a_SOURCES = version.c version.h
+
+BUILT_SOURCES += version.c
+version.c: Makefile
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)printf '#include <config.h>\n' > $@t
+ $(AM_V_at)printf 'char const *Version = "$(PACKAGE_VERSION)";\n' >> $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+BUILT_SOURCES += version.h
+version.h: Makefile
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)printf 'extern char const *Version;\n' > $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+DISTCLEANFILES = version.c version.h
+MAINTAINERCLEANFILES = $(BUILT_SOURCES)
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..a0f1c99
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,1680 @@
+# Makefile.in generated by automake 1.99a from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2015 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Automakefile for GNU diffutils programs.
+
+# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2016 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = cmp$(EXEEXT) diff$(EXEEXT) diff3$(EXEEXT) \
+ sdiff$(EXEEXT)
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/00gnulib.m4 \
+ $(top_srcdir)/m4/absolute-header.m4 $(top_srcdir)/m4/alloca.m4 \
+ $(top_srcdir)/m4/arpa_inet_h.m4 $(top_srcdir)/m4/btowc.m4 \
+ $(top_srcdir)/m4/c-stack.m4 $(top_srcdir)/m4/clock_time.m4 \
+ $(top_srcdir)/m4/close.m4 $(top_srcdir)/m4/codeset.m4 \
+ $(top_srcdir)/m4/config-h.m4 $(top_srcdir)/m4/configmake.m4 \
+ $(top_srcdir)/m4/ctype.m4 $(top_srcdir)/m4/dirname.m4 \
+ $(top_srcdir)/m4/double-slash-root.m4 $(top_srcdir)/m4/dup2.m4 \
+ $(top_srcdir)/m4/eealloc.m4 $(top_srcdir)/m4/environ.m4 \
+ $(top_srcdir)/m4/errno_h.m4 $(top_srcdir)/m4/error.m4 \
+ $(top_srcdir)/m4/exponentd.m4 $(top_srcdir)/m4/extensions.m4 \
+ $(top_srcdir)/m4/extern-inline.m4 $(top_srcdir)/m4/fcntl-o.m4 \
+ $(top_srcdir)/m4/fcntl.m4 $(top_srcdir)/m4/fcntl_h.m4 \
+ $(top_srcdir)/m4/fdopen.m4 $(top_srcdir)/m4/filenamecat.m4 \
+ $(top_srcdir)/m4/flexmember.m4 $(top_srcdir)/m4/float_h.m4 \
+ $(top_srcdir)/m4/fnmatch.m4 $(top_srcdir)/m4/fpieee.m4 \
+ $(top_srcdir)/m4/freopen.m4 $(top_srcdir)/m4/fstat.m4 \
+ $(top_srcdir)/m4/ftruncate.m4 $(top_srcdir)/m4/getcwd.m4 \
+ $(top_srcdir)/m4/getdtablesize.m4 $(top_srcdir)/m4/getopt.m4 \
+ $(top_srcdir)/m4/getpagesize.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/gettime.m4 $(top_srcdir)/m4/gettimeofday.m4 \
+ $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/gnu-make.m4 \
+ $(top_srcdir)/m4/gnulib-common.m4 \
+ $(top_srcdir)/m4/gnulib-comp.m4 \
+ $(top_srcdir)/m4/hard-locale.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/iconv_h.m4 $(top_srcdir)/m4/iconv_open.m4 \
+ $(top_srcdir)/m4/include_next.m4 $(top_srcdir)/m4/inet_pton.m4 \
+ $(top_srcdir)/m4/inline.m4 $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/intmax_t.m4 $(top_srcdir)/m4/inttostr.m4 \
+ $(top_srcdir)/m4/inttypes-pri.m4 $(top_srcdir)/m4/inttypes.m4 \
+ $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/ioctl.m4 \
+ $(top_srcdir)/m4/isblank.m4 $(top_srcdir)/m4/iswblank.m4 \
+ $(top_srcdir)/m4/langinfo_h.m4 $(top_srcdir)/m4/largefile.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libsigsegv.m4 \
+ $(top_srcdir)/m4/libunistring-base.m4 \
+ $(top_srcdir)/m4/localcharset.m4 $(top_srcdir)/m4/locale-fr.m4 \
+ $(top_srcdir)/m4/locale-ja.m4 $(top_srcdir)/m4/locale-tr.m4 \
+ $(top_srcdir)/m4/locale-zh.m4 $(top_srcdir)/m4/locale_h.m4 \
+ $(top_srcdir)/m4/localeconv.m4 $(top_srcdir)/m4/longlong.m4 \
+ $(top_srcdir)/m4/lstat.m4 $(top_srcdir)/m4/malloc.m4 \
+ $(top_srcdir)/m4/malloca.m4 $(top_srcdir)/m4/manywarnings.m4 \
+ $(top_srcdir)/m4/mbchar.m4 $(top_srcdir)/m4/mbiter.m4 \
+ $(top_srcdir)/m4/mbrtowc.m4 $(top_srcdir)/m4/mbsinit.m4 \
+ $(top_srcdir)/m4/mbslen.m4 $(top_srcdir)/m4/mbsrtowcs.m4 \
+ $(top_srcdir)/m4/mbstate_t.m4 $(top_srcdir)/m4/mbtowc.m4 \
+ $(top_srcdir)/m4/memchr.m4 $(top_srcdir)/m4/mkstemp.m4 \
+ $(top_srcdir)/m4/mktime.m4 $(top_srcdir)/m4/mmap-anon.m4 \
+ $(top_srcdir)/m4/mode_t.m4 $(top_srcdir)/m4/msvc-inval.m4 \
+ $(top_srcdir)/m4/msvc-nothrow.m4 $(top_srcdir)/m4/multiarch.m4 \
+ $(top_srcdir)/m4/nanosleep.m4 $(top_srcdir)/m4/netinet_in_h.m4 \
+ $(top_srcdir)/m4/nl_langinfo.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/nocrash.m4 $(top_srcdir)/m4/off_t.m4 \
+ $(top_srcdir)/m4/open.m4 $(top_srcdir)/m4/pathmax.m4 \
+ $(top_srcdir)/m4/perror.m4 $(top_srcdir)/m4/pipe.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/printf.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/putenv.m4 \
+ $(top_srcdir)/m4/quote.m4 $(top_srcdir)/m4/quotearg.m4 \
+ $(top_srcdir)/m4/raise.m4 $(top_srcdir)/m4/rawmemchr.m4 \
+ $(top_srcdir)/m4/readlink.m4 $(top_srcdir)/m4/regex.m4 \
+ $(top_srcdir)/m4/secure_getenv.m4 $(top_srcdir)/m4/select.m4 \
+ $(top_srcdir)/m4/setenv.m4 $(top_srcdir)/m4/setlocale.m4 \
+ $(top_srcdir)/m4/sigaction.m4 $(top_srcdir)/m4/signal_h.m4 \
+ $(top_srcdir)/m4/signalblocking.m4 \
+ $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/sleep.m4 \
+ $(top_srcdir)/m4/snprintf.m4 $(top_srcdir)/m4/socketlib.m4 \
+ $(top_srcdir)/m4/sockets.m4 $(top_srcdir)/m4/socklen.m4 \
+ $(top_srcdir)/m4/sockpfaf.m4 $(top_srcdir)/m4/ssize_t.m4 \
+ $(top_srcdir)/m4/stat-time.m4 $(top_srcdir)/m4/stat.m4 \
+ $(top_srcdir)/m4/stdalign.m4 $(top_srcdir)/m4/stdarg.m4 \
+ $(top_srcdir)/m4/stdbool.m4 $(top_srcdir)/m4/stddef_h.m4 \
+ $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \
+ $(top_srcdir)/m4/stdio_h.m4 $(top_srcdir)/m4/stdlib_h.m4 \
+ $(top_srcdir)/m4/strcase.m4 $(top_srcdir)/m4/strerror.m4 \
+ $(top_srcdir)/m4/strerror_r.m4 $(top_srcdir)/m4/strftime.m4 \
+ $(top_srcdir)/m4/string_h.m4 $(top_srcdir)/m4/strings_h.m4 \
+ $(top_srcdir)/m4/strndup.m4 $(top_srcdir)/m4/strnlen.m4 \
+ $(top_srcdir)/m4/strptime.m4 $(top_srcdir)/m4/strtoull.m4 \
+ $(top_srcdir)/m4/strtoumax.m4 $(top_srcdir)/m4/symlink.m4 \
+ $(top_srcdir)/m4/sys_ioctl_h.m4 \
+ $(top_srcdir)/m4/sys_select_h.m4 \
+ $(top_srcdir)/m4/sys_socket_h.m4 \
+ $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/sys_time_h.m4 \
+ $(top_srcdir)/m4/sys_types_h.m4 $(top_srcdir)/m4/sys_uio_h.m4 \
+ $(top_srcdir)/m4/sys_wait_h.m4 $(top_srcdir)/m4/tempname.m4 \
+ $(top_srcdir)/m4/time_h.m4 $(top_srcdir)/m4/time_r.m4 \
+ $(top_srcdir)/m4/time_rz.m4 $(top_srcdir)/m4/timegm.m4 \
+ $(top_srcdir)/m4/timespec.m4 $(top_srcdir)/m4/tm_gmtoff.m4 \
+ $(top_srcdir)/m4/unistd_h.m4 $(top_srcdir)/m4/unlocked-io.m4 \
+ $(top_srcdir)/m4/vararrays.m4 $(top_srcdir)/m4/vasnprintf.m4 \
+ $(top_srcdir)/m4/vasprintf.m4 $(top_srcdir)/m4/version-etc.m4 \
+ $(top_srcdir)/m4/warn-on-use.m4 $(top_srcdir)/m4/warnings.m4 \
+ $(top_srcdir)/m4/wchar_h.m4 $(top_srcdir)/m4/wchar_t.m4 \
+ $(top_srcdir)/m4/wcrtomb.m4 $(top_srcdir)/m4/wctob.m4 \
+ $(top_srcdir)/m4/wctomb.m4 $(top_srcdir)/m4/wctype_h.m4 \
+ $(top_srcdir)/m4/wcwidth.m4 $(top_srcdir)/m4/wint_t.m4 \
+ $(top_srcdir)/m4/xalloc.m4 $(top_srcdir)/m4/xsize.m4 \
+ $(top_srcdir)/m4/xstrndup.m4 $(top_srcdir)/m4/xstrtol.m4 \
+ $(top_srcdir)/m4/xvasprintf.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/lib/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libver_a_AR = $(AR) $(ARFLAGS)
+libver_a_LIBADD =
+nodist_libver_a_OBJECTS = version.$(OBJEXT)
+libver_a_OBJECTS = $(nodist_libver_a_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_cmp_OBJECTS = cmp.$(OBJEXT)
+cmp_OBJECTS = $(am_cmp_OBJECTS)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = libver.a ../lib/libdiffutils.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+cmp_DEPENDENCIES = $(am__DEPENDENCIES_2)
+am_diff_OBJECTS = analyze.$(OBJEXT) context.$(OBJEXT) diff.$(OBJEXT) \
+ dir.$(OBJEXT) ed.$(OBJEXT) ifdef.$(OBJEXT) io.$(OBJEXT) \
+ normal.$(OBJEXT) side.$(OBJEXT) util.$(OBJEXT)
+diff_OBJECTS = $(am_diff_OBJECTS)
+diff_DEPENDENCIES = $(am__DEPENDENCIES_2)
+am_diff3_OBJECTS = diff3.$(OBJEXT)
+diff3_OBJECTS = $(am_diff3_OBJECTS)
+diff3_DEPENDENCIES = $(am__DEPENDENCIES_2)
+am_sdiff_OBJECTS = sdiff.$(OBJEXT)
+sdiff_OBJECTS = $(am_sdiff_OBJECTS)
+sdiff_DEPENDENCIES = $(am__DEPENDENCIES_2)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/analyze.Po ./$(DEPDIR)/cmp.Po \
+ ./$(DEPDIR)/context.Po ./$(DEPDIR)/diff.Po \
+ ./$(DEPDIR)/diff3.Po ./$(DEPDIR)/dir.Po ./$(DEPDIR)/ed.Po \
+ ./$(DEPDIR)/ifdef.Po ./$(DEPDIR)/io.Po ./$(DEPDIR)/normal.Po \
+ ./$(DEPDIR)/sdiff.Po ./$(DEPDIR)/side.Po ./$(DEPDIR)/util.Po \
+ ./$(DEPDIR)/version.Po
+am__mv = mv -f
+am__set_depbase = depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.[^.]*$$||'`
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(nodist_libver_a_SOURCES) $(cmp_SOURCES) $(diff_SOURCES) \
+ $(diff3_SOURCES) $(sdiff_SOURCES)
+DIST_SOURCES = $(cmp_SOURCES) $(diff_SOURCES) $(diff3_SOURCES) \
+ $(sdiff_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build-aux/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+pkglibexecdir = @pkglibexecdir@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALLOCA_H = @ALLOCA_H@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
+BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
+BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
+BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
+BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONFIG_INCLUDE = @CONFIG_INCLUDE@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@
+EMULTIHOP_VALUE = @EMULTIHOP_VALUE@
+ENOLINK_HIDDEN = @ENOLINK_HIDDEN@
+ENOLINK_VALUE = @ENOLINK_VALUE@
+EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@
+EOVERFLOW_VALUE = @EOVERFLOW_VALUE@
+ERRNO_H = @ERRNO_H@
+EXEEXT = @EXEEXT@
+FLOAT_H = @FLOAT_H@
+FNMATCH_H = @FNMATCH_H@
+GETOPT_H = @GETOPT_H@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GLIBC21 = @GLIBC21@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNULIB_ACCEPT = @GNULIB_ACCEPT@
+GNULIB_ACCEPT4 = @GNULIB_ACCEPT4@
+GNULIB_ATOLL = @GNULIB_ATOLL@
+GNULIB_BIND = @GNULIB_BIND@
+GNULIB_BTOWC = @GNULIB_BTOWC@
+GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@
+GNULIB_CANONICALIZE_FILE_NAME = @GNULIB_CANONICALIZE_FILE_NAME@
+GNULIB_CHDIR = @GNULIB_CHDIR@
+GNULIB_CHOWN = @GNULIB_CHOWN@
+GNULIB_CLOSE = @GNULIB_CLOSE@
+GNULIB_CONNECT = @GNULIB_CONNECT@
+GNULIB_DPRINTF = @GNULIB_DPRINTF@
+GNULIB_DUP = @GNULIB_DUP@
+GNULIB_DUP2 = @GNULIB_DUP2@
+GNULIB_DUP3 = @GNULIB_DUP3@
+GNULIB_DUPLOCALE = @GNULIB_DUPLOCALE@
+GNULIB_ENVIRON = @GNULIB_ENVIRON@
+GNULIB_EUIDACCESS = @GNULIB_EUIDACCESS@
+GNULIB_FACCESSAT = @GNULIB_FACCESSAT@
+GNULIB_FCHDIR = @GNULIB_FCHDIR@
+GNULIB_FCHMODAT = @GNULIB_FCHMODAT@
+GNULIB_FCHOWNAT = @GNULIB_FCHOWNAT@
+GNULIB_FCLOSE = @GNULIB_FCLOSE@
+GNULIB_FCNTL = @GNULIB_FCNTL@
+GNULIB_FDATASYNC = @GNULIB_FDATASYNC@
+GNULIB_FDOPEN = @GNULIB_FDOPEN@
+GNULIB_FFLUSH = @GNULIB_FFLUSH@
+GNULIB_FFS = @GNULIB_FFS@
+GNULIB_FFSL = @GNULIB_FFSL@
+GNULIB_FFSLL = @GNULIB_FFSLL@
+GNULIB_FGETC = @GNULIB_FGETC@
+GNULIB_FGETS = @GNULIB_FGETS@
+GNULIB_FOPEN = @GNULIB_FOPEN@
+GNULIB_FPRINTF = @GNULIB_FPRINTF@
+GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@
+GNULIB_FPURGE = @GNULIB_FPURGE@
+GNULIB_FPUTC = @GNULIB_FPUTC@
+GNULIB_FPUTS = @GNULIB_FPUTS@
+GNULIB_FREAD = @GNULIB_FREAD@
+GNULIB_FREOPEN = @GNULIB_FREOPEN@
+GNULIB_FSCANF = @GNULIB_FSCANF@
+GNULIB_FSEEK = @GNULIB_FSEEK@
+GNULIB_FSEEKO = @GNULIB_FSEEKO@
+GNULIB_FSTAT = @GNULIB_FSTAT@
+GNULIB_FSTATAT = @GNULIB_FSTATAT@
+GNULIB_FSYNC = @GNULIB_FSYNC@
+GNULIB_FTELL = @GNULIB_FTELL@
+GNULIB_FTELLO = @GNULIB_FTELLO@
+GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@
+GNULIB_FUTIMENS = @GNULIB_FUTIMENS@
+GNULIB_FWRITE = @GNULIB_FWRITE@
+GNULIB_GETC = @GNULIB_GETC@
+GNULIB_GETCHAR = @GNULIB_GETCHAR@
+GNULIB_GETCWD = @GNULIB_GETCWD@
+GNULIB_GETDELIM = @GNULIB_GETDELIM@
+GNULIB_GETDOMAINNAME = @GNULIB_GETDOMAINNAME@
+GNULIB_GETDTABLESIZE = @GNULIB_GETDTABLESIZE@
+GNULIB_GETGROUPS = @GNULIB_GETGROUPS@
+GNULIB_GETHOSTNAME = @GNULIB_GETHOSTNAME@
+GNULIB_GETLINE = @GNULIB_GETLINE@
+GNULIB_GETLOADAVG = @GNULIB_GETLOADAVG@
+GNULIB_GETLOGIN = @GNULIB_GETLOGIN@
+GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@
+GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@
+GNULIB_GETPEERNAME = @GNULIB_GETPEERNAME@
+GNULIB_GETSOCKNAME = @GNULIB_GETSOCKNAME@
+GNULIB_GETSOCKOPT = @GNULIB_GETSOCKOPT@
+GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@
+GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@
+GNULIB_GETUSERSHELL = @GNULIB_GETUSERSHELL@
+GNULIB_GL_UNISTD_H_GETOPT = @GNULIB_GL_UNISTD_H_GETOPT@
+GNULIB_GRANTPT = @GNULIB_GRANTPT@
+GNULIB_GROUP_MEMBER = @GNULIB_GROUP_MEMBER@
+GNULIB_ICONV = @GNULIB_ICONV@
+GNULIB_IMAXABS = @GNULIB_IMAXABS@
+GNULIB_IMAXDIV = @GNULIB_IMAXDIV@
+GNULIB_INET_NTOP = @GNULIB_INET_NTOP@
+GNULIB_INET_PTON = @GNULIB_INET_PTON@
+GNULIB_IOCTL = @GNULIB_IOCTL@
+GNULIB_ISATTY = @GNULIB_ISATTY@
+GNULIB_ISBLANK = @GNULIB_ISBLANK@
+GNULIB_ISWBLANK = @GNULIB_ISWBLANK@
+GNULIB_ISWCTYPE = @GNULIB_ISWCTYPE@
+GNULIB_LCHMOD = @GNULIB_LCHMOD@
+GNULIB_LCHOWN = @GNULIB_LCHOWN@
+GNULIB_LINK = @GNULIB_LINK@
+GNULIB_LINKAT = @GNULIB_LINKAT@
+GNULIB_LISTEN = @GNULIB_LISTEN@
+GNULIB_LOCALECONV = @GNULIB_LOCALECONV@
+GNULIB_LSEEK = @GNULIB_LSEEK@
+GNULIB_LSTAT = @GNULIB_LSTAT@
+GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@
+GNULIB_MBRLEN = @GNULIB_MBRLEN@
+GNULIB_MBRTOWC = @GNULIB_MBRTOWC@
+GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@
+GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@
+GNULIB_MBSCHR = @GNULIB_MBSCHR@
+GNULIB_MBSCSPN = @GNULIB_MBSCSPN@
+GNULIB_MBSINIT = @GNULIB_MBSINIT@
+GNULIB_MBSLEN = @GNULIB_MBSLEN@
+GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@
+GNULIB_MBSNLEN = @GNULIB_MBSNLEN@
+GNULIB_MBSNRTOWCS = @GNULIB_MBSNRTOWCS@
+GNULIB_MBSPBRK = @GNULIB_MBSPBRK@
+GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@
+GNULIB_MBSRCHR = @GNULIB_MBSRCHR@
+GNULIB_MBSRTOWCS = @GNULIB_MBSRTOWCS@
+GNULIB_MBSSEP = @GNULIB_MBSSEP@
+GNULIB_MBSSPN = @GNULIB_MBSSPN@
+GNULIB_MBSSTR = @GNULIB_MBSSTR@
+GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@
+GNULIB_MBTOWC = @GNULIB_MBTOWC@
+GNULIB_MEMCHR = @GNULIB_MEMCHR@
+GNULIB_MEMMEM = @GNULIB_MEMMEM@
+GNULIB_MEMPCPY = @GNULIB_MEMPCPY@
+GNULIB_MEMRCHR = @GNULIB_MEMRCHR@
+GNULIB_MKDIRAT = @GNULIB_MKDIRAT@
+GNULIB_MKDTEMP = @GNULIB_MKDTEMP@
+GNULIB_MKFIFO = @GNULIB_MKFIFO@
+GNULIB_MKFIFOAT = @GNULIB_MKFIFOAT@
+GNULIB_MKNOD = @GNULIB_MKNOD@
+GNULIB_MKNODAT = @GNULIB_MKNODAT@
+GNULIB_MKOSTEMP = @GNULIB_MKOSTEMP@
+GNULIB_MKOSTEMPS = @GNULIB_MKOSTEMPS@
+GNULIB_MKSTEMP = @GNULIB_MKSTEMP@
+GNULIB_MKSTEMPS = @GNULIB_MKSTEMPS@
+GNULIB_MKTIME = @GNULIB_MKTIME@
+GNULIB_NANOSLEEP = @GNULIB_NANOSLEEP@
+GNULIB_NL_LANGINFO = @GNULIB_NL_LANGINFO@
+GNULIB_NONBLOCKING = @GNULIB_NONBLOCKING@
+GNULIB_OBSTACK_PRINTF = @GNULIB_OBSTACK_PRINTF@
+GNULIB_OBSTACK_PRINTF_POSIX = @GNULIB_OBSTACK_PRINTF_POSIX@
+GNULIB_OPEN = @GNULIB_OPEN@
+GNULIB_OPENAT = @GNULIB_OPENAT@
+GNULIB_PCLOSE = @GNULIB_PCLOSE@
+GNULIB_PERROR = @GNULIB_PERROR@
+GNULIB_PIPE = @GNULIB_PIPE@
+GNULIB_PIPE2 = @GNULIB_PIPE2@
+GNULIB_POPEN = @GNULIB_POPEN@
+GNULIB_POSIX_OPENPT = @GNULIB_POSIX_OPENPT@
+GNULIB_PREAD = @GNULIB_PREAD@
+GNULIB_PRINTF = @GNULIB_PRINTF@
+GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@
+GNULIB_PSELECT = @GNULIB_PSELECT@
+GNULIB_PTHREAD_SIGMASK = @GNULIB_PTHREAD_SIGMASK@
+GNULIB_PTSNAME = @GNULIB_PTSNAME@
+GNULIB_PTSNAME_R = @GNULIB_PTSNAME_R@
+GNULIB_PUTC = @GNULIB_PUTC@
+GNULIB_PUTCHAR = @GNULIB_PUTCHAR@
+GNULIB_PUTENV = @GNULIB_PUTENV@
+GNULIB_PUTS = @GNULIB_PUTS@
+GNULIB_PWRITE = @GNULIB_PWRITE@
+GNULIB_QSORT_R = @GNULIB_QSORT_R@
+GNULIB_RAISE = @GNULIB_RAISE@
+GNULIB_RANDOM = @GNULIB_RANDOM@
+GNULIB_RANDOM_R = @GNULIB_RANDOM_R@
+GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@
+GNULIB_READ = @GNULIB_READ@
+GNULIB_READLINK = @GNULIB_READLINK@
+GNULIB_READLINKAT = @GNULIB_READLINKAT@
+GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@
+GNULIB_REALPATH = @GNULIB_REALPATH@
+GNULIB_RECV = @GNULIB_RECV@
+GNULIB_RECVFROM = @GNULIB_RECVFROM@
+GNULIB_REMOVE = @GNULIB_REMOVE@
+GNULIB_RENAME = @GNULIB_RENAME@
+GNULIB_RENAMEAT = @GNULIB_RENAMEAT@
+GNULIB_RMDIR = @GNULIB_RMDIR@
+GNULIB_RPMATCH = @GNULIB_RPMATCH@
+GNULIB_SCANF = @GNULIB_SCANF@
+GNULIB_SECURE_GETENV = @GNULIB_SECURE_GETENV@
+GNULIB_SELECT = @GNULIB_SELECT@
+GNULIB_SEND = @GNULIB_SEND@
+GNULIB_SENDTO = @GNULIB_SENDTO@
+GNULIB_SETENV = @GNULIB_SETENV@
+GNULIB_SETHOSTNAME = @GNULIB_SETHOSTNAME@
+GNULIB_SETLOCALE = @GNULIB_SETLOCALE@
+GNULIB_SETSOCKOPT = @GNULIB_SETSOCKOPT@
+GNULIB_SHUTDOWN = @GNULIB_SHUTDOWN@
+GNULIB_SIGACTION = @GNULIB_SIGACTION@
+GNULIB_SIGNAL_H_SIGPIPE = @GNULIB_SIGNAL_H_SIGPIPE@
+GNULIB_SIGPROCMASK = @GNULIB_SIGPROCMASK@
+GNULIB_SLEEP = @GNULIB_SLEEP@
+GNULIB_SNPRINTF = @GNULIB_SNPRINTF@
+GNULIB_SOCKET = @GNULIB_SOCKET@
+GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@
+GNULIB_STAT = @GNULIB_STAT@
+GNULIB_STDIO_H_NONBLOCKING = @GNULIB_STDIO_H_NONBLOCKING@
+GNULIB_STDIO_H_SIGPIPE = @GNULIB_STDIO_H_SIGPIPE@
+GNULIB_STPCPY = @GNULIB_STPCPY@
+GNULIB_STPNCPY = @GNULIB_STPNCPY@
+GNULIB_STRCASESTR = @GNULIB_STRCASESTR@
+GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@
+GNULIB_STRDUP = @GNULIB_STRDUP@
+GNULIB_STRERROR = @GNULIB_STRERROR@
+GNULIB_STRERROR_R = @GNULIB_STRERROR_R@
+GNULIB_STRNCAT = @GNULIB_STRNCAT@
+GNULIB_STRNDUP = @GNULIB_STRNDUP@
+GNULIB_STRNLEN = @GNULIB_STRNLEN@
+GNULIB_STRPBRK = @GNULIB_STRPBRK@
+GNULIB_STRPTIME = @GNULIB_STRPTIME@
+GNULIB_STRSEP = @GNULIB_STRSEP@
+GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@
+GNULIB_STRSTR = @GNULIB_STRSTR@
+GNULIB_STRTOD = @GNULIB_STRTOD@
+GNULIB_STRTOIMAX = @GNULIB_STRTOIMAX@
+GNULIB_STRTOK_R = @GNULIB_STRTOK_R@
+GNULIB_STRTOLL = @GNULIB_STRTOLL@
+GNULIB_STRTOULL = @GNULIB_STRTOULL@
+GNULIB_STRTOUMAX = @GNULIB_STRTOUMAX@
+GNULIB_STRVERSCMP = @GNULIB_STRVERSCMP@
+GNULIB_SYMLINK = @GNULIB_SYMLINK@
+GNULIB_SYMLINKAT = @GNULIB_SYMLINKAT@
+GNULIB_SYSTEM_POSIX = @GNULIB_SYSTEM_POSIX@
+GNULIB_TIMEGM = @GNULIB_TIMEGM@
+GNULIB_TIME_R = @GNULIB_TIME_R@
+GNULIB_TIME_RZ = @GNULIB_TIME_RZ@
+GNULIB_TMPFILE = @GNULIB_TMPFILE@
+GNULIB_TOWCTRANS = @GNULIB_TOWCTRANS@
+GNULIB_TTYNAME_R = @GNULIB_TTYNAME_R@
+GNULIB_UNISTD_H_NONBLOCKING = @GNULIB_UNISTD_H_NONBLOCKING@
+GNULIB_UNISTD_H_SIGPIPE = @GNULIB_UNISTD_H_SIGPIPE@
+GNULIB_UNLINK = @GNULIB_UNLINK@
+GNULIB_UNLINKAT = @GNULIB_UNLINKAT@
+GNULIB_UNLOCKPT = @GNULIB_UNLOCKPT@
+GNULIB_UNSETENV = @GNULIB_UNSETENV@
+GNULIB_USLEEP = @GNULIB_USLEEP@
+GNULIB_UTIMENSAT = @GNULIB_UTIMENSAT@
+GNULIB_VASPRINTF = @GNULIB_VASPRINTF@
+GNULIB_VDPRINTF = @GNULIB_VDPRINTF@
+GNULIB_VFPRINTF = @GNULIB_VFPRINTF@
+GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@
+GNULIB_VFSCANF = @GNULIB_VFSCANF@
+GNULIB_VPRINTF = @GNULIB_VPRINTF@
+GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@
+GNULIB_VSCANF = @GNULIB_VSCANF@
+GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@
+GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@
+GNULIB_WAITPID = @GNULIB_WAITPID@
+GNULIB_WARN_CFLAGS = @GNULIB_WARN_CFLAGS@
+GNULIB_WCPCPY = @GNULIB_WCPCPY@
+GNULIB_WCPNCPY = @GNULIB_WCPNCPY@
+GNULIB_WCRTOMB = @GNULIB_WCRTOMB@
+GNULIB_WCSCASECMP = @GNULIB_WCSCASECMP@
+GNULIB_WCSCAT = @GNULIB_WCSCAT@
+GNULIB_WCSCHR = @GNULIB_WCSCHR@
+GNULIB_WCSCMP = @GNULIB_WCSCMP@
+GNULIB_WCSCOLL = @GNULIB_WCSCOLL@
+GNULIB_WCSCPY = @GNULIB_WCSCPY@
+GNULIB_WCSCSPN = @GNULIB_WCSCSPN@
+GNULIB_WCSDUP = @GNULIB_WCSDUP@
+GNULIB_WCSLEN = @GNULIB_WCSLEN@
+GNULIB_WCSNCASECMP = @GNULIB_WCSNCASECMP@
+GNULIB_WCSNCAT = @GNULIB_WCSNCAT@
+GNULIB_WCSNCMP = @GNULIB_WCSNCMP@
+GNULIB_WCSNCPY = @GNULIB_WCSNCPY@
+GNULIB_WCSNLEN = @GNULIB_WCSNLEN@
+GNULIB_WCSNRTOMBS = @GNULIB_WCSNRTOMBS@
+GNULIB_WCSPBRK = @GNULIB_WCSPBRK@
+GNULIB_WCSRCHR = @GNULIB_WCSRCHR@
+GNULIB_WCSRTOMBS = @GNULIB_WCSRTOMBS@
+GNULIB_WCSSPN = @GNULIB_WCSSPN@
+GNULIB_WCSSTR = @GNULIB_WCSSTR@
+GNULIB_WCSTOK = @GNULIB_WCSTOK@
+GNULIB_WCSWIDTH = @GNULIB_WCSWIDTH@
+GNULIB_WCSXFRM = @GNULIB_WCSXFRM@
+GNULIB_WCTOB = @GNULIB_WCTOB@
+GNULIB_WCTOMB = @GNULIB_WCTOMB@
+GNULIB_WCTRANS = @GNULIB_WCTRANS@
+GNULIB_WCTYPE = @GNULIB_WCTYPE@
+GNULIB_WCWIDTH = @GNULIB_WCWIDTH@
+GNULIB_WMEMCHR = @GNULIB_WMEMCHR@
+GNULIB_WMEMCMP = @GNULIB_WMEMCMP@
+GNULIB_WMEMCPY = @GNULIB_WMEMCPY@
+GNULIB_WMEMMOVE = @GNULIB_WMEMMOVE@
+GNULIB_WMEMSET = @GNULIB_WMEMSET@
+GNULIB_WRITE = @GNULIB_WRITE@
+GNULIB__EXIT = @GNULIB__EXIT@
+GREP = @GREP@
+HAVE_ACCEPT4 = @HAVE_ACCEPT4@
+HAVE_ARPA_INET_H = @HAVE_ARPA_INET_H@
+HAVE_ATOLL = @HAVE_ATOLL@
+HAVE_BTOWC = @HAVE_BTOWC@
+HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@
+HAVE_CHOWN = @HAVE_CHOWN@
+HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@
+HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@
+HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@
+HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@
+HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@
+HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@
+HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@
+HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@
+HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@
+HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@
+HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@
+HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@
+HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@
+HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@
+HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@
+HAVE_DECL_INET_NTOP = @HAVE_DECL_INET_NTOP@
+HAVE_DECL_INET_PTON = @HAVE_DECL_INET_PTON@
+HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@
+HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
+HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
+HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@
+HAVE_DECL_SETENV = @HAVE_DECL_SETENV@
+HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@
+HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@
+HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
+HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@
+HAVE_DECL_STRNCASECMP = @HAVE_DECL_STRNCASECMP@
+HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
+HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@
+HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@
+HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@
+HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
+HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@
+HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@
+HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@
+HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@
+HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@
+HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@
+HAVE_DPRINTF = @HAVE_DPRINTF@
+HAVE_DUP2 = @HAVE_DUP2@
+HAVE_DUP3 = @HAVE_DUP3@
+HAVE_DUPLOCALE = @HAVE_DUPLOCALE@
+HAVE_EUIDACCESS = @HAVE_EUIDACCESS@
+HAVE_FACCESSAT = @HAVE_FACCESSAT@
+HAVE_FCHDIR = @HAVE_FCHDIR@
+HAVE_FCHMODAT = @HAVE_FCHMODAT@
+HAVE_FCHOWNAT = @HAVE_FCHOWNAT@
+HAVE_FCNTL = @HAVE_FCNTL@
+HAVE_FDATASYNC = @HAVE_FDATASYNC@
+HAVE_FEATURES_H = @HAVE_FEATURES_H@
+HAVE_FFS = @HAVE_FFS@
+HAVE_FFSL = @HAVE_FFSL@
+HAVE_FFSLL = @HAVE_FFSLL@
+HAVE_FSEEKO = @HAVE_FSEEKO@
+HAVE_FSTATAT = @HAVE_FSTATAT@
+HAVE_FSYNC = @HAVE_FSYNC@
+HAVE_FTELLO = @HAVE_FTELLO@
+HAVE_FTRUNCATE = @HAVE_FTRUNCATE@
+HAVE_FUTIMENS = @HAVE_FUTIMENS@
+HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@
+HAVE_GETGROUPS = @HAVE_GETGROUPS@
+HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@
+HAVE_GETLOGIN = @HAVE_GETLOGIN@
+HAVE_GETOPT_H = @HAVE_GETOPT_H@
+HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@
+HAVE_GETSUBOPT = @HAVE_GETSUBOPT@
+HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@
+HAVE_GRANTPT = @HAVE_GRANTPT@
+HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@
+HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_ISBLANK = @HAVE_ISBLANK@
+HAVE_ISWBLANK = @HAVE_ISWBLANK@
+HAVE_ISWCNTRL = @HAVE_ISWCNTRL@
+HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@
+HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@
+HAVE_LANGINFO_H = @HAVE_LANGINFO_H@
+HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@
+HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@
+HAVE_LCHMOD = @HAVE_LCHMOD@
+HAVE_LCHOWN = @HAVE_LCHOWN@
+HAVE_LIBSIGSEGV = @HAVE_LIBSIGSEGV@
+HAVE_LINK = @HAVE_LINK@
+HAVE_LINKAT = @HAVE_LINKAT@
+HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@
+HAVE_LSTAT = @HAVE_LSTAT@
+HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@
+HAVE_MBRLEN = @HAVE_MBRLEN@
+HAVE_MBRTOWC = @HAVE_MBRTOWC@
+HAVE_MBSINIT = @HAVE_MBSINIT@
+HAVE_MBSLEN = @HAVE_MBSLEN@
+HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@
+HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@
+HAVE_MEMCHR = @HAVE_MEMCHR@
+HAVE_MEMPCPY = @HAVE_MEMPCPY@
+HAVE_MKDIRAT = @HAVE_MKDIRAT@
+HAVE_MKDTEMP = @HAVE_MKDTEMP@
+HAVE_MKFIFO = @HAVE_MKFIFO@
+HAVE_MKFIFOAT = @HAVE_MKFIFOAT@
+HAVE_MKNOD = @HAVE_MKNOD@
+HAVE_MKNODAT = @HAVE_MKNODAT@
+HAVE_MKOSTEMP = @HAVE_MKOSTEMP@
+HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@
+HAVE_MKSTEMP = @HAVE_MKSTEMP@
+HAVE_MKSTEMPS = @HAVE_MKSTEMPS@
+HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@
+HAVE_NANOSLEEP = @HAVE_NANOSLEEP@
+HAVE_NETINET_IN_H = @HAVE_NETINET_IN_H@
+HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@
+HAVE_OPENAT = @HAVE_OPENAT@
+HAVE_OS_H = @HAVE_OS_H@
+HAVE_PCLOSE = @HAVE_PCLOSE@
+HAVE_PIPE = @HAVE_PIPE@
+HAVE_PIPE2 = @HAVE_PIPE2@
+HAVE_POPEN = @HAVE_POPEN@
+HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@
+HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@
+HAVE_PREAD = @HAVE_PREAD@
+HAVE_PSELECT = @HAVE_PSELECT@
+HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@
+HAVE_PTSNAME = @HAVE_PTSNAME@
+HAVE_PTSNAME_R = @HAVE_PTSNAME_R@
+HAVE_PWRITE = @HAVE_PWRITE@
+HAVE_RAISE = @HAVE_RAISE@
+HAVE_RANDOM = @HAVE_RANDOM@
+HAVE_RANDOM_H = @HAVE_RANDOM_H@
+HAVE_RANDOM_R = @HAVE_RANDOM_R@
+HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@
+HAVE_READLINK = @HAVE_READLINK@
+HAVE_READLINKAT = @HAVE_READLINKAT@
+HAVE_REALPATH = @HAVE_REALPATH@
+HAVE_RENAMEAT = @HAVE_RENAMEAT@
+HAVE_RPMATCH = @HAVE_RPMATCH@
+HAVE_SA_FAMILY_T = @HAVE_SA_FAMILY_T@
+HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@
+HAVE_SETENV = @HAVE_SETENV@
+HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@
+HAVE_SIGACTION = @HAVE_SIGACTION@
+HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@
+HAVE_SIGINFO_T = @HAVE_SIGINFO_T@
+HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
+HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
+HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
+HAVE_SIGSET_T = @HAVE_SIGSET_T@
+HAVE_SLEEP = @HAVE_SLEEP@
+HAVE_STDINT_H = @HAVE_STDINT_H@
+HAVE_STPCPY = @HAVE_STPCPY@
+HAVE_STPNCPY = @HAVE_STPNCPY@
+HAVE_STRCASECMP = @HAVE_STRCASECMP@
+HAVE_STRCASESTR = @HAVE_STRCASESTR@
+HAVE_STRCHRNUL = @HAVE_STRCHRNUL@
+HAVE_STRINGS_H = @HAVE_STRINGS_H@
+HAVE_STRPBRK = @HAVE_STRPBRK@
+HAVE_STRPTIME = @HAVE_STRPTIME@
+HAVE_STRSEP = @HAVE_STRSEP@
+HAVE_STRTOD = @HAVE_STRTOD@
+HAVE_STRTOLL = @HAVE_STRTOLL@
+HAVE_STRTOULL = @HAVE_STRTOULL@
+HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@
+HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@
+HAVE_STRUCT_SOCKADDR_STORAGE = @HAVE_STRUCT_SOCKADDR_STORAGE@
+HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY = @HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY@
+HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@
+HAVE_STRVERSCMP = @HAVE_STRVERSCMP@
+HAVE_SYMLINK = @HAVE_SYMLINK@
+HAVE_SYMLINKAT = @HAVE_SYMLINKAT@
+HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
+HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
+HAVE_SYS_IOCTL_H = @HAVE_SYS_IOCTL_H@
+HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@
+HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@
+HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@
+HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@
+HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
+HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_SYS_UIO_H = @HAVE_SYS_UIO_H@
+HAVE_TIMEGM = @HAVE_TIMEGM@
+HAVE_TIMEZONE_T = @HAVE_TIMEZONE_T@
+HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@
+HAVE_UNISTD_H = @HAVE_UNISTD_H@
+HAVE_UNLINKAT = @HAVE_UNLINKAT@
+HAVE_UNLOCKPT = @HAVE_UNLOCKPT@
+HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@
+HAVE_USLEEP = @HAVE_USLEEP@
+HAVE_UTIMENSAT = @HAVE_UTIMENSAT@
+HAVE_VASPRINTF = @HAVE_VASPRINTF@
+HAVE_VDPRINTF = @HAVE_VDPRINTF@
+HAVE_WCHAR_H = @HAVE_WCHAR_H@
+HAVE_WCHAR_T = @HAVE_WCHAR_T@
+HAVE_WCPCPY = @HAVE_WCPCPY@
+HAVE_WCPNCPY = @HAVE_WCPNCPY@
+HAVE_WCRTOMB = @HAVE_WCRTOMB@
+HAVE_WCSCASECMP = @HAVE_WCSCASECMP@
+HAVE_WCSCAT = @HAVE_WCSCAT@
+HAVE_WCSCHR = @HAVE_WCSCHR@
+HAVE_WCSCMP = @HAVE_WCSCMP@
+HAVE_WCSCOLL = @HAVE_WCSCOLL@
+HAVE_WCSCPY = @HAVE_WCSCPY@
+HAVE_WCSCSPN = @HAVE_WCSCSPN@
+HAVE_WCSDUP = @HAVE_WCSDUP@
+HAVE_WCSLEN = @HAVE_WCSLEN@
+HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@
+HAVE_WCSNCAT = @HAVE_WCSNCAT@
+HAVE_WCSNCMP = @HAVE_WCSNCMP@
+HAVE_WCSNCPY = @HAVE_WCSNCPY@
+HAVE_WCSNLEN = @HAVE_WCSNLEN@
+HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@
+HAVE_WCSPBRK = @HAVE_WCSPBRK@
+HAVE_WCSRCHR = @HAVE_WCSRCHR@
+HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@
+HAVE_WCSSPN = @HAVE_WCSSPN@
+HAVE_WCSSTR = @HAVE_WCSSTR@
+HAVE_WCSTOK = @HAVE_WCSTOK@
+HAVE_WCSWIDTH = @HAVE_WCSWIDTH@
+HAVE_WCSXFRM = @HAVE_WCSXFRM@
+HAVE_WCTRANS_T = @HAVE_WCTRANS_T@
+HAVE_WCTYPE_H = @HAVE_WCTYPE_H@
+HAVE_WCTYPE_T = @HAVE_WCTYPE_T@
+HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@
+HAVE_WINT_T = @HAVE_WINT_T@
+HAVE_WMEMCHR = @HAVE_WMEMCHR@
+HAVE_WMEMCMP = @HAVE_WMEMCMP@
+HAVE_WMEMCPY = @HAVE_WMEMCPY@
+HAVE_WMEMMOVE = @HAVE_WMEMMOVE@
+HAVE_WMEMSET = @HAVE_WMEMSET@
+HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@
+HAVE_XLOCALE_H = @HAVE_XLOCALE_H@
+HAVE__BOOL = @HAVE__BOOL@
+HAVE__EXIT = @HAVE__EXIT@
+HELP2MAN = @HELP2MAN@
+ICONV_CONST = @ICONV_CONST@
+ICONV_H = @ICONV_H@
+INCLUDE_NEXT = @INCLUDE_NEXT@
+INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@
+INET_PTON_LIB = @INET_PTON_LIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@
+INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBCSTACK = @LIBCSTACK@
+LIBDIFFUTILS_LIBDEPS = @LIBDIFFUTILS_LIBDEPS@
+LIBDIFFUTILS_LTLIBDEPS = @LIBDIFFUTILS_LTLIBDEPS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSIGSEGV = @LIBSIGSEGV@
+LIBSIGSEGV_PREFIX = @LIBSIGSEGV_PREFIX@
+LIBSOCKET = @LIBSOCKET@
+LIBTESTS_LIBDEPS = @LIBTESTS_LIBDEPS@
+LIBUNISTRING_UNISTR_H = @LIBUNISTRING_UNISTR_H@
+LIBUNISTRING_UNITYPES_H = @LIBUNISTRING_UNITYPES_H@
+LIBUNISTRING_UNIWIDTH_H = @LIBUNISTRING_UNIWIDTH_H@
+LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@
+LIB_NANOSLEEP = @LIB_NANOSLEEP@
+LIB_SELECT = @LIB_SELECT@
+LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@
+LOCALE_FR = @LOCALE_FR@
+LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@
+LOCALE_JA = @LOCALE_JA@
+LOCALE_TR_UTF8 = @LOCALE_TR_UTF8@
+LOCALE_ZH_CN = @LOCALE_ZH_CN@
+LTLIBCSTACK = @LTLIBCSTACK@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBSIGSEGV = @LTLIBSIGSEGV@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NETINET_IN_H = @NETINET_IN_H@
+NEXT_ARPA_INET_H = @NEXT_ARPA_INET_H@
+NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H = @NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H@
+NEXT_AS_FIRST_DIRECTIVE_CTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_CTYPE_H@
+NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@
+NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@
+NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@
+NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@
+NEXT_AS_FIRST_DIRECTIVE_ICONV_H = @NEXT_AS_FIRST_DIRECTIVE_ICONV_H@
+NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@
+NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@
+NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@
+NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H = @NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H@
+NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@
+NEXT_AS_FIRST_DIRECTIVE_STDARG_H = @NEXT_AS_FIRST_DIRECTIVE_STDARG_H@
+NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@
+NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@
+NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@
+NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@
+NEXT_AS_FIRST_DIRECTIVE_STRINGS_H = @NEXT_AS_FIRST_DIRECTIVE_STRINGS_H@
+NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_WAIT_H@
+NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@
+NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@
+NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@
+NEXT_CTYPE_H = @NEXT_CTYPE_H@
+NEXT_ERRNO_H = @NEXT_ERRNO_H@
+NEXT_FCNTL_H = @NEXT_FCNTL_H@
+NEXT_FLOAT_H = @NEXT_FLOAT_H@
+NEXT_GETOPT_H = @NEXT_GETOPT_H@
+NEXT_ICONV_H = @NEXT_ICONV_H@
+NEXT_INTTYPES_H = @NEXT_INTTYPES_H@
+NEXT_LANGINFO_H = @NEXT_LANGINFO_H@
+NEXT_LOCALE_H = @NEXT_LOCALE_H@
+NEXT_NETINET_IN_H = @NEXT_NETINET_IN_H@
+NEXT_SIGNAL_H = @NEXT_SIGNAL_H@
+NEXT_STDARG_H = @NEXT_STDARG_H@
+NEXT_STDDEF_H = @NEXT_STDDEF_H@
+NEXT_STDINT_H = @NEXT_STDINT_H@
+NEXT_STDIO_H = @NEXT_STDIO_H@
+NEXT_STDLIB_H = @NEXT_STDLIB_H@
+NEXT_STRINGS_H = @NEXT_STRINGS_H@
+NEXT_STRING_H = @NEXT_STRING_H@
+NEXT_SYS_IOCTL_H = @NEXT_SYS_IOCTL_H@
+NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@
+NEXT_SYS_SOCKET_H = @NEXT_SYS_SOCKET_H@
+NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@
+NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@
+NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@
+NEXT_SYS_UIO_H = @NEXT_SYS_UIO_H@
+NEXT_SYS_WAIT_H = @NEXT_SYS_WAIT_H@
+NEXT_TIME_H = @NEXT_TIME_H@
+NEXT_UNISTD_H = @NEXT_UNISTD_H@
+NEXT_WCHAR_H = @NEXT_WCHAR_H@
+NEXT_WCTYPE_H = @NEXT_WCTYPE_H@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PRAGMA_COLUMNS = @PRAGMA_COLUMNS@
+PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@
+PRIPTR_PREFIX = @PRIPTR_PREFIX@
+PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@
+PR_PROGRAM = @PR_PROGRAM@
+PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@
+PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+RANLIB = @RANLIB@
+REPLACE_BTOWC = @REPLACE_BTOWC@
+REPLACE_CALLOC = @REPLACE_CALLOC@
+REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@
+REPLACE_CHOWN = @REPLACE_CHOWN@
+REPLACE_CLOSE = @REPLACE_CLOSE@
+REPLACE_DPRINTF = @REPLACE_DPRINTF@
+REPLACE_DUP = @REPLACE_DUP@
+REPLACE_DUP2 = @REPLACE_DUP2@
+REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@
+REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@
+REPLACE_FCLOSE = @REPLACE_FCLOSE@
+REPLACE_FCNTL = @REPLACE_FCNTL@
+REPLACE_FDOPEN = @REPLACE_FDOPEN@
+REPLACE_FFLUSH = @REPLACE_FFLUSH@
+REPLACE_FOPEN = @REPLACE_FOPEN@
+REPLACE_FPRINTF = @REPLACE_FPRINTF@
+REPLACE_FPURGE = @REPLACE_FPURGE@
+REPLACE_FREOPEN = @REPLACE_FREOPEN@
+REPLACE_FSEEK = @REPLACE_FSEEK@
+REPLACE_FSEEKO = @REPLACE_FSEEKO@
+REPLACE_FSTAT = @REPLACE_FSTAT@
+REPLACE_FSTATAT = @REPLACE_FSTATAT@
+REPLACE_FTELL = @REPLACE_FTELL@
+REPLACE_FTELLO = @REPLACE_FTELLO@
+REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@
+REPLACE_FUTIMENS = @REPLACE_FUTIMENS@
+REPLACE_GETCWD = @REPLACE_GETCWD@
+REPLACE_GETDELIM = @REPLACE_GETDELIM@
+REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@
+REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@
+REPLACE_GETGROUPS = @REPLACE_GETGROUPS@
+REPLACE_GETLINE = @REPLACE_GETLINE@
+REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@
+REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@
+REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
+REPLACE_GMTIME = @REPLACE_GMTIME@
+REPLACE_ICONV = @REPLACE_ICONV@
+REPLACE_ICONV_OPEN = @REPLACE_ICONV_OPEN@
+REPLACE_ICONV_UTF = @REPLACE_ICONV_UTF@
+REPLACE_INET_NTOP = @REPLACE_INET_NTOP@
+REPLACE_INET_PTON = @REPLACE_INET_PTON@
+REPLACE_IOCTL = @REPLACE_IOCTL@
+REPLACE_ISATTY = @REPLACE_ISATTY@
+REPLACE_ISWBLANK = @REPLACE_ISWBLANK@
+REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@
+REPLACE_ITOLD = @REPLACE_ITOLD@
+REPLACE_LCHOWN = @REPLACE_LCHOWN@
+REPLACE_LINK = @REPLACE_LINK@
+REPLACE_LINKAT = @REPLACE_LINKAT@
+REPLACE_LOCALECONV = @REPLACE_LOCALECONV@
+REPLACE_LOCALTIME = @REPLACE_LOCALTIME@
+REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@
+REPLACE_LSEEK = @REPLACE_LSEEK@
+REPLACE_LSTAT = @REPLACE_LSTAT@
+REPLACE_MALLOC = @REPLACE_MALLOC@
+REPLACE_MBRLEN = @REPLACE_MBRLEN@
+REPLACE_MBRTOWC = @REPLACE_MBRTOWC@
+REPLACE_MBSINIT = @REPLACE_MBSINIT@
+REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@
+REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@
+REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@
+REPLACE_MBTOWC = @REPLACE_MBTOWC@
+REPLACE_MEMCHR = @REPLACE_MEMCHR@
+REPLACE_MEMMEM = @REPLACE_MEMMEM@
+REPLACE_MKDIR = @REPLACE_MKDIR@
+REPLACE_MKFIFO = @REPLACE_MKFIFO@
+REPLACE_MKNOD = @REPLACE_MKNOD@
+REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
+REPLACE_MKTIME = @REPLACE_MKTIME@
+REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@
+REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@
+REPLACE_NULL = @REPLACE_NULL@
+REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@
+REPLACE_OPEN = @REPLACE_OPEN@
+REPLACE_OPENAT = @REPLACE_OPENAT@
+REPLACE_PERROR = @REPLACE_PERROR@
+REPLACE_POPEN = @REPLACE_POPEN@
+REPLACE_PREAD = @REPLACE_PREAD@
+REPLACE_PRINTF = @REPLACE_PRINTF@
+REPLACE_PSELECT = @REPLACE_PSELECT@
+REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@
+REPLACE_PTSNAME = @REPLACE_PTSNAME@
+REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@
+REPLACE_PUTENV = @REPLACE_PUTENV@
+REPLACE_PWRITE = @REPLACE_PWRITE@
+REPLACE_QSORT_R = @REPLACE_QSORT_R@
+REPLACE_RAISE = @REPLACE_RAISE@
+REPLACE_RANDOM_R = @REPLACE_RANDOM_R@
+REPLACE_READ = @REPLACE_READ@
+REPLACE_READLINK = @REPLACE_READLINK@
+REPLACE_READLINKAT = @REPLACE_READLINKAT@
+REPLACE_REALLOC = @REPLACE_REALLOC@
+REPLACE_REALPATH = @REPLACE_REALPATH@
+REPLACE_REMOVE = @REPLACE_REMOVE@
+REPLACE_RENAME = @REPLACE_RENAME@
+REPLACE_RENAMEAT = @REPLACE_RENAMEAT@
+REPLACE_RMDIR = @REPLACE_RMDIR@
+REPLACE_SELECT = @REPLACE_SELECT@
+REPLACE_SETENV = @REPLACE_SETENV@
+REPLACE_SETLOCALE = @REPLACE_SETLOCALE@
+REPLACE_SLEEP = @REPLACE_SLEEP@
+REPLACE_SNPRINTF = @REPLACE_SNPRINTF@
+REPLACE_SPRINTF = @REPLACE_SPRINTF@
+REPLACE_STAT = @REPLACE_STAT@
+REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@
+REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@
+REPLACE_STPNCPY = @REPLACE_STPNCPY@
+REPLACE_STRCASESTR = @REPLACE_STRCASESTR@
+REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@
+REPLACE_STRDUP = @REPLACE_STRDUP@
+REPLACE_STRERROR = @REPLACE_STRERROR@
+REPLACE_STRERROR_R = @REPLACE_STRERROR_R@
+REPLACE_STRNCAT = @REPLACE_STRNCAT@
+REPLACE_STRNDUP = @REPLACE_STRNDUP@
+REPLACE_STRNLEN = @REPLACE_STRNLEN@
+REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@
+REPLACE_STRSTR = @REPLACE_STRSTR@
+REPLACE_STRTOD = @REPLACE_STRTOD@
+REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@
+REPLACE_STRTOK_R = @REPLACE_STRTOK_R@
+REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@
+REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@
+REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@
+REPLACE_SYMLINK = @REPLACE_SYMLINK@
+REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@
+REPLACE_TIMEGM = @REPLACE_TIMEGM@
+REPLACE_TMPFILE = @REPLACE_TMPFILE@
+REPLACE_TOWLOWER = @REPLACE_TOWLOWER@
+REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@
+REPLACE_UNLINK = @REPLACE_UNLINK@
+REPLACE_UNLINKAT = @REPLACE_UNLINKAT@
+REPLACE_UNSETENV = @REPLACE_UNSETENV@
+REPLACE_USLEEP = @REPLACE_USLEEP@
+REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@
+REPLACE_VASPRINTF = @REPLACE_VASPRINTF@
+REPLACE_VDPRINTF = @REPLACE_VDPRINTF@
+REPLACE_VFPRINTF = @REPLACE_VFPRINTF@
+REPLACE_VPRINTF = @REPLACE_VPRINTF@
+REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@
+REPLACE_VSPRINTF = @REPLACE_VSPRINTF@
+REPLACE_WCRTOMB = @REPLACE_WCRTOMB@
+REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@
+REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@
+REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@
+REPLACE_WCTOB = @REPLACE_WCTOB@
+REPLACE_WCTOMB = @REPLACE_WCTOMB@
+REPLACE_WCWIDTH = @REPLACE_WCWIDTH@
+REPLACE_WRITE = @REPLACE_WRITE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
+SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
+SRC_VERSION_C = @SRC_VERSION_C@
+STDALIGN_H = @STDALIGN_H@
+STDARG_H = @STDARG_H@
+STDBOOL_H = @STDBOOL_H@
+STDDEF_H = @STDDEF_H@
+STDINT_H = @STDINT_H@
+STRIP = @STRIP@
+SYS_IOCTL_H_HAVE_WINSOCK2_H = @SYS_IOCTL_H_HAVE_WINSOCK2_H@
+SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@
+SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@
+TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@
+UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@
+UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@
+UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@
+UNISTD_H_DEFINES_STRUCT_TIMESPEC = @UNISTD_H_DEFINES_STRUCT_TIMESPEC@
+UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@
+UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
+WERROR_CFLAGS = @WERROR_CFLAGS@
+WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@
+WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@
+WINT_T_SUFFIX = @WINT_T_SUFFIX@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_aux_dir = @abs_aux_dir@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gl_LIBOBJS = @gl_LIBOBJS@
+gl_LTLIBOBJS = @gl_LTLIBOBJS@
+gltests_LIBOBJS = @gltests_LIBOBJS@
+gltests_LTLIBOBJS = @gltests_LTLIBOBJS@
+gltests_WITNESS = @gltests_WITNESS@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+lispdir = @lispdir@
+localedir = $(datadir)/locale
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I../lib -I$(top_srcdir)/lib
+AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
+LDADD = \
+ libver.a \
+ ../lib/libdiffutils.a \
+ $(LIBCSTACK) \
+ $(LIBINTL) \
+ $(LIBICONV) \
+ $(LIBSIGSEGV) \
+ $(LIB_CLOCK_GETTIME)
+
+diff_LDADD = $(LDADD)
+cmp_LDADD = $(LDADD)
+sdiff_LDADD = $(LDADD)
+diff3_LDADD = $(LDADD)
+cmp_SOURCES = cmp.c
+diff3_SOURCES = diff3.c
+sdiff_SOURCES = sdiff.c
+diff_SOURCES = \
+ analyze.c context.c diff.c dir.c ed.c ifdef.c io.c \
+ normal.c side.c util.c
+
+noinst_HEADERS = diff.h system.h
+MOSTLYCLEANFILES = paths.h paths.ht
+gdiff = `echo diff|sed '$(transform)'`
+BUILT_SOURCES = paths.h version.c version.h
+noinst_LIBRARIES = libver.a
+nodist_libver_a_SOURCES = version.c version.h
+DISTCLEANFILES = version.c version.h
+MAINTAINERCLEANFILES = $(BUILT_SOURCES)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libver.a: $(libver_a_OBJECTS) $(libver_a_DEPENDENCIES) $(EXTRA_libver_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libver.a
+ $(AM_V_AR)$(libver_a_AR) libver.a $(libver_a_OBJECTS) $(libver_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libver.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+cmp$(EXEEXT): $(cmp_OBJECTS) $(cmp_DEPENDENCIES) $(EXTRA_cmp_DEPENDENCIES)
+ @rm -f cmp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(cmp_OBJECTS) $(cmp_LDADD) $(LIBS)
+
+diff$(EXEEXT): $(diff_OBJECTS) $(diff_DEPENDENCIES) $(EXTRA_diff_DEPENDENCIES)
+ @rm -f diff$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(diff_OBJECTS) $(diff_LDADD) $(LIBS)
+
+diff3$(EXEEXT): $(diff3_OBJECTS) $(diff3_DEPENDENCIES) $(EXTRA_diff3_DEPENDENCIES)
+ @rm -f diff3$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(diff3_OBJECTS) $(diff3_LDADD) $(LIBS)
+
+sdiff$(EXEEXT): $(sdiff_OBJECTS) $(sdiff_DEPENDENCIES) $(EXTRA_sdiff_DEPENDENCIES)
+ @rm -f sdiff$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sdiff_OBJECTS) $(sdiff_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/analyze.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diff.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diff3.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ed.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifdef.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdiff.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/side.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(am__set_depbase) && \
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $<; \
+@am__fastdepCC_TRUE@ if test $$? = 0; then $(am__mv) $$depbase.Tpo $$depbase.Po; \
+@am__fastdepCC_TRUE@ else rm -f $$depbase.Tpo; false; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(am__set_depbase) && \
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $$($(CYGPATH_W) $<); \
+@am__fastdepCC_TRUE@ if test $$? = 0; then $(am__mv) $$depbase.Tpo $$depbase.Po; \
+@am__fastdepCC_TRUE@ else rm -f $$depbase.Tpo; false; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $$($(CYGPATH_W) $<)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/analyze.Po
+ -rm -f ./$(DEPDIR)/cmp.Po
+ -rm -f ./$(DEPDIR)/context.Po
+ -rm -f ./$(DEPDIR)/diff.Po
+ -rm -f ./$(DEPDIR)/diff3.Po
+ -rm -f ./$(DEPDIR)/dir.Po
+ -rm -f ./$(DEPDIR)/ed.Po
+ -rm -f ./$(DEPDIR)/ifdef.Po
+ -rm -f ./$(DEPDIR)/io.Po
+ -rm -f ./$(DEPDIR)/normal.Po
+ -rm -f ./$(DEPDIR)/sdiff.Po
+ -rm -f ./$(DEPDIR)/side.Po
+ -rm -f ./$(DEPDIR)/util.Po
+ -rm -f ./$(DEPDIR)/version.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/analyze.Po
+ -rm -f ./$(DEPDIR)/cmp.Po
+ -rm -f ./$(DEPDIR)/context.Po
+ -rm -f ./$(DEPDIR)/diff.Po
+ -rm -f ./$(DEPDIR)/diff3.Po
+ -rm -f ./$(DEPDIR)/dir.Po
+ -rm -f ./$(DEPDIR)/ed.Po
+ -rm -f ./$(DEPDIR)/ifdef.Po
+ -rm -f ./$(DEPDIR)/io.Po
+ -rm -f ./$(DEPDIR)/normal.Po
+ -rm -f ./$(DEPDIR)/sdiff.Po
+ -rm -f ./$(DEPDIR)/side.Po
+ -rm -f ./$(DEPDIR)/util.Po
+ -rm -f ./$(DEPDIR)/version.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: all check install install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-binPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+cmp.$(OBJEXT) diff3.$(OBJEXT) diff.$(OBJEXT) sdiff.$(OBJEXT): paths.h
+paths.h: Makefile.am
+ $(AM_V_GEN)(echo '#define DEFAULT_DIFF_PROGRAM "'$(gdiff)'"' && \
+ echo '#define LOCALEDIR "$(localedir)"') >$@t && mv $@t $@
+version.c: Makefile
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)printf '#include <config.h>\n' > $@t
+ $(AM_V_at)printf 'char const *Version = "$(PACKAGE_VERSION)";\n' >> $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+version.h: Makefile
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)printf 'extern char const *Version;\n' > $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/analyze.c b/src/analyze.c
new file mode 100644
index 0000000..3981872
--- /dev/null
+++ b/src/analyze.c
@@ -0,0 +1,707 @@
+/* Analyze file differences for GNU DIFF.
+
+ Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007,
+ 2009-2013, 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "diff.h"
+#include <cmpbuf.h>
+#include <error.h>
+#include <file-type.h>
+#include <xalloc.h>
+
+/* The core of the Diff algorithm. */
+#define ELEMENT lin
+#define EQUAL(x,y) ((x) == (y))
+#define OFFSET lin
+#define EXTRA_CONTEXT_FIELDS /* none */
+#define NOTE_DELETE(c, xoff) (files[0].changed[files[0].realindexes[xoff]] = 1)
+#define NOTE_INSERT(c, yoff) (files[1].changed[files[1].realindexes[yoff]] = 1)
+#define USE_HEURISTIC 1
+#include <diffseq.h>
+
+/* 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[])
+{
+ int f;
+ lin i;
+ char *discarded[2];
+ lin *equiv_count[2];
+ lin *p;
+
+ /* Allocate our results. */
+ p = xmalloc ((filevec[0].buffered_lines + filevec[1].buffered_lines)
+ * (2 * sizeof *p));
+ 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 = zalloc (filevec[0].equiv_max * (2 * sizeof *p));
+ equiv_count[0] = p;
+ equiv_count[1] = p + filevec[0].equiv_max;
+
+ 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] = zalloc (filevec[0].buffered_lines
+ + filevec[1].buffered_lines);
+ discarded[1] = discarded[0] + filevec[0].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++)
+ {
+ size_t end = filevec[f].buffered_lines;
+ char *discards = discarded[f];
+ lin *counts = equiv_count[1 - f];
+ lin *equivs = filevec[f].equivs;
+ size_t many = 5;
+ size_t 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++)
+ {
+ lin 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++)
+ {
+ lin 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 lin j;
+ lin length;
+ lin 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 lin consec;
+ lin minimum = 1;
+ lin tem = length >> 2;
+
+ /* 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 (0 < (tem >>= 2))
+ minimum <<= 1;
+ 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];
+ lin end = filevec[f].buffered_lines;
+ lin j = 0;
+ for (i = 0; i < end; ++i)
+ if (minimal || discards[i] == 0)
+ {
+ filevec[f].undiscarded[j] = filevec[f].equivs[i];
+ filevec[f].realindexes[j++] = i;
+ }
+ else
+ filevec[f].changed[i] = 1;
+ filevec[f].nondiscarded_lines = j;
+ }
+
+ free (discarded[0]);
+ free (equiv_count[0]);
+}
+
+/* Adjust inserts/deletes of identical lines to join changes
+ as much as possible.
+
+ We do something when a run of changed lines include a
+ line at one end and have an excluded, identical line at the other.
+ We are free to choose which identical line is included.
+ 'compareseq' usually chooses the one at the beginning,
+ but usually it is cleaner to consider the following identical line
+ to be the "change". */
+
+static void
+shift_boundaries (struct file_data filevec[])
+{
+ int f;
+
+ for (f = 0; f < 2; f++)
+ {
+ char *changed = filevec[f].changed;
+ char *other_changed = filevec[1 - f].changed;
+ lin const *equivs = filevec[f].equivs;
+ lin i = 0;
+ lin j = 0;
+ lin i_end = filevec[f].buffered_lines;
+
+ while (1)
+ {
+ lin 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])
+ {
+ 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 (lin line0, lin line1, lin deleted, lin inserted,
+ struct change *old)
+{
+ struct change *new = xmalloc (sizeof *new);
+
+ new->line0 = line0;
+ new->line1 = line1;
+ new->inserted = inserted;
+ new->deleted = deleted;
+ new->link = old;
+ return new;
+}
+
+/* Scan the tables of which lines are inserted and deleted,
+ producing an edit script in reverse order. */
+
+static struct change *
+build_reverse_script (struct file_data const filevec[])
+{
+ struct change *script = 0;
+ char *changed0 = filevec[0].changed;
+ char *changed1 = filevec[1].changed;
+ lin len0 = filevec[0].buffered_lines;
+ lin len1 = filevec[1].buffered_lines;
+
+ /* Note that changedN[lenN] does exist, and is 0. */
+
+ lin i0 = 0, i1 = 0;
+
+ while (i0 < len0 || i1 < len1)
+ {
+ if (changed0[i0] | changed1[i1])
+ {
+ lin 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 (struct file_data const filevec[])
+{
+ struct change *script = 0;
+ char *changed0 = filevec[0].changed;
+ char *changed1 = filevec[1].changed;
+ lin i0 = filevec[0].buffered_lines, i1 = filevec[1].buffered_lines;
+
+ /* Note that changedN[-1] does exist, and is 0. */
+
+ while (i0 >= 0 || i1 >= 0)
+ {
+ if (changed0[i0 - 1] | changed1[i1 - 1])
+ {
+ lin line0 = i0, line1 = i1;
+
+ /* Find # lines changed here in each file. */
+ while (changed0[i0 - 1]) --i0;
+ while (changed1[i1 - 1]) --i1;
+
+ /* Record this change. */
+ script = add_change (i0, i1, line0 - i0, line1 - i1, script);
+ }
+
+ /* We have reached lines in the two files that match each other. */
+ i0--, i1--;
+ }
+
+ return script;
+}
+
+/* If CHANGES, briefly report that two files differed. */
+static void
+briefly_report (int changes, struct file_data const filevec[])
+{
+ if (changes)
+ message ((brief
+ ? _("Files %s and %s differ\n")
+ : _("Binary files %s and %s differ\n")),
+ file_label[0] ? file_label[0] : filevec[0].name,
+ file_label[1] ? file_label[1] : filevec[1].name);
+}
+
+/* Report the differences of two files. */
+int
+diff_2_files (struct comparison *cmp)
+{
+ int f;
+ struct change *e, *p;
+ struct change *script;
+ int changes;
+
+
+ /* If we have detected that either file is binary,
+ compare the two files as binary. This can happen
+ only when the first chunk is read.
+ Also, --brief without any --ignore-* options means
+ we can speed things up by treating the files as binary. */
+
+ if (read_files (cmp->file, files_can_be_treated_as_binary))
+ {
+ /* Files with different lengths must be different. */
+ if (cmp->file[0].stat.st_size != cmp->file[1].stat.st_size
+ && 0 < cmp->file[0].stat.st_size
+ && 0 < cmp->file[1].stat.st_size
+ && (cmp->file[0].desc < 0 || S_ISREG (cmp->file[0].stat.st_mode))
+ && (cmp->file[1].desc < 0 || S_ISREG (cmp->file[1].stat.st_mode)))
+ changes = 1;
+
+ /* Standard input equals itself. */
+ else if (cmp->file[0].desc == cmp->file[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 lcm_max = PTRDIFF_MAX - 1;
+ size_t buffer_size =
+ buffer_lcm (sizeof (word),
+ buffer_lcm (STAT_BLOCKSIZE (cmp->file[0].stat),
+ STAT_BLOCKSIZE (cmp->file[1].stat),
+ lcm_max),
+ lcm_max);
+ for (f = 0; f < 2; f++)
+ cmp->file[f].buffer = xrealloc (cmp->file[f].buffer, buffer_size);
+
+ for (;; cmp->file[0].buffered = cmp->file[1].buffered = 0)
+ {
+ /* Read a buffer's worth from both files. */
+ for (f = 0; f < 2; f++)
+ if (0 <= cmp->file[f].desc)
+ file_block_read (&cmp->file[f],
+ buffer_size - cmp->file[f].buffered);
+
+ /* If the buffers differ, the files differ. */
+ if (cmp->file[0].buffered != cmp->file[1].buffered
+ || memcmp (cmp->file[0].buffer,
+ cmp->file[1].buffer,
+ cmp->file[0].buffered))
+ {
+ changes = 1;
+ break;
+ }
+
+ /* If we reach end of file, the files are the same. */
+ if (cmp->file[0].buffered != buffer_size)
+ {
+ changes = 0;
+ break;
+ }
+ }
+ }
+
+ briefly_report (changes, cmp->file);
+ }
+ else
+ {
+ struct context ctxt;
+ lin diags;
+
+ /* 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 0, at each end of each vector. */
+
+ size_t s = cmp->file[0].buffered_lines + cmp->file[1].buffered_lines + 4;
+ char *flag_space = zalloc (s);
+ cmp->file[0].changed = flag_space + 1;
+ cmp->file[1].changed = flag_space + cmp->file[0].buffered_lines + 3;
+
+ /* 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 (cmp->file);
+
+ /* Now do the main comparison algorithm, considering just the
+ undiscarded lines. */
+
+ ctxt.xvec = cmp->file[0].undiscarded;
+ ctxt.yvec = cmp->file[1].undiscarded;
+ diags = (cmp->file[0].nondiscarded_lines
+ + cmp->file[1].nondiscarded_lines + 3);
+ ctxt.fdiag = xmalloc (diags * (2 * sizeof *ctxt.fdiag));
+ ctxt.bdiag = ctxt.fdiag + diags;
+ ctxt.fdiag += cmp->file[1].nondiscarded_lines + 1;
+ ctxt.bdiag += cmp->file[1].nondiscarded_lines + 1;
+
+ ctxt.heuristic = speed_large_files;
+
+ files[0] = cmp->file[0];
+ files[1] = cmp->file[1];
+
+ compareseq (0, cmp->file[0].nondiscarded_lines,
+ 0, cmp->file[1].nondiscarded_lines, &ctxt);
+
+ free (ctxt.fdiag - (cmp->file[1].nondiscarded_lines + 1));
+
+ /* Modify the results slightly to make them prettier
+ in cases where that can validly be done. */
+
+ shift_boundaries (cmp->file);
+
+ /* 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 (cmp->file);
+ else
+ script = build_script (cmp->file);
+
+ /* Set CHANGES if we had any diffs.
+ If some changes are ignored, we must scan the script to decide. */
+ if (ignore_blank_lines || ignore_regexp.fastmap)
+ {
+ struct change *next = script;
+ changes = 0;
+
+ while (next && changes == 0)
+ {
+ struct change *this, *end;
+ lin first0, last0, first1, last1;
+
+ /* Find a set of changes that belong together. */
+ this = next;
+ end = find_change (next);
+
+ /* Disconnect them from the rest of the changes, making them
+ a hunk, and remember the rest for next iteration. */
+ next = end->link;
+ end->link = 0;
+
+ /* Determine whether this hunk is really a difference. */
+ if (analyze_hunk (this, &first0, &last0, &first1, &last1))
+ changes = 1;
+
+ /* Reconnect the script so it will all be freed properly. */
+ end->link = next;
+ }
+ }
+ else
+ changes = (script != 0);
+
+ if (brief)
+ briefly_report (changes, cmp->file);
+ 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 (file_label[0] ? file_label[0] : cmp->file[0].name,
+ file_label[1] ? file_label[1] : cmp->file[1].name,
+ cmp->parent != 0);
+
+ switch (output_style)
+ {
+ case OUTPUT_CONTEXT:
+ print_context_script (script, false);
+ break;
+
+ case OUTPUT_UNIFIED:
+ print_context_script (script, true);
+ 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);
+ break;
+
+ default:
+ abort ();
+ }
+
+ finish_output ();
+ }
+ }
+
+ free (cmp->file[0].undiscarded);
+
+ free (flag_space);
+
+ for (f = 0; f < 2; f++)
+ {
+ free (cmp->file[f].equivs);
+ free (cmp->file[f].linbuf + cmp->file[f].linbuf_base);
+ }
+
+ for (e = script; e; e = p)
+ {
+ p = e->link;
+ free (e);
+ }
+
+ if (! ROBUST_OUTPUT_STYLE (output_style))
+ for (f = 0; f < 2; ++f)
+ if (cmp->file[f].missing_newline)
+ {
+ error (0, 0, "%s: %s\n",
+ file_label[f] ? file_label[f] : cmp->file[f].name,
+ _("No newline at end of file"));
+ changes = 2;
+ }
+ }
+
+ if (cmp->file[0].buffer != cmp->file[1].buffer)
+ free (cmp->file[0].buffer);
+ free (cmp->file[1].buffer);
+
+ return changes;
+}
diff --git a/src/cmp.c b/src/cmp.c
new file mode 100644
index 0000000..ba5553b
--- /dev/null
+++ b/src/cmp.c
@@ -0,0 +1,661 @@
+/* cmp - compare two files byte by byte
+
+ Copyright (C) 1990-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
+ 2015-2016 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "paths.h"
+
+#include <stdio.h>
+
+#include <c-stack.h>
+#include <cmpbuf.h>
+#include <error.h>
+#include <exitfail.h>
+#include <file-type.h>
+#include <getopt.h>
+#include <hard-locale.h>
+#include <inttostr.h>
+#include <progname.h>
+#include <unlocked-io.h>
+#include <version-etc.h>
+#include <xalloc.h>
+#include <binary-io.h>
+#include <xstrtol.h>
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "cmp"
+
+#define AUTHORS \
+ proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
+ proper_name ("David MacKenzie")
+
+#if defined LC_MESSAGES && ENABLE_NLS
+# define hard_locale_LC_MESSAGES hard_locale (LC_MESSAGES)
+#else
+# define hard_locale_LC_MESSAGES 0
+#endif
+
+static int cmp (void);
+static off_t file_position (int);
+static size_t block_compare (word const *, word const *) _GL_ATTRIBUTE_PURE;
+static size_t count_newlines (char *, size_t);
+static void sprintc (char *, unsigned char);
+
+/* Filenames of the compared files. */
+static char const *file[2];
+
+/* File descriptors of the files. */
+static int file_desc[2];
+
+/* Status of the files. */
+static struct stat stat_buf[2];
+
+/* Read buffers for the files. */
+static word *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[2];
+
+/* Number of bytes to compare. */
+static uintmax_t bytes = UINTMAX_MAX;
+
+/* Output format. */
+static enum comparison_type
+ {
+ type_first_diff, /* Print the first difference. */
+ type_all_diffs, /* Print all differences. */
+ type_no_stdout, /* Do not output to stdout; only stderr. */
+ type_status /* Exit status only. */
+ } comparison_type;
+
+/* If nonzero, print values of bytes quoted like cat -t does. */
+static bool opt_print_bytes;
+
+/* Values for long options that do not have single-letter equivalents. */
+enum
+{
+ HELP_OPTION = CHAR_MAX + 1
+};
+
+static struct option const long_options[] =
+{
+ {"print-bytes", 0, 0, 'b'},
+ {"print-chars", 0, 0, 'c'}, /* obsolescent as of diffutils 2.7.3 */
+ {"ignore-initial", 1, 0, 'i'},
+ {"verbose", 0, 0, 'l'},
+ {"bytes", 1, 0, 'n'},
+ {"silent", 0, 0, 's'},
+ {"quiet", 0, 0, 's'},
+ {"version", 0, 0, 'v'},
+ {"help", 0, 0, HELP_OPTION},
+ {0, 0, 0, 0}
+};
+
+static void try_help (char const *, char const *) __attribute__((noreturn));
+static void
+try_help (char const *reason_msgid, char const *operand)
+{
+ if (reason_msgid)
+ error (0, 0, _(reason_msgid), operand);
+ error (EXIT_TROUBLE, 0,
+ _("Try '%s --help' for more information."), program_name);
+ abort ();
+}
+
+static char const valid_suffixes[] = "kKMGTPEZY0";
+
+/* Update ignore_initial[F] according to the result of parsing an
+ *operand ARGPTR of --ignore-initial, updating *ARGPTR to point
+ *after the operand. If DELIMITER is nonzero, the operand may be
+ *followed by DELIMITER; otherwise it must be null-terminated. */
+static void
+specify_ignore_initial (int f, char **argptr, char delimiter)
+{
+ uintmax_t val;
+ char const *arg = *argptr;
+ strtol_error e = xstrtoumax (arg, argptr, 0, &val, valid_suffixes);
+ if (! (e == LONGINT_OK
+ || (e == LONGINT_INVALID_SUFFIX_CHAR && **argptr == delimiter))
+ || TYPE_MAXIMUM (off_t) < val)
+ try_help ("invalid --ignore-initial value '%s'", arg);
+ if (ignore_initial[f] < val)
+ ignore_initial[f] = val;
+}
+
+/* Specify the output format. */
+static void
+specify_comparison_type (enum comparison_type t)
+{
+ if (comparison_type && comparison_type != t)
+ try_help ("options -l and -s are incompatible", 0);
+ comparison_type = t;
+}
+
+static void
+check_stdout (void)
+{
+ if (ferror (stdout))
+ error (EXIT_TROUBLE, 0, "%s", _("write failed"));
+ else if (fclose (stdout) != 0)
+ error (EXIT_TROUBLE, errno, "%s", _("standard output"));
+}
+
+static char const * const option_help_msgid[] = {
+ N_("-b, --print-bytes print differing bytes"),
+ N_("-i, --ignore-initial=SKIP skip first SKIP bytes of both inputs"),
+ N_("-i, --ignore-initial=SKIP1:SKIP2 skip first SKIP1 bytes of FILE1 and\n"
+ " first SKIP2 bytes of FILE2"),
+ N_("-l, --verbose output byte numbers and differing byte values"),
+ N_("-n, --bytes=LIMIT compare at most LIMIT bytes"),
+ N_("-s, --quiet, --silent suppress all normal output"),
+ N_(" --help display this help and exit"),
+ N_("-v, --version output version information and exit"),
+ 0
+};
+
+static void
+usage (void)
+{
+ char const * const *p;
+
+ printf (_("Usage: %s [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\n"),
+ program_name);
+ printf ("%s\n", _("Compare two files byte by byte."));
+ printf ("\n%s\n\n",
+_("The optional SKIP1 and SKIP2 specify the number of bytes to skip\n"
+ "at the beginning of each file (zero by default)."));
+
+ fputs (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+ for (p = option_help_msgid; *p; p++)
+ printf (" %s\n", _(*p));
+ printf ("\n%s\n\n%s\n%s\n",
+ _("SKIP values may be followed by the following multiplicative suffixes:\n\
+kB 1000, K 1024, MB 1,000,000, M 1,048,576,\n\
+GB 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y."),
+ _("If a FILE is '-' or missing, read standard input."),
+ _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."));
+ emit_bug_reporting_address ();
+}
+
+int
+main (int argc, char **argv)
+{
+ int c, f, exit_status;
+ size_t words_per_buffer;
+
+ exit_failure = EXIT_TROUBLE;
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+ c_stack_action (0);
+
+ /* Parse command line options. */
+
+ while ((c = getopt_long (argc, argv, "bci:ln:sv", long_options, 0))
+ != -1)
+ switch (c)
+ {
+ case 'b':
+ case 'c': /* 'c' is obsolescent as of diffutils 2.7.3 */
+ opt_print_bytes = true;
+ break;
+
+ case 'i':
+ specify_ignore_initial (0, &optarg, ':');
+ if (*optarg++ == ':')
+ specify_ignore_initial (1, &optarg, 0);
+ else if (ignore_initial[1] < ignore_initial[0])
+ ignore_initial[1] = ignore_initial[0];
+ break;
+
+ case 'l':
+ specify_comparison_type (type_all_diffs);
+ break;
+
+ case 'n':
+ {
+ uintmax_t n;
+ if (xstrtoumax (optarg, 0, 0, &n, valid_suffixes) != LONGINT_OK)
+ try_help ("invalid --bytes value '%s'", optarg);
+ if (n < bytes)
+ bytes = n;
+ }
+ break;
+
+ case 's':
+ specify_comparison_type (type_status);
+ break;
+
+ case 'v':
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
+ AUTHORS, (char *) NULL);
+ check_stdout ();
+ return EXIT_SUCCESS;
+
+ case HELP_OPTION:
+ usage ();
+ check_stdout ();
+ return EXIT_SUCCESS;
+
+ default:
+ try_help (0, 0);
+ }
+
+ if (optind == argc)
+ try_help ("missing operand after '%s'", argv[argc - 1]);
+
+ file[0] = argv[optind++];
+ file[1] = optind < argc ? argv[optind++] : "-";
+
+ for (f = 0; f < 2 && optind < argc; f++)
+ {
+ char *arg = argv[optind++];
+ specify_ignore_initial (f, &arg, 0);
+ }
+
+ if (optind < argc)
+ try_help ("extra operand '%s'", argv[optind]);
+
+ for (f = 0; f < 2; f++)
+ {
+ /* If file[1] is "-", treat it first; this avoids a misdiagnostic if
+ stdin is closed and opening file[0] yields file descriptor 0. */
+ int f1 = f ^ (STREQ (file[1], "-"));
+
+ /* Two files with the same name and offset are identical.
+ But wait until we open the file once, for proper diagnostics. */
+ if (f && ignore_initial[0] == ignore_initial[1]
+ && file_name_cmp (file[0], file[1]) == 0)
+ return EXIT_SUCCESS;
+
+ if (STREQ (file[f1], "-"))
+ {
+ file_desc[f1] = STDIN_FILENO;
+ if (O_BINARY && ! isatty (STDIN_FILENO))
+ set_binary_mode (STDIN_FILENO, O_BINARY);
+ }
+ else
+ file_desc[f1] = open (file[f1], O_RDONLY | O_BINARY, 0);
+
+ if (file_desc[f1] < 0 || fstat (file_desc[f1], stat_buf + f1) != 0)
+ {
+ if (file_desc[f1] < 0 && comparison_type == type_status)
+ exit (EXIT_TROUBLE);
+ else
+ error (EXIT_TROUBLE, errno, "%s", file[f1]);
+ }
+ }
+
+ /* If the files are links to the same inode and have the same file position,
+ they are identical. */
+
+ if (0 < same_file (&stat_buf[0], &stat_buf[1])
+ && same_file_attributes (&stat_buf[0], &stat_buf[1])
+ && file_position (0) == file_position (1))
+ return EXIT_SUCCESS;
+
+ /* If output is redirected to the null device, we can avoid some of
+ the work. */
+
+ if (comparison_type != type_status)
+ {
+ struct stat outstat, nullstat;
+
+ if (fstat (STDOUT_FILENO, &outstat) == 0
+ && stat (NULL_DEVICE, &nullstat) == 0
+ && 0 < same_file (&outstat, &nullstat))
+ comparison_type = type_no_stdout;
+ }
+
+ /* 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
+ and if more bytes will be compared than are in the smaller file. */
+
+ 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 (s0 < 0)
+ s0 = 0;
+ if (s1 < 0)
+ s1 = 0;
+ if (s0 != s1 && MIN (s0, s1) < bytes)
+ exit (EXIT_FAILURE);
+ }
+
+ /* Get the optimal block size of the files. */
+
+ buf_size = buffer_lcm (STAT_BLOCKSIZE (stat_buf[0]),
+ STAT_BLOCKSIZE (stat_buf[1]),
+ PTRDIFF_MAX - sizeof (word));
+
+ /* Allocate word-aligned buffers, with space for sentinels at the end. */
+
+ words_per_buffer = (buf_size + 2 * sizeof (word) - 1) / sizeof (word);
+ buffer[0] = xmalloc (2 * sizeof (word) * words_per_buffer);
+ buffer[1] = buffer[0] + words_per_buffer;
+
+ exit_status = cmp ();
+
+ for (f = 0; f < 2; f++)
+ if (close (file_desc[f]) != 0)
+ error (EXIT_TROUBLE, errno, "%s", file[f]);
+ if (exit_status != EXIT_SUCCESS && comparison_type < type_no_stdout)
+ check_stdout ();
+ exit (exit_status);
+ return exit_status;
+}
+
+/* Compare the two files already open on 'file_desc[0]' and 'file_desc[1]',
+ using 'buffer[0]' and 'buffer[1]'.
+ Return EXIT_SUCCESS if identical, EXIT_FAILURE if different,
+ >1 if error. */
+
+static int
+cmp (void)
+{
+ off_t line_number = 1; /* Line number (1...) of difference. */
+ off_t byte_number = 1; /* Byte number (1...) of difference. */
+ uintmax_t remaining = bytes; /* Remaining number of bytes to compare. */
+ size_t read0, read1; /* Number of bytes read from each file. */
+ size_t first_diff; /* Offset (0...) in buffers of 1st diff. */
+ size_t smaller; /* The lesser of 'read0' and 'read1'. */
+ word *buffer0 = buffer[0];
+ word *buffer1 = buffer[1];
+ char *buf0 = (char *) buffer0;
+ char *buf1 = (char *) buffer1;
+ int differing = 0;
+ int f;
+ int offset_width IF_LINT (= 0);
+
+ if (comparison_type == type_all_diffs)
+ {
+ off_t byte_number_max = MIN (bytes, TYPE_MAXIMUM (off_t));
+
+ for (f = 0; f < 2; f++)
+ if (S_ISREG (stat_buf[f].st_mode))
+ {
+ off_t file_bytes = stat_buf[f].st_size - file_position (f);
+ if (file_bytes < byte_number_max)
+ byte_number_max = file_bytes;
+ }
+
+ for (offset_width = 1; (byte_number_max /= 10) != 0; offset_width++)
+ continue;
+ }
+
+ for (f = 0; f < 2; f++)
+ {
+ off_t ig = ignore_initial[f];
+ if (ig && file_position (f) == -1)
+ {
+ /* lseek failed; read and discard the ignored initial prefix. */
+ do
+ {
+ size_t bytes_to_read = MIN (ig, buf_size);
+ size_t r = block_read (file_desc[f], buf0, bytes_to_read);
+ if (r != bytes_to_read)
+ {
+ if (r == SIZE_MAX)
+ error (EXIT_TROUBLE, errno, "%s", file[f]);
+ break;
+ }
+ ig -= r;
+ }
+ while (ig);
+ }
+ }
+
+ do
+ {
+ size_t bytes_to_read = buf_size;
+
+ if (remaining != UINTMAX_MAX)
+ {
+ if (remaining < bytes_to_read)
+ bytes_to_read = remaining;
+ remaining -= bytes_to_read;
+ }
+
+ read0 = block_read (file_desc[0], buf0, bytes_to_read);
+ if (read0 == SIZE_MAX)
+ error (EXIT_TROUBLE, errno, "%s", file[0]);
+ read1 = block_read (file_desc[1], buf1, bytes_to_read);
+ if (read1 == SIZE_MAX)
+ error (EXIT_TROUBLE, errno, "%s", file[1]);
+
+ smaller = MIN (read0, read1);
+
+ /* Optimize the common case where the buffers are the same. */
+ if (memcmp (buf0, buf1, smaller) == 0)
+ first_diff = smaller;
+ else
+ {
+ /* Insert sentinels for the block compare. */
+ buf0[read0] = ~buf1[read0];
+ buf1[read1] = ~buf0[read1];
+
+ first_diff = block_compare (buffer0, buffer1);
+ }
+
+ byte_number += first_diff;
+ if (comparison_type == type_first_diff)
+ line_number += count_newlines (buf0, first_diff);
+
+ if (first_diff < smaller)
+ {
+ switch (comparison_type)
+ {
+ case type_first_diff:
+ {
+ char byte_buf[INT_BUFSIZE_BOUND (off_t)];
+ char line_buf[INT_BUFSIZE_BOUND (off_t)];
+ char const *byte_num = offtostr (byte_number, byte_buf);
+ char const *line_num = offtostr (line_number, line_buf);
+ if (!opt_print_bytes)
+ {
+ /* See POSIX 1003.1-2001 for this format. This
+ message is used only in the POSIX locale, so it
+ need not be translated. */
+ static char const char_message[] =
+ "%s %s differ: char %s, line %s\n";
+
+ /* The POSIX rationale recommends using the word
+ "byte" outside the POSIX locale. Some gettext
+ implementations translate even in the POSIX
+ locale if certain other environment variables
+ are set, so use "byte" if a translation is
+ available, or if outside the POSIX locale. */
+ static char const byte_msgid[] =
+ N_("%s %s differ: byte %s, line %s\n");
+ char const *byte_message = _(byte_msgid);
+ bool use_byte_message = (byte_message != byte_msgid
+ || hard_locale_LC_MESSAGES);
+
+ printf (use_byte_message ? byte_message : char_message,
+ file[0], file[1], byte_num, line_num);
+ }
+ else
+ {
+ unsigned char c0 = buf0[first_diff];
+ unsigned char c1 = buf1[first_diff];
+ char s0[5];
+ char s1[5];
+ sprintc (s0, c0);
+ sprintc (s1, c1);
+ printf (_("%s %s differ: byte %s, line %s is %3o %s %3o %s\n"),
+ file[0], file[1], byte_num, line_num,
+ c0, s0, c1, s1);
+ }
+ }
+ /* Fall through. */
+ case type_status:
+ return EXIT_FAILURE;
+
+ case type_all_diffs:
+ do
+ {
+ unsigned char c0 = buf0[first_diff];
+ unsigned char c1 = buf1[first_diff];
+ if (c0 != c1)
+ {
+ char byte_buf[INT_BUFSIZE_BOUND (off_t)];
+ char const *byte_num = offtostr (byte_number, byte_buf);
+ if (!opt_print_bytes)
+ {
+ /* See POSIX 1003.1-2001 for this format. */
+ printf ("%*s %3o %3o\n",
+ offset_width, byte_num, c0, c1);
+ }
+ else
+ {
+ char s0[5];
+ char s1[5];
+ sprintc (s0, c0);
+ sprintc (s1, c1);
+ printf ("%*s %3o %-4s %3o %s\n",
+ offset_width, byte_num, c0, s0, c1, s1);
+ }
+ }
+ byte_number++;
+ first_diff++;
+ }
+ while (first_diff < smaller);
+ differing = -1;
+ break;
+
+ case type_no_stdout:
+ differing = 1;
+ break;
+ }
+ }
+
+ if (read0 != read1)
+ {
+ if (differing <= 0 && comparison_type != type_status)
+ {
+ /* See POSIX 1003.1-2001 for this format. */
+ fprintf (stderr, _("cmp: EOF on %s\n"), file[read1 < read0]);
+ }
+
+ return EXIT_FAILURE;
+ }
+ }
+ while (differing <= 0 && read0 == buf_size);
+
+ return differing == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+/* Compare two blocks of memory P0 and P1 until they differ.
+ 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 (word const *p0, word const *p1)
+{
+ word const *l0, *l1;
+ char const *c0, *c1;
+
+ /* Find the rough position of the first difference by reading words,
+ not bytes. */
+
+ for (l0 = p0, l1 = p1; *l0 == *l1; l0++, l1++)
+ continue;
+
+ /* Find the exact differing position (endianness independent). */
+
+ for (c0 = (char const *) l0, c1 = (char const *) l1;
+ *c0 == *c1;
+ c0++, c1++)
+ continue;
+
+ return c0 - (char const *) p0;
+}
+
+/* Return the number of newlines in BUF, of size BUFSIZE,
+ where BUF[NBYTES] is available for use as a sentinel. */
+
+static size_t
+count_newlines (char *buf, size_t bufsize)
+{
+ size_t count = 0;
+ char *p;
+ char *lim = buf + bufsize;
+ *lim = '\n';
+ for (p = buf; (p = rawmemchr (p, '\n')) != lim; p++)
+ count++;
+ return count;
+}
+
+/* Put into BUF the unsigned char C, making unprintable bytes
+ visible by quoting like cat -t does. */
+
+static void
+sprintc (char *buf, unsigned char c)
+{
+ if (! isprint (c))
+ {
+ if (c >= 128)
+ {
+ *buf++ = 'M';
+ *buf++ = '-';
+ c -= 128;
+ }
+ if (c < 32)
+ {
+ *buf++ = '^';
+ c += 64;
+ }
+ else if (c == 127)
+ {
+ *buf++ = '^';
+ c = '?';
+ }
+ }
+
+ *buf++ = c;
+ *buf = 0;
+}
+
+/* Position file F to ignore_initial[F] bytes from its initial position,
+ and yield its new position. Don't try more than once. */
+
+static off_t
+file_position (int f)
+{
+ static bool positioned[2];
+ static off_t position[2];
+
+ if (! positioned[f])
+ {
+ positioned[f] = true;
+ position[f] = lseek (file_desc[f], ignore_initial[f], SEEK_CUR);
+ }
+ return position[f];
+}
diff --git a/src/context.c b/src/context.c
new file mode 100644
index 0000000..1a92a60
--- /dev/null
+++ b/src/context.c
@@ -0,0 +1,537 @@
+/* Context-format output routines for GNU DIFF.
+
+ Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
+ 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "diff.h"
+#include "c-ctype.h"
+#include <stat-time.h>
+#include <strftime.h>
+
+static char const *find_function (char const * const *, lin);
+static struct change *find_hunk (struct change *);
+static void mark_ignorable (struct change *);
+static void pr_context_hunk (struct change *);
+static void pr_unidiff_hunk (struct change *);
+
+/* Last place find_function started searching from. */
+static lin find_function_last_search;
+
+/* The value find_function returned when it started searching there. */
+static lin find_function_last_match;
+
+/* Print a label for a context diff, with a file name and date or a label. */
+
+static void
+print_context_label (char const *mark,
+ struct file_data *inf,
+ char const *name,
+ char const *label)
+{
+ if (label)
+ fprintf (outfile, "%s %s\n", mark, label);
+ else
+ {
+ char buf[MAX (INT_STRLEN_BOUND (int) + 32,
+ INT_STRLEN_BOUND (time_t) + 11)];
+ struct tm const *tm = localtime (&inf->stat.st_mtime);
+ int nsec = get_stat_mtime_ns (&inf->stat);
+ if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec)))
+ {
+ verify (TYPE_IS_INTEGER (time_t));
+ if (LONG_MIN <= TYPE_MINIMUM (time_t)
+ && TYPE_MAXIMUM (time_t) <= LONG_MAX)
+ {
+ long int sec = inf->stat.st_mtime;
+ sprintf (buf, "%ld.%.9d", sec, nsec);
+ }
+ else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX)
+ {
+ intmax_t sec = inf->stat.st_mtime;
+ sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec);
+ }
+ else
+ {
+ uintmax_t sec = inf->stat.st_mtime;
+ sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec);
+ }
+ }
+ fprintf (outfile, "%s %s\t%s\n", mark, name, buf);
+ }
+}
+
+/* Print a header for a context diff, with the file names and dates. */
+
+void
+print_context_header (struct file_data inf[], char const *const *names, bool unidiff)
+{
+ set_color_context (HEADER_CONTEXT);
+ if (unidiff)
+ {
+ print_context_label ("---", &inf[0], names[0], file_label[0]);
+ print_context_label ("+++", &inf[1], names[1], file_label[1]);
+ }
+ else
+ {
+ print_context_label ("***", &inf[0], names[0], file_label[0]);
+ print_context_label ("---", &inf[1], names[1], file_label[1]);
+ }
+ set_color_context (RESET_CONTEXT);
+}
+
+/* Print an edit script in context format. */
+
+void
+print_context_script (struct change *script, bool unidiff)
+{
+ if (ignore_blank_lines || ignore_regexp.fastmap)
+ mark_ignorable (script);
+ else
+ {
+ struct change *e;
+ for (e = script; e; e = e->link)
+ e->ignore = false;
+ }
+
+ find_function_last_search = - files[0].prefix_lines;
+ find_function_last_match = LIN_MAX;
+
+ if (unidiff)
+ print_script (script, find_hunk, pr_unidiff_hunk);
+ else
+ print_script (script, find_hunk, pr_context_hunk);
+}
+
+/* Print a pair of line numbers with a comma, translated for file FILE.
+ If the second number is not greater, use the first in place of it.
+
+ Args A and B are internal line numbers.
+ We print the translated (real) line numbers. */
+
+static void
+print_context_number_range (struct file_data const *file, lin a, lin b)
+{
+ long int trans_a, trans_b;
+ translate_range (file, a, b, &trans_a, &trans_b);
+
+ /* 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.
+
+ POSIX 1003.1-2001 requires two line numbers separated by a comma
+ even if the line numbers are the same. However, this does not
+ match existing practice and is surely an error in the
+ specification. */
+
+ if (trans_b <= trans_a)
+ fprintf (outfile, "%ld", trans_b);
+ else
+ fprintf (outfile, "%ld,%ld", trans_a, trans_b);
+}
+
+/* Print FUNCTION in a context header. */
+static void
+print_context_function (FILE *out, char const *function)
+{
+ int i, j;
+ putc (' ', out);
+ for (i = 0; c_isspace ((unsigned char) function[i]) && function[i] != '\n'; i++)
+ continue;
+ for (j = i; j < i + 40 && function[j] != '\n'; j++)
+ continue;
+ while (i < j && c_isspace ((unsigned char) function[j - 1]))
+ j--;
+ fwrite (function + i, sizeof (char), j - i, out);
+}
+
+/* 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 (struct change *hunk)
+{
+ lin first0, last0, first1, last1, i;
+ char const *prefix;
+ char const *function;
+ FILE *out;
+
+ /* Determine range of line numbers involved in each file. */
+
+ enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
+ if (! changes)
+ return;
+
+ /* Include a context's width before and after. */
+
+ i = - files[0].prefix_lines;
+ first0 = MAX (first0 - context, i);
+ first1 = MAX (first1 - context, i);
+ if (last0 < files[0].valid_lines - context)
+ last0 += context;
+ else
+ last0 = files[0].valid_lines - 1;
+ if (last1 < files[1].valid_lines - context)
+ last1 += context;
+ else
+ last1 = files[1].valid_lines - 1;
+
+ /* If desired, find the preceding function definition line in file 0. */
+ function = NULL;
+ if (function_regexp.fastmap)
+ function = find_function (files[0].linbuf, first0);
+
+ begin_output ();
+ out = outfile;
+
+ fputs ("***************", out);
+
+ if (function)
+ print_context_function (out, function);
+
+ putc ('\n', out);
+ set_color_context (LINE_NUMBER_CONTEXT);
+ fputs ("*** ", out);
+ print_context_number_range (&files[0], first0, last0);
+ fputs (" ****", out);
+ set_color_context (RESET_CONTEXT);
+ putc ('\n', out);
+
+ if (changes & OLD)
+ {
+ struct change *next = hunk;
+
+ if (first0 <= last0)
+ set_color_context (DELETE_CONTEXT);
+
+ 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_nl (prefix, &files[0].linbuf[i], true);
+ if (i == last0)
+ set_color_context (RESET_CONTEXT);
+ if (files[0].linbuf[i + 1][-1] == '\n')
+ putc ('\n', out);
+ }
+ }
+
+ set_color_context (LINE_NUMBER_CONTEXT);
+ fputs ("--- ", out);
+ print_context_number_range (&files[1], first1, last1);
+ fputs (" ----", out);
+ set_color_context (RESET_CONTEXT);
+ putc ('\n', out);
+
+ if (changes & NEW)
+ {
+ struct change *next = hunk;
+
+ if (first1 <= last1)
+ set_color_context (ADD_CONTEXT);
+
+ 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_nl (prefix, &files[1].linbuf[i], true);
+ if (i == last1)
+ set_color_context (RESET_CONTEXT);
+ if (files[1].linbuf[i + 1][-1] == '\n')
+ putc ('\n', out);
+ }
+ }
+}
+
+/* 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 (struct file_data const *file, lin a, lin b)
+{
+ long int trans_a, trans_b;
+ translate_range (file, a, b, &trans_a, &trans_b);
+
+ /* We can have B < A in the case of a range of no lines.
+ In this case, we print the line number before the range,
+ which is B. It would be more logical to print A, but
+ 'patch' expects B in order to detect diffs against empty files. */
+ if (trans_b <= trans_a)
+ fprintf (outfile, trans_b < trans_a ? "%ld,0" : "%ld", trans_b);
+ else
+ fprintf (outfile, "%ld,%ld", trans_a, trans_b - trans_a + 1);
+}
+
+/* Print a portion of an edit script in unidiff format.
+ HUNK is the beginning of the portion to be printed.
+ The end is marked by a 'link' that has been nulled out.
+
+ Prints out lines from both files, and precedes each
+ line with the appropriate flag-character. */
+
+static void
+pr_unidiff_hunk (struct change *hunk)
+{
+ lin first0, last0, first1, last1;
+ lin i, j, k;
+ struct change *next;
+ char const *function;
+ FILE *out;
+
+ /* Determine range of line numbers involved in each file. */
+
+ if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
+ return;
+
+ /* Include a context's width before and after. */
+
+ i = - files[0].prefix_lines;
+ first0 = MAX (first0 - context, i);
+ first1 = MAX (first1 - context, i);
+ if (last0 < files[0].valid_lines - context)
+ last0 += context;
+ else
+ last0 = files[0].valid_lines - 1;
+ if (last1 < files[1].valid_lines - context)
+ last1 += context;
+ else
+ last1 = files[1].valid_lines - 1;
+
+ /* If desired, find the preceding function definition line in file 0. */
+ function = NULL;
+ if (function_regexp.fastmap)
+ function = find_function (files[0].linbuf, first0);
+
+ begin_output ();
+ out = outfile;
+
+ set_color_context (LINE_NUMBER_CONTEXT);
+ fputs ("@@ -", out);
+ print_unidiff_number_range (&files[0], first0, last0);
+ fputs (" +", out);
+ print_unidiff_number_range (&files[1], first1, last1);
+ fputs (" @@", out);
+ set_color_context (RESET_CONTEXT);
+
+ if (function)
+ print_context_function (out, function);
+
+ 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)
+ {
+ char const *const *line = &files[0].linbuf[i++];
+ if (! (suppress_blank_empty && **line == '\n'))
+ putc (initial_tab ? '\t' : ' ', out);
+ print_1_line (NULL, line);
+ j++;
+ }
+ else
+ {
+ /* For each difference, first output the deleted part. */
+
+ k = next->deleted;
+ if (k)
+ set_color_context (DELETE_CONTEXT);
+
+ while (k--)
+ {
+ char const * const *line = &files[0].linbuf[i++];
+ putc ('-', out);
+ if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
+ putc ('\t', out);
+ print_1_line_nl (NULL, line, true);
+
+ if (!k)
+ set_color_context (RESET_CONTEXT);
+
+ if (line[1][-1] == '\n')
+ putc ('\n', out);
+ }
+
+ /* Then output the inserted part. */
+
+ k = next->inserted;
+ if (k)
+ set_color_context (ADD_CONTEXT);
+
+ while (k--)
+ {
+ char const * const *line = &files[1].linbuf[j++];
+ putc ('+', out);
+ if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
+ putc ('\t', out);
+ print_1_line_nl (NULL, line, true);
+
+ if (!k)
+ set_color_context (RESET_CONTEXT);
+
+ if (line[1][-1] == '\n')
+ putc ('\n', out);
+ }
+
+ /* We're done with this hunk, so on to the next! */
+
+ next = next->link;
+ }
+ }
+}
+
+/* Scan a (forward-ordered) edit script for the first place that more than
+ 2*CONTEXT unchanged lines appear, and return a pointer
+ to the 'struct change' for the last change before those lines. */
+
+static struct change * _GL_ATTRIBUTE_PURE
+find_hunk (struct change *start)
+{
+ struct change *prev;
+ lin top0, top1;
+ lin thresh;
+
+ /* Threshold distance is CONTEXT if the second change is ignorable,
+ 2 * CONTEXT + 1 otherwise. Integer overflow can't happen, due
+ to CONTEXT_LIM. */
+ lin ignorable_threshold = context;
+ lin non_ignorable_threshold = 2 * context + 1;
+
+ 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;
+ thresh = (start && start->ignore
+ ? ignorable_threshold
+ : non_ignorable_threshold);
+ /* 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 (struct change *script)
+{
+ while (script)
+ {
+ struct change *next = script->link;
+ lin first0, last0, first1, last1;
+
+ /* Turn this change into a hunk: detach it from the others. */
+ script->link = NULL;
+
+ /* Determine whether this change is ignorable. */
+ script->ignore = ! analyze_hunk (script,
+ &first0, &last0, &first1, &last1);
+
+ /* Reconnect the chain as before. */
+ script->link = next;
+
+ /* Advance to the following change. */
+ script = next;
+ }
+}
+
+/* Find the last function-header line in LINBUF prior to line number LINENUM.
+ This is a line containing a match for the regexp in 'function_regexp'.
+ Return the address of the text, or NULL if no function-header is found. */
+
+static char const *
+find_function (char const * const *linbuf, lin linenum)
+{
+ lin i = linenum;
+ lin last = find_function_last_search;
+ find_function_last_search = i;
+
+ while (last <= --i)
+ {
+ /* See if this line is what we want. */
+ char const *line = linbuf[i];
+ size_t linelen = linbuf[i + 1] - line - 1;
+
+ /* FIXME: re_search's size args should be size_t, not int. */
+ int len = MIN (linelen, INT_MAX);
+
+ if (0 <= re_search (&function_regexp, line, len, 0, len, NULL))
+ {
+ find_function_last_match = i;
+ return line;
+ }
+ }
+ /* If we search back to where we started searching the previous time,
+ find the line we found last time. */
+ if (find_function_last_match != LIN_MAX)
+ return linbuf[find_function_last_match];
+
+ return NULL;
+}
diff --git a/src/diff.c b/src/diff.c
new file mode 100644
index 0000000..686945e
--- /dev/null
+++ b/src/diff.c
@@ -0,0 +1,1472 @@
+/* diff - compare files line by line
+
+ Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
+ 2009-2013, 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#define GDIFF_MAIN
+#include "diff.h"
+#include <assert.h>
+#include "paths.h"
+#include <c-stack.h>
+#include <dirname.h>
+#include <error.h>
+#include <exclude.h>
+#include <exitfail.h>
+#include <filenamecat.h>
+#include <file-type.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <hard-locale.h>
+#include <prepargs.h>
+#include <progname.h>
+#include <sh-quote.h>
+#include <stat-time.h>
+#include <timespec.h>
+#include <version-etc.h>
+#include <xalloc.h>
+#include <xreadlink.h>
+#include <binary-io.h>
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "diff"
+
+#define AUTHORS \
+ proper_name ("Paul Eggert"), \
+ proper_name ("Mike Haertel"), \
+ proper_name ("David Hayes"), \
+ proper_name ("Richard Stallman"), \
+ proper_name ("Len Tower")
+
+#ifndef GUTTER_WIDTH_MINIMUM
+# define GUTTER_WIDTH_MINIMUM 3
+#endif
+
+struct regexp_list
+{
+ char *regexps; /* chars representing disjunction of the regexps */
+ size_t len; /* chars used in 'regexps' */
+ size_t size; /* size malloc'ed for 'regexps'; 0 if not malloc'ed */
+ bool multiple_regexps;/* Does 'regexps' represent a disjunction? */
+ struct re_pattern_buffer *buf;
+};
+
+static int compare_files (struct comparison const *, char const *, char const *);
+static void add_regexp (struct regexp_list *, char const *);
+static void summarize_regexp_list (struct regexp_list *);
+static void specify_style (enum output_style);
+static void specify_value (char const **, char const *, char const *);
+static void specify_colors_style (char const *);
+static void try_help (char const *, char const *) __attribute__((noreturn));
+static void check_stdout (void);
+static void usage (void);
+
+/* If comparing directories, compare their common subdirectories
+ recursively. */
+static bool recursive;
+
+/* In context diffs, show previous lines that match these regexps. */
+static struct regexp_list function_regexp_list;
+
+/* Ignore changes affecting only lines that match these regexps. */
+static struct regexp_list ignore_regexp_list;
+
+#if O_BINARY
+/* Use binary I/O when reading and writing data (--binary).
+ On POSIX hosts, this has no effect. */
+static bool binary;
+#else
+enum { binary = true };
+#endif
+
+/* If one file is missing, treat it as present but empty (-N). */
+static bool new_file;
+
+/* If the first file is missing, treat it as present but empty
+ (--unidirectional-new-file). */
+static bool unidirectional_new_file;
+
+/* Report files compared that are the same (-s).
+ Normally nothing is output when that happens. */
+static bool report_identical_files;
+
+static char const shortopts[] =
+"0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:yZ";
+
+/* Values for long options that do not have single-letter equivalents. */
+enum
+{
+ BINARY_OPTION = CHAR_MAX + 1,
+ FROM_FILE_OPTION,
+ HELP_OPTION,
+ HORIZON_LINES_OPTION,
+ IGNORE_FILE_NAME_CASE_OPTION,
+ INHIBIT_HUNK_MERGE_OPTION,
+ LEFT_COLUMN_OPTION,
+ LINE_FORMAT_OPTION,
+ NO_DEREFERENCE_OPTION,
+ NO_IGNORE_FILE_NAME_CASE_OPTION,
+ NORMAL_OPTION,
+ SDIFF_MERGE_ASSIST_OPTION,
+ STRIP_TRAILING_CR_OPTION,
+ SUPPRESS_BLANK_EMPTY_OPTION,
+ SUPPRESS_COMMON_LINES_OPTION,
+ TABSIZE_OPTION,
+ TO_FILE_OPTION,
+
+ /* These options must be in sequence. */
+ UNCHANGED_LINE_FORMAT_OPTION,
+ OLD_LINE_FORMAT_OPTION,
+ NEW_LINE_FORMAT_OPTION,
+
+ /* These options must be in sequence. */
+ UNCHANGED_GROUP_FORMAT_OPTION,
+ OLD_GROUP_FORMAT_OPTION,
+ NEW_GROUP_FORMAT_OPTION,
+ CHANGED_GROUP_FORMAT_OPTION,
+
+ COLOR_OPTION,
+ COLOR_PALETTE_OPTION,
+
+ PRESUME_OUTPUT_TTY_OPTION,
+};
+
+static char const group_format_option[][sizeof "--unchanged-group-format"] =
+ {
+ "--unchanged-group-format",
+ "--old-group-format",
+ "--new-group-format",
+ "--changed-group-format"
+ };
+
+static char const line_format_option[][sizeof "--unchanged-line-format"] =
+ {
+ "--unchanged-line-format",
+ "--old-line-format",
+ "--new-line-format"
+ };
+
+static struct option const longopts[] =
+{
+ {"binary", 0, 0, BINARY_OPTION},
+ {"brief", 0, 0, 'q'},
+ {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
+ {"color", 2, 0, COLOR_OPTION},
+ {"context", 2, 0, 'C'},
+ {"ed", 0, 0, 'e'},
+ {"exclude", 1, 0, 'x'},
+ {"exclude-from", 1, 0, 'X'},
+ {"expand-tabs", 0, 0, 't'},
+ {"forward-ed", 0, 0, 'f'},
+ {"from-file", 1, 0, FROM_FILE_OPTION},
+ {"help", 0, 0, HELP_OPTION},
+ {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
+ {"ifdef", 1, 0, 'D'},
+ {"ignore-all-space", 0, 0, 'w'},
+ {"ignore-blank-lines", 0, 0, 'B'},
+ {"ignore-case", 0, 0, 'i'},
+ {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
+ {"ignore-matching-lines", 1, 0, 'I'},
+ {"ignore-space-change", 0, 0, 'b'},
+ {"ignore-tab-expansion", 0, 0, 'E'},
+ {"ignore-trailing-space", 0, 0, 'Z'},
+ {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
+ {"initial-tab", 0, 0, 'T'},
+ {"label", 1, 0, 'L'},
+ {"left-column", 0, 0, LEFT_COLUMN_OPTION},
+ {"line-format", 1, 0, LINE_FORMAT_OPTION},
+ {"minimal", 0, 0, 'd'},
+ {"new-file", 0, 0, 'N'},
+ {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
+ {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
+ {"no-dereference", 0, 0, NO_DEREFERENCE_OPTION},
+ {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
+ {"normal", 0, 0, NORMAL_OPTION},
+ {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
+ {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
+ {"paginate", 0, 0, 'l'},
+ {"palette", 1, 0, COLOR_PALETTE_OPTION},
+ {"rcs", 0, 0, 'n'},
+ {"recursive", 0, 0, 'r'},
+ {"report-identical-files", 0, 0, 's'},
+ {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
+ {"show-c-function", 0, 0, 'p'},
+ {"show-function-line", 1, 0, 'F'},
+ {"side-by-side", 0, 0, 'y'},
+ {"speed-large-files", 0, 0, 'H'},
+ {"starting-file", 1, 0, 'S'},
+ {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
+ {"suppress-blank-empty", 0, 0, SUPPRESS_BLANK_EMPTY_OPTION},
+ {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
+ {"tabsize", 1, 0, TABSIZE_OPTION},
+ {"text", 0, 0, 'a'},
+ {"to-file", 1, 0, TO_FILE_OPTION},
+ {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
+ {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
+ {"unidirectional-new-file", 0, 0, 'P'},
+ {"unified", 2, 0, 'U'},
+ {"version", 0, 0, 'v'},
+ {"width", 1, 0, 'W'},
+
+ /* This is solely for testing. Do not document. */
+ {"-presume-output-tty", no_argument, NULL, PRESUME_OUTPUT_TTY_OPTION},
+ {0, 0, 0, 0}
+};
+
+/* 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 (char **optionvec, int count)
+{
+ int i;
+ size_t size = 1;
+ char *result;
+ char *p;
+
+ for (i = 0; i < count; i++)
+ size += 1 + shell_quote_length (optionvec[i]);
+
+ p = result = xmalloc (size);
+
+ for (i = 0; i < count; i++)
+ {
+ *p++ = ' ';
+ p = shell_quote_copy (p, optionvec[i]);
+ }
+
+ *p = '\0';
+ return result;
+}
+
+
+/* Return an option value suitable for add_exclude. */
+
+static int
+exclude_options (void)
+{
+ return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
+}
+
+int
+main (int argc, char **argv)
+{
+ int exit_status = EXIT_SUCCESS;
+ int c;
+ int i;
+ int prev = -1;
+ lin ocontext = -1;
+ bool explicit_context = false;
+ size_t width = 0;
+ bool show_c_function = false;
+ char const *from_file = NULL;
+ char const *to_file = NULL;
+ uintmax_t numval;
+ char *numend;
+
+ /* Do our initializations. */
+ exit_failure = EXIT_TROUBLE;
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+ c_stack_action (0);
+ function_regexp_list.buf = &function_regexp;
+ ignore_regexp_list.buf = &ignore_regexp;
+ re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
+ excluded = new_exclude ();
+
+ /* Decode the options. */
+
+ while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 0:
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ocontext = (! ISDIGIT (prev)
+ ? c - '0'
+ : (ocontext - (c - '0' <= CONTEXT_MAX % 10)
+ < CONTEXT_MAX / 10)
+ ? 10 * ocontext + (c - '0')
+ : CONTEXT_MAX);
+ break;
+
+ case 'a':
+ text = true;
+ break;
+
+ case 'b':
+ if (ignore_white_space < IGNORE_SPACE_CHANGE)
+ ignore_white_space = IGNORE_SPACE_CHANGE;
+ break;
+
+ case 'Z':
+ if (ignore_white_space < IGNORE_SPACE_CHANGE)
+ ignore_white_space |= IGNORE_TRAILING_SPACE;
+ break;
+
+ case 'B':
+ ignore_blank_lines = true;
+ break;
+
+ case 'C':
+ case 'U':
+ {
+ if (optarg)
+ {
+ numval = strtoumax (optarg, &numend, 10);
+ if (*numend)
+ try_help ("invalid context length '%s'", optarg);
+ if (CONTEXT_MAX < numval)
+ numval = CONTEXT_MAX;
+ }
+ else
+ numval = 3;
+
+ specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
+ if (context < numval)
+ context = numval;
+ explicit_context = true;
+ }
+ break;
+
+ case 'c':
+ specify_style (OUTPUT_CONTEXT);
+ if (context < 3)
+ context = 3;
+ break;
+
+ case 'd':
+ minimal = true;
+ break;
+
+ case 'D':
+ specify_style (OUTPUT_IFDEF);
+ {
+ static char const C_ifdef_group_formats[] =
+ "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%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,
+ 0,
+ optarg, optarg, 0,
+ optarg, optarg, 0,
+ optarg, optarg, optarg);
+ for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
+ {
+ specify_value (&group_format[i], b, "-D");
+ b += strlen (b) + 1;
+ }
+ }
+ break;
+
+ case 'e':
+ specify_style (OUTPUT_ED);
+ break;
+
+ case 'E':
+ if (ignore_white_space < IGNORE_SPACE_CHANGE)
+ ignore_white_space |= IGNORE_TAB_EXPANSION;
+ break;
+
+ case 'f':
+ specify_style (OUTPUT_FORWARD_ED);
+ break;
+
+ case 'F':
+ add_regexp (&function_regexp_list, optarg);
+ break;
+
+ case 'h':
+ /* Split the files into chunks for faster processing.
+ Usually does not change the result.
+
+ This currently has no effect. */
+ break;
+
+ case 'H':
+ speed_large_files = true;
+ break;
+
+ case 'i':
+ ignore_case = true;
+ break;
+
+ case 'I':
+ add_regexp (&ignore_regexp_list, optarg);
+ break;
+
+ case 'l':
+ if (!pr_program[0])
+ try_help ("pagination not supported on this host", NULL);
+ paginate = true;
+#ifdef SIGCHLD
+ /* Pagination requires forking and waiting, and
+ System V fork+wait does not work if SIGCHLD is ignored. */
+ signal (SIGCHLD, SIG_DFL);
+#endif
+ break;
+
+ case 'L':
+ 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':
+ specify_style (OUTPUT_RCS);
+ break;
+
+ case 'N':
+ new_file = true;
+ break;
+
+ case 'p':
+ show_c_function = true;
+ add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
+ break;
+
+ case 'P':
+ unidirectional_new_file = true;
+ break;
+
+ case 'q':
+ brief = true;
+ break;
+
+ case 'r':
+ recursive = true;
+ break;
+
+ case 's':
+ report_identical_files = true;
+ break;
+
+ case 'S':
+ specify_value (&starting_file, optarg, "-S");
+ break;
+
+ case 't':
+ expand_tabs = true;
+ break;
+
+ case 'T':
+ initial_tab = true;
+ break;
+
+ case 'u':
+ specify_style (OUTPUT_UNIFIED);
+ if (context < 3)
+ context = 3;
+ break;
+
+ case 'v':
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
+ AUTHORS, (char *) NULL);
+ check_stdout ();
+ return EXIT_SUCCESS;
+
+ case 'w':
+ ignore_white_space = IGNORE_ALL_SPACE;
+ break;
+
+ case 'x':
+ add_exclude (excluded, optarg, exclude_options ());
+ break;
+
+ case 'X':
+ if (add_exclude_file (add_exclude, excluded, optarg,
+ exclude_options (), '\n'))
+ pfatal_with_name (optarg);
+ break;
+
+ case 'y':
+ specify_style (OUTPUT_SDIFF);
+ break;
+
+ case 'W':
+ numval = strtoumax (optarg, &numend, 10);
+ if (! (0 < numval && numval <= SIZE_MAX) || *numend)
+ try_help ("invalid width '%s'", optarg);
+ if (width != numval)
+ {
+ if (width)
+ fatal ("conflicting width options");
+ width = numval;
+ }
+ break;
+
+ case BINARY_OPTION:
+#if O_BINARY
+ binary = true;
+ if (! isatty (STDOUT_FILENO))
+ set_binary_mode (STDOUT_FILENO, O_BINARY);
+#endif
+ break;
+
+ case FROM_FILE_OPTION:
+ specify_value (&from_file, optarg, "--from-file");
+ break;
+
+ case HELP_OPTION:
+ usage ();
+ check_stdout ();
+ return EXIT_SUCCESS;
+
+ case HORIZON_LINES_OPTION:
+ numval = strtoumax (optarg, &numend, 10);
+ if (*numend)
+ try_help ("invalid horizon length '%s'", optarg);
+ horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
+ break;
+
+ case IGNORE_FILE_NAME_CASE_OPTION:
+ ignore_file_name_case = true;
+ break;
+
+ case INHIBIT_HUNK_MERGE_OPTION:
+ /* This option is obsolete, but accept it for backward
+ compatibility. */
+ break;
+
+ case LEFT_COLUMN_OPTION:
+ left_column = true;
+ break;
+
+ case LINE_FORMAT_OPTION:
+ specify_style (OUTPUT_IFDEF);
+ for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
+ specify_value (&line_format[i], optarg, "--line-format");
+ break;
+
+ case NO_DEREFERENCE_OPTION:
+ no_dereference_symlinks = true;
+ break;
+
+ case NO_IGNORE_FILE_NAME_CASE_OPTION:
+ ignore_file_name_case = false;
+ break;
+
+ case NORMAL_OPTION:
+ specify_style (OUTPUT_NORMAL);
+ break;
+
+ case SDIFF_MERGE_ASSIST_OPTION:
+ specify_style (OUTPUT_SDIFF);
+ sdiff_merge_assist = true;
+ break;
+
+ case STRIP_TRAILING_CR_OPTION:
+ strip_trailing_cr = true;
+ break;
+
+ case SUPPRESS_BLANK_EMPTY_OPTION:
+ suppress_blank_empty = true;
+ break;
+
+ case SUPPRESS_COMMON_LINES_OPTION:
+ suppress_common_lines = true;
+ break;
+
+ case TABSIZE_OPTION:
+ numval = strtoumax (optarg, &numend, 10);
+ if (! (0 < numval && numval <= SIZE_MAX - GUTTER_WIDTH_MINIMUM)
+ || *numend)
+ try_help ("invalid tabsize '%s'", optarg);
+ if (tabsize != numval)
+ {
+ if (tabsize)
+ fatal ("conflicting tabsize options");
+ tabsize = numval;
+ }
+ break;
+
+ case TO_FILE_OPTION:
+ specify_value (&to_file, optarg, "--to-file");
+ break;
+
+ case UNCHANGED_LINE_FORMAT_OPTION:
+ case OLD_LINE_FORMAT_OPTION:
+ case NEW_LINE_FORMAT_OPTION:
+ specify_style (OUTPUT_IFDEF);
+ c -= UNCHANGED_LINE_FORMAT_OPTION;
+ specify_value (&line_format[c], optarg, line_format_option[c]);
+ break;
+
+ case UNCHANGED_GROUP_FORMAT_OPTION:
+ case OLD_GROUP_FORMAT_OPTION:
+ case NEW_GROUP_FORMAT_OPTION:
+ case CHANGED_GROUP_FORMAT_OPTION:
+ specify_style (OUTPUT_IFDEF);
+ c -= UNCHANGED_GROUP_FORMAT_OPTION;
+ specify_value (&group_format[c], optarg, group_format_option[c]);
+ break;
+
+ case COLOR_OPTION:
+ specify_colors_style (optarg);
+ break;
+
+ case COLOR_PALETTE_OPTION:
+ set_color_palette (optarg);
+ break;
+
+ case PRESUME_OUTPUT_TTY_OPTION:
+ presume_output_tty = true;
+ break;
+
+ default:
+ try_help (NULL, NULL);
+ }
+ prev = c;
+ }
+
+ if (colors_style == AUTO)
+ {
+ char const *t = getenv ("TERM");
+ if (t && STREQ (t, "dumb"))
+ colors_style = NEVER;
+ }
+
+ if (output_style == OUTPUT_UNSPECIFIED)
+ {
+ if (show_c_function)
+ {
+ specify_style (OUTPUT_CONTEXT);
+ if (ocontext < 0)
+ context = 3;
+ }
+ else
+ specify_style (OUTPUT_NORMAL);
+ }
+
+ if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
+ {
+#if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
+ || defined HAVE_STRUCT_STAT_ST_SPARE1)
+ time_format = "%Y-%m-%d %H:%M:%S.%N %z";
+#else
+ time_format = "%Y-%m-%d %H:%M:%S %z";
+#endif
+ }
+ else
+ {
+ /* See POSIX 1003.1-2001 for this format. */
+ time_format = "%a %b %e %T %Y";
+ }
+
+ if (0 <= ocontext
+ && (output_style == OUTPUT_CONTEXT
+ || output_style == OUTPUT_UNIFIED)
+ && (context < ocontext
+ || (ocontext < context && ! explicit_context)))
+ context = ocontext;
+
+ if (! tabsize)
+ tabsize = 8;
+ if (! width)
+ width = 130;
+
+ {
+ /* 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. */
+
+ size_t t = expand_tabs ? 1 : tabsize;
+ size_t w = width;
+ size_t t_plus_g = t + GUTTER_WIDTH_MINIMUM;
+ size_t unaligned_off = (w >> 1) + (t_plus_g >> 1) + (w & t_plus_g & 1);
+ size_t off = unaligned_off - unaligned_off % t;
+ sdiff_half_width = (off <= GUTTER_WIDTH_MINIMUM || w <= off
+ ? 0
+ : MIN (off - GUTTER_WIDTH_MINIMUM, w - off));
+ sdiff_column2_offset = sdiff_half_width ? off : w;
+ }
+
+ /* Make the horizon at least as large as the context, so that
+ shift_boundaries has more freedom to shift the first and last hunks. */
+ if (horizon_lines < context)
+ horizon_lines = context;
+
+ summarize_regexp_list (&function_regexp_list);
+ summarize_regexp_list (&ignore_regexp_list);
+
+ if (output_style == OUTPUT_IFDEF)
+ {
+ for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
+ if (!line_format[i])
+ line_format[i] = "%l\n";
+ if (!group_format[OLD])
+ group_format[OLD]
+ = group_format[CHANGED] ? group_format[CHANGED] : "%<";
+ if (!group_format[NEW])
+ group_format[NEW]
+ = group_format[CHANGED] ? group_format[CHANGED] : "%>";
+ 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]
+ || (STREQ (group_format[UNCHANGED], "%=")
+ && !*line_format[UNCHANGED]))
+ : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
+
+ files_can_be_treated_as_binary =
+ (brief & binary
+ & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
+ | (ignore_regexp_list.regexps || ignore_white_space)));
+
+ switch_string = option_list (argv + 1, optind - 1);
+
+ if (from_file)
+ {
+ if (to_file)
+ fatal ("--from-file and --to-file both specified");
+ else
+ for (; optind < argc; optind++)
+ {
+ int status = compare_files (NULL, from_file, argv[optind]);
+ if (exit_status < status)
+ exit_status = status;
+ }
+ }
+ else
+ {
+ if (to_file)
+ for (; optind < argc; optind++)
+ {
+ int status = compare_files (NULL, argv[optind], to_file);
+ if (exit_status < status)
+ exit_status = status;
+ }
+ else
+ {
+ if (argc - optind != 2)
+ {
+ if (argc - optind < 2)
+ try_help ("missing operand after '%s'", argv[argc - 1]);
+ else
+ try_help ("extra operand '%s'", argv[optind + 2]);
+ }
+
+ exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
+ }
+ }
+
+ /* Print any messages that were saved up for last. */
+ print_message_queue ();
+
+ check_stdout ();
+ exit (exit_status);
+ return exit_status;
+}
+
+/* Append to REGLIST the regexp PATTERN. */
+
+static void
+add_regexp (struct regexp_list *reglist, char const *pattern)
+{
+ size_t patlen = strlen (pattern);
+ char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
+
+ if (m != 0)
+ error (0, 0, "%s: %s", pattern, m);
+ else
+ {
+ char *regexps = reglist->regexps;
+ size_t len = reglist->len;
+ bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
+ size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
+ size_t size = reglist->size;
+
+ if (size <= newlen)
+ {
+ if (!size)
+ size = 1;
+
+ do size *= 2;
+ while (size <= newlen);
+
+ reglist->size = size;
+ reglist->regexps = regexps = xrealloc (regexps, size);
+ }
+ if (multiple_regexps)
+ {
+ regexps[len++] = '\\';
+ regexps[len++] = '|';
+ }
+ memcpy (regexps + len, pattern, patlen + 1);
+ }
+}
+
+/* Ensure that REGLIST represents the disjunction of its regexps.
+ This is done here, rather than earlier, to avoid O(N^2) behavior. */
+
+static void
+summarize_regexp_list (struct regexp_list *reglist)
+{
+ if (reglist->regexps)
+ {
+ /* At least one regexp was specified. Allocate a fastmap for it. */
+ reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
+ if (reglist->multiple_regexps)
+ {
+ /* Compile the disjunction of the regexps.
+ (If just one regexp was specified, it is already compiled.) */
+ char const *m = re_compile_pattern (reglist->regexps, reglist->len,
+ reglist->buf);
+ if (m)
+ error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
+ }
+ }
+}
+
+static void
+try_help (char const *reason_msgid, char const *operand)
+{
+ if (reason_msgid)
+ error (0, 0, _(reason_msgid), operand);
+ error (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
+ program_name);
+ abort ();
+}
+
+static void
+check_stdout (void)
+{
+ if (ferror (stdout))
+ fatal ("write failed");
+ else if (fclose (stdout) != 0)
+ pfatal_with_name (_("standard output"));
+}
+
+static char const * const option_help_msgid[] = {
+ N_(" --normal output a normal diff (the default)"),
+ N_("-q, --brief report only when files differ"),
+ N_("-s, --report-identical-files report when two files are the same"),
+ N_("-c, -C NUM, --context[=NUM] output NUM (default 3) lines of copied context"),
+ N_("-u, -U NUM, --unified[=NUM] output NUM (default 3) lines of unified context"),
+ N_("-e, --ed output an ed script"),
+ N_("-n, --rcs output an RCS format diff"),
+ N_("-y, --side-by-side output in two columns"),
+ N_("-W, --width=NUM output at most NUM (default 130) print columns"),
+ N_(" --left-column output only the left column of common lines"),
+ N_(" --suppress-common-lines do not output common lines"),
+ "",
+ N_("-p, --show-c-function show which C function each change is in"),
+ N_("-F, --show-function-line=RE show the most recent line matching RE"),
+ N_(" --label LABEL use LABEL instead of file name and timestamp\n"
+ " (can be repeated)"),
+ "",
+ N_("-t, --expand-tabs expand tabs to spaces in output"),
+ N_("-T, --initial-tab make tabs line up by prepending a tab"),
+ N_(" --tabsize=NUM tab stops every NUM (default 8) print columns"),
+ N_(" --suppress-blank-empty suppress space or tab before empty output lines"),
+ N_("-l, --paginate pass output through 'pr' to paginate it"),
+ "",
+ N_("-r, --recursive recursively compare any subdirectories found"),
+ N_(" --no-dereference don't follow symbolic links"),
+ N_("-N, --new-file treat absent files as empty"),
+ N_(" --unidirectional-new-file treat absent first files as empty"),
+ N_(" --ignore-file-name-case ignore case when comparing file names"),
+ N_(" --no-ignore-file-name-case consider case when comparing file names"),
+ N_("-x, --exclude=PAT exclude files that match PAT"),
+ N_("-X, --exclude-from=FILE exclude files that match any pattern in FILE"),
+ N_("-S, --starting-file=FILE start with FILE when comparing directories"),
+ N_(" --from-file=FILE1 compare FILE1 to all operands;\n"
+ " FILE1 can be a directory"),
+ N_(" --to-file=FILE2 compare all operands to FILE2;\n"
+ " FILE2 can be a directory"),
+ "",
+ N_("-i, --ignore-case ignore case differences in file contents"),
+ N_("-E, --ignore-tab-expansion ignore changes due to tab expansion"),
+ N_("-Z, --ignore-trailing-space ignore white space at line end"),
+ N_("-b, --ignore-space-change ignore changes in the amount of white space"),
+ N_("-w, --ignore-all-space ignore all white space"),
+ N_("-B, --ignore-blank-lines ignore changes where lines are all blank"),
+ N_("-I, --ignore-matching-lines=RE ignore changes where all lines match RE"),
+ "",
+ N_("-a, --text treat all files as text"),
+ N_(" --strip-trailing-cr strip trailing carriage return on input"),
+#if O_BINARY
+ N_(" --binary read and write data in binary mode"),
+#endif
+ "",
+ N_("-D, --ifdef=NAME output merged file with '#ifdef NAME' diffs"),
+ N_(" --GTYPE-group-format=GFMT format GTYPE input groups with GFMT"),
+ N_(" --line-format=LFMT format all input lines with LFMT"),
+ N_(" --LTYPE-line-format=LFMT format LTYPE input lines with LFMT"),
+ N_(" These format options provide fine-grained control over the output\n"
+ " of diff, generalizing -D/--ifdef."),
+ N_(" LTYPE is 'old', 'new', or 'unchanged'. GTYPE is LTYPE or 'changed'."),
+ N_(" GFMT (only) may contain:\n\
+ %< lines from FILE1\n\
+ %> lines from FILE2\n\
+ %= lines common to FILE1 and FILE2\n\
+ %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\
+ LETTERs are as follows for new group, lower case for old group:\n\
+ F first line number\n\
+ L last line number\n\
+ N number of lines = L-F+1\n\
+ E F-1\n\
+ M L+1\n\
+ %(A=B?T:E) if A equals B then T else E"),
+ N_(" LFMT (only) may contain:\n\
+ %L contents of line\n\
+ %l contents of line, excluding any trailing newline\n\
+ %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"),
+ N_(" Both GFMT and LFMT may contain:\n\
+ %% %\n\
+ %c'C' the single character C\n\
+ %c'\\OOO' the character with octal code OOO\n\
+ C the character C (other characters represent themselves)"),
+ "",
+ N_("-d, --minimal try hard to find a smaller set of changes"),
+ N_(" --horizon-lines=NUM keep NUM lines of the common prefix and suffix"),
+ N_(" --speed-large-files assume large files and many scattered small changes"),
+ N_(" --color[=WHEN] colorize the output; WHEN can be 'never', 'always',"),
+ N_(" or 'auto' (the default)"),
+ N_(" --palette=PALETTE specify the colors to use when --color is active"),
+ N_(" PALETTE is a colon-separated list terminfo capabilities"),
+ "",
+ N_(" --help display this help and exit"),
+ N_("-v, --version output version information and exit"),
+ "",
+ N_("FILES are 'FILE1 FILE2' or 'DIR1 DIR2' or 'DIR FILE' or 'FILE DIR'."),
+ N_("If --from-file or --to-file is given, there are no restrictions on FILE(s)."),
+ N_("If a FILE is '-', read standard input."),
+ N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
+ 0
+};
+
+static void
+usage (void)
+{
+ char const * const *p;
+
+ printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
+ printf ("%s\n\n", _("Compare FILES line by line."));
+
+ fputs (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+
+ for (p = option_help_msgid; *p; p++)
+ {
+ if (!**p)
+ putchar ('\n');
+ else
+ {
+ char const *msg = _(*p);
+ char const *nl;
+ while ((nl = strchr (msg, '\n')))
+ {
+ int msglen = nl + 1 - msg;
+ printf (" %.*s", msglen, msg);
+ msg = nl + 1;
+ }
+
+ printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
+ }
+ }
+ emit_bug_reporting_address ();
+}
+
+/* Set VAR to VALUE, reporting an OPTION error if this is a
+ conflict. */
+static void
+specify_value (char const **var, char const *value, char const *option)
+{
+ if (*var && ! STREQ (*var, value))
+ {
+ error (0, 0, _("conflicting %s option value '%s'"), option, value);
+ try_help (NULL, NULL);
+ }
+ *var = value;
+}
+
+/* Set the output style to STYLE, diagnosing conflicts. */
+static void
+specify_style (enum output_style style)
+{
+ if (output_style != style)
+ {
+ if (output_style != OUTPUT_UNSPECIFIED)
+ try_help ("conflicting output style options", NULL);
+ output_style = style;
+ }
+}
+
+/* Set the color mode. */
+static void
+specify_colors_style (char const *value)
+{
+ if (value == NULL || STREQ (value, "auto"))
+ colors_style = AUTO;
+ else if (STREQ (value, "always"))
+ colors_style = ALWAYS;
+ else if (STREQ (value, "never"))
+ colors_style = NEVER;
+ else
+ try_help ("invalid color '%s'", value);
+}
+
+
+/* Set the last-modified time of *ST to be the current time. */
+
+static void
+set_mtime_to_now (struct stat *st)
+{
+#ifdef STAT_TIMESPEC
+ gettime (&STAT_TIMESPEC (st, st_mtim));
+#else
+ struct timespec t;
+ gettime (&t);
+ st->st_mtime = t.tv_sec;
+# if defined STAT_TIMESPEC_NS
+ STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
+# elif defined HAVE_STRUCT_STAT_ST_SPARE1
+ st->st_spare1 = t.tv_nsec / 1000;
+# endif
+#endif
+}
+
+/* Compare two files (or dirs) with parent comparison PARENT
+ and names NAME0 and NAME1.
+ (If PARENT is null, then the first name is just NAME0, etc.)
+ This is self-contained; it opens the files and closes them.
+
+ Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
+ different, EXIT_TROUBLE if there is a problem opening them. */
+
+static int
+compare_files (struct comparison const *parent,
+ char const *name0,
+ char const *name1)
+{
+ struct comparison cmp;
+#define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
+ register int f;
+ int status = EXIT_SUCCESS;
+ bool same_files;
+ char *free0;
+ char *free1;
+
+ /* 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 && name1)
+ || (unidirectional_new_file && name1)
+ || new_file))
+ {
+ char const *name = name0 ? name0 : name1;
+ char const *dir = parent->file[!name0].name;
+
+ /* See POSIX 1003.1-2001 for this format. */
+ message ("Only in %s: %s\n", dir, name);
+
+ /* Return EXIT_FAILURE so that diff_dirs will return
+ EXIT_FAILURE ("some files differ"). */
+ return EXIT_FAILURE;
+ }
+
+ memset (cmp.file, 0, sizeof cmp.file);
+ cmp.parent = parent;
+
+ /* cmp.file[f].desc markers */
+#define NONEXISTENT (-1) /* nonexistent file */
+#define UNOPENED (-2) /* unopened file (e.g. directory) */
+#define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
+
+#define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
+
+ cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
+ cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
+
+ /* Now record the full name of each file, including nonexistent ones. */
+
+ if (!name0)
+ name0 = name1;
+ if (!name1)
+ name1 = name0;
+
+ if (!parent)
+ {
+ free0 = NULL;
+ free1 = NULL;
+ cmp.file[0].name = name0;
+ cmp.file[1].name = name1;
+ }
+ else
+ {
+ cmp.file[0].name = free0
+ = file_name_concat (parent->file[0].name, name0, NULL);
+ cmp.file[1].name = free1
+ = file_name_concat (parent->file[1].name, name1, NULL);
+ }
+
+ /* Stat the files. */
+
+ for (f = 0; f < 2; f++)
+ {
+ if (cmp.file[f].desc != NONEXISTENT)
+ {
+ if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
+ {
+ cmp.file[f].desc = cmp.file[0].desc;
+ cmp.file[f].stat = cmp.file[0].stat;
+ }
+ else if (STREQ (cmp.file[f].name, "-"))
+ {
+ cmp.file[f].desc = STDIN_FILENO;
+ if (binary && ! isatty (STDIN_FILENO))
+ set_binary_mode (STDIN_FILENO, O_BINARY);
+ if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
+ cmp.file[f].desc = ERRNO_ENCODE (errno);
+ else
+ {
+ if (S_ISREG (cmp.file[f].stat.st_mode))
+ {
+ off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
+ if (pos < 0)
+ cmp.file[f].desc = ERRNO_ENCODE (errno);
+ else
+ cmp.file[f].stat.st_size =
+ MAX (0, cmp.file[f].stat.st_size - pos);
+ }
+
+ /* POSIX 1003.1-2001 requires current time for
+ stdin. */
+ set_mtime_to_now (&cmp.file[f].stat);
+ }
+ }
+ else if ((no_dereference_symlinks
+ ? lstat (cmp.file[f].name, &cmp.file[f].stat)
+ : stat (cmp.file[f].name, &cmp.file[f].stat))
+ != 0)
+ cmp.file[f].desc = ERRNO_ENCODE (errno);
+ }
+ }
+
+ /* Mark files as nonexistent as needed for -N and -P, if they are
+ inaccessible empty regular files (the kind of files that 'patch'
+ creates to indicate nonexistent backups), or if they are
+ top-level files that do not exist but their counterparts do
+ exist. */
+ for (f = 0; f < 2; f++)
+ if ((new_file || (f == 0 && unidirectional_new_file))
+ && (cmp.file[f].desc == UNOPENED
+ ? (S_ISREG (cmp.file[f].stat.st_mode)
+ && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
+ && cmp.file[f].stat.st_size == 0)
+ : ((cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
+ || cmp.file[f].desc == ERRNO_ENCODE (EBADF))
+ && ! parent
+ && (cmp.file[1 - f].desc == UNOPENED
+ || cmp.file[1 - f].desc == STDIN_FILENO))))
+ cmp.file[f].desc = NONEXISTENT;
+
+ for (f = 0; f < 2; f++)
+ if (cmp.file[f].desc == NONEXISTENT)
+ {
+ memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
+ cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
+ }
+
+ for (f = 0; f < 2; f++)
+ {
+ int e = ERRNO_DECODE (cmp.file[f].desc);
+ if (0 <= e)
+ {
+ errno = e;
+ perror_with_name (cmp.file[f].name);
+ status = EXIT_TROUBLE;
+ }
+ }
+
+ if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
+ {
+ /* 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 = DIR_P (0);
+ int dir_arg = 1 - fnm_arg;
+ char const *fnm = cmp.file[fnm_arg].name;
+ char const *dir = cmp.file[dir_arg].name;
+ char const *filename = cmp.file[dir_arg].name = free0
+ = find_dir_file_pathname (dir, last_component (fnm));
+
+ if (STREQ (fnm, "-"))
+ fatal ("cannot compare '-' to a directory");
+
+ if ((no_dereference_symlinks
+ ? lstat (filename, &cmp.file[dir_arg].stat)
+ : stat (filename, &cmp.file[dir_arg].stat))
+ != 0)
+ {
+ perror_with_name (filename);
+ status = EXIT_TROUBLE;
+ }
+ }
+
+ if (status != EXIT_SUCCESS)
+ {
+ /* One of the files should exist but does not. */
+ }
+ else if (cmp.file[0].desc == NONEXISTENT
+ && cmp.file[1].desc == NONEXISTENT)
+ {
+ /* Neither file "exists", so there's nothing to compare. */
+ }
+ else if ((same_files
+ = (cmp.file[0].desc != NONEXISTENT
+ && cmp.file[1].desc != NONEXISTENT
+ && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
+ && same_file_attributes (&cmp.file[0].stat,
+ &cmp.file[1].stat)))
+ && no_diff_means_no_output)
+ {
+ /* The two named files are actually the same physical file.
+ We know they are identical without actually reading them. */
+ }
+ else if (DIR_P (0) & DIR_P (1))
+ {
+ if (output_style == OUTPUT_IFDEF)
+ fatal ("-D option not supported with directories");
+
+ /* If both are directories, compare the files in them. */
+
+ if (parent && !recursive)
+ {
+ /* But don't compare dir contents one level down
+ unless -r was specified.
+ See POSIX 1003.1-2001 for this format. */
+ message ("Common subdirectories: %s and %s\n",
+ cmp.file[0].name, cmp.file[1].name);
+ }
+ else
+ status = diff_dirs (&cmp, compare_files);
+ }
+ else if ((DIR_P (0) | DIR_P (1))
+ || (parent
+ && !((S_ISREG (cmp.file[0].stat.st_mode)
+ || S_ISLNK (cmp.file[0].stat.st_mode))
+ && (S_ISREG (cmp.file[1].stat.st_mode)
+ || S_ISLNK (cmp.file[1].stat.st_mode)))))
+ {
+ if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
+ {
+ /* We have a subdirectory that exists only in one directory. */
+
+ if ((DIR_P (0) | DIR_P (1))
+ && recursive
+ && (new_file
+ || (unidirectional_new_file
+ && cmp.file[0].desc == NONEXISTENT)))
+ status = diff_dirs (&cmp, compare_files);
+ else
+ {
+ char const *dir;
+
+ /* PARENT must be non-NULL here. */
+ assert (parent);
+ dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
+
+ /* See POSIX 1003.1-2001 for this format. */
+ message ("Only in %s: %s\n", dir, name0);
+
+ status = EXIT_FAILURE;
+ }
+ }
+ else
+ {
+ /* We have two files that are not to be compared. */
+
+ /* See POSIX 1003.1-2001 for this format. */
+ message5 ("File %s is a %s while file %s is a %s\n",
+ file_label[0] ? file_label[0] : cmp.file[0].name,
+ file_type (&cmp.file[0].stat),
+ file_label[1] ? file_label[1] : cmp.file[1].name,
+ file_type (&cmp.file[1].stat));
+
+ /* This is a difference. */
+ status = EXIT_FAILURE;
+ }
+ }
+ else if (S_ISLNK (cmp.file[0].stat.st_mode)
+ || S_ISLNK (cmp.file[1].stat.st_mode))
+ {
+ /* We get here only if we use lstat(), not stat(). */
+ assert (no_dereference_symlinks);
+
+ if (S_ISLNK (cmp.file[0].stat.st_mode)
+ && S_ISLNK (cmp.file[1].stat.st_mode))
+ {
+ /* Compare the values of the symbolic links. */
+ char *link_value[2] = { NULL, NULL };
+
+ for (f = 0; f < 2; f++)
+ {
+ link_value[f] = xreadlink (cmp.file[f].name);
+ if (link_value[f] == NULL)
+ {
+ perror_with_name (cmp.file[f].name);
+ status = EXIT_TROUBLE;
+ break;
+ }
+ }
+ if (status == EXIT_SUCCESS)
+ {
+ if ( ! STREQ (link_value[0], link_value[1]))
+ {
+ message ("Symbolic links %s and %s differ\n",
+ cmp.file[0].name, cmp.file[1].name);
+ /* This is a difference. */
+ status = EXIT_FAILURE;
+ }
+ }
+ for (f = 0; f < 2; f++)
+ free (link_value[f]);
+ }
+ else
+ {
+ /* We have two files that are not to be compared, because
+ one of them is a symbolic link and the other one is not. */
+
+ message5 ("File %s is a %s while file %s is a %s\n",
+ file_label[0] ? file_label[0] : cmp.file[0].name,
+ file_type (&cmp.file[0].stat),
+ file_label[1] ? file_label[1] : cmp.file[1].name,
+ file_type (&cmp.file[1].stat));
+
+ /* This is a difference. */
+ status = EXIT_FAILURE;
+ }
+ }
+ else if (files_can_be_treated_as_binary
+ && S_ISREG (cmp.file[0].stat.st_mode)
+ && S_ISREG (cmp.file[1].stat.st_mode)
+ && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size
+ && 0 < cmp.file[0].stat.st_size
+ && 0 < cmp.file[1].stat.st_size)
+ {
+ message ("Files %s and %s differ\n",
+ file_label[0] ? file_label[0] : cmp.file[0].name,
+ file_label[1] ? file_label[1] : cmp.file[1].name);
+ status = EXIT_FAILURE;
+ }
+ else
+ {
+ /* Both exist and neither is a directory. */
+
+ /* Open the files and record their descriptors. */
+
+ int oflags = O_RDONLY | (binary ? O_BINARY : 0);
+
+ if (cmp.file[0].desc == UNOPENED)
+ if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
+ {
+ perror_with_name (cmp.file[0].name);
+ status = EXIT_TROUBLE;
+ }
+ if (cmp.file[1].desc == UNOPENED)
+ {
+ if (same_files)
+ cmp.file[1].desc = cmp.file[0].desc;
+ else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
+ {
+ perror_with_name (cmp.file[1].name);
+ status = EXIT_TROUBLE;
+ }
+ }
+
+ /* Compare the files, if no error was found. */
+
+ if (status == EXIT_SUCCESS)
+ status = diff_2_files (&cmp);
+
+ /* Close the file descriptors. */
+
+ if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
+ {
+ perror_with_name (cmp.file[0].name);
+ status = EXIT_TROUBLE;
+ }
+ if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
+ && close (cmp.file[1].desc) != 0)
+ {
+ perror_with_name (cmp.file[1].name);
+ status = EXIT_TROUBLE;
+ }
+ }
+
+ /* Now the comparison has been done, if no error prevented it,
+ and STATUS is the value this function will return. */
+
+ if (status == EXIT_SUCCESS)
+ {
+ if (report_identical_files && !DIR_P (0))
+ message ("Files %s and %s are identical\n",
+ file_label[0] ? file_label[0] : cmp.file[0].name,
+ file_label[1] ? file_label[1] : cmp.file[1].name);
+ }
+ else
+ {
+ /* Flush stdout so that the user sees differences immediately.
+ This can hurt performance, unfortunately. */
+ if (fflush (stdout) != 0)
+ pfatal_with_name (_("standard output"));
+ }
+
+ free (free0);
+ free (free1);
+
+ return status;
+}
diff --git a/src/diff.h b/src/diff.h
new file mode 100644
index 0000000..0983e7c
--- /dev/null
+++ b/src/diff.h
@@ -0,0 +1,423 @@
+/* Shared definitions for GNU DIFF
+
+ Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2009-2013,
+ 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include <regex.h>
+#include <stdio.h>
+#include <unlocked-io.h>
+
+/* What kind of changes a hunk contains. */
+enum changes
+{
+ /* No changes: lines common to both files. */
+ UNCHANGED,
+
+ /* Deletes only: lines taken from just the first file. */
+ OLD,
+
+ /* Inserts only: lines taken from just the second file. */
+ NEW,
+
+ /* Both deletes and inserts: a hunk containing both old and new lines. */
+ CHANGED
+};
+
+/* When colors should be used in the output. */
+enum colors_style
+{
+ /* Never output colors. */
+ NEVER,
+
+ /* Output colors if the output is a terminal. */
+ AUTO,
+
+ /* Always output colors. */
+ ALWAYS,
+};
+
+/* Variables for command line options */
+
+#ifndef GDIFF_MAIN
+# define XTERN extern
+#else
+# define XTERN
+#endif
+
+enum output_style
+{
+ /* No output style specified. */
+ OUTPUT_UNSPECIFIED,
+
+ /* 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)
+
+XTERN enum output_style output_style;
+
+/* Define the current color context used to print a line. */
+XTERN enum colors_style colors_style;
+
+/* Nonzero if output cannot be generated for identical files. */
+XTERN bool 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. */
+XTERN lin context;
+
+/* Consider all files as text files (-a).
+ Don't interpret codes over 0177 as implying a "binary file". */
+XTERN bool text;
+
+/* Number of lines to keep in identical prefix and suffix. */
+XTERN lin horizon_lines;
+
+/* The significance of white space during comparisons. */
+enum DIFF_white_space
+{
+ /* All white space is significant (the default). */
+ IGNORE_NO_WHITE_SPACE,
+
+ /* Ignore changes due to tab expansion (-E). */
+ IGNORE_TAB_EXPANSION,
+
+ /* Ignore changes in trailing horizontal white space (-Z). */
+ IGNORE_TRAILING_SPACE,
+
+ /* IGNORE_TAB_EXPANSION and IGNORE_TRAILING_SPACE are a special case
+ because they are independent and can be ORed together, yielding
+ IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE. */
+ IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE,
+
+ /* Ignore changes in horizontal white space (-b). */
+ IGNORE_SPACE_CHANGE,
+
+ /* Ignore all horizontal white space (-w). */
+ IGNORE_ALL_SPACE
+};
+XTERN enum DIFF_white_space ignore_white_space;
+
+/* Ignore changes that affect only blank lines (-B). */
+XTERN bool ignore_blank_lines;
+
+/* Files can be compared byte-by-byte, as if they were binary.
+ This depends on various options. */
+XTERN bool files_can_be_treated_as_binary;
+
+/* Ignore differences in case of letters (-i). */
+XTERN bool ignore_case;
+
+/* Ignore differences in case of letters in file names. */
+XTERN bool ignore_file_name_case;
+
+/* Act on symbolic links themselves rather than on their target
+ (--no-dereference). */
+XTERN bool no_dereference_symlinks;
+
+/* File labels for '-c' output headers (--label). */
+XTERN char *file_label[2];
+
+/* Regexp to identify function-header lines (-F). */
+XTERN struct re_pattern_buffer function_regexp;
+
+/* Ignore changes that affect only lines matching this regexp (-I). */
+XTERN struct re_pattern_buffer ignore_regexp;
+
+/* Say only whether files differ, not how (-q). */
+XTERN bool brief;
+
+/* Expand tabs in the output so the text lines up properly
+ despite the characters added to the front of each line (-t). */
+XTERN bool expand_tabs;
+
+/* Number of columns between tab stops. */
+XTERN size_t tabsize;
+
+/* 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 (-T). */
+XTERN bool initial_tab;
+
+/* Do not output an initial space or tab before the text of an empty line. */
+XTERN bool suppress_blank_empty;
+
+/* Remove trailing carriage returns from input. */
+XTERN bool strip_trailing_cr;
+
+/* In directory comparison, specify file to start with (-S).
+ This is used for resuming an aborted comparison.
+ All file names less than this name are ignored. */
+XTERN char const *starting_file;
+
+/* Pipe each file's output through pr (-l). */
+XTERN bool paginate;
+
+/* Line group formats for unchanged, old, new, and changed groups. */
+XTERN char const *group_format[CHANGED + 1];
+
+/* Line formats for unchanged, old, and new lines. */
+XTERN char const *line_format[NEW + 1];
+
+/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */
+XTERN bool sdiff_merge_assist;
+
+/* Tell OUTPUT_SDIFF to show only the left version of common lines. */
+XTERN bool left_column;
+
+/* Tell OUTPUT_SDIFF to not show common lines. */
+XTERN bool suppress_common_lines;
+
+/* The half line width and column 2 offset for OUTPUT_SDIFF. */
+XTERN size_t sdiff_half_width;
+XTERN size_t 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. */
+XTERN char *switch_string;
+
+/* Use heuristics for better speed with large files with a small
+ density of changes. */
+XTERN bool speed_large_files;
+
+/* Patterns that match file names to be excluded. */
+XTERN struct exclude *excluded;
+
+/* Don't discard lines. This makes things slower (sometimes much
+ slower) but will find a guaranteed minimal set of changes. */
+XTERN bool minimal;
+
+/* The strftime format to use for time strings. */
+XTERN char const *time_format;
+
+/* 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 */
+ lin inserted; /* # lines of file 1 changed here. */
+ lin deleted; /* # lines of file 0 changed here. */
+ lin line0; /* Line number of 1st deleted line. */
+ lin line1; /* Line number of 1st inserted line. */
+ bool ignore; /* Flag used in context.c. */
+};
+
+/* Structures that describe the input files. */
+
+/* Data on one input file being compared. */
+
+struct file_data {
+ int desc; /* File descriptor */
+ char const *name; /* File name */
+ struct stat stat; /* File status */
+
+ /* Buffer in which text of file is read. */
+ word *buffer;
+
+ /* Allocated size of buffer, in bytes. Always a multiple of
+ sizeof *buffer. */
+ size_t bufsize;
+
+ /* Number of valid bytes now in the buffer. */
+ size_t buffered;
+
+ /* Array of pointers to lines in the file. */
+ char const **linbuf;
+
+ /* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
+ linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
+ linebuf[linbuf_base ... valid_lines - 1] contain valid data.
+ linebuf[linbuf_base ... alloc_lines - 1] are allocated. */
+ lin linbuf_base, buffered_lines, valid_lines, alloc_lines;
+
+ /* Pointer to end of prefix of this file to ignore when hashing. */
+ char const *prefix_end;
+
+ /* Count of lines in the prefix.
+ There are this many lines in the file before linbuf[0]. */
+ lin prefix_lines;
+
+ /* Pointer to start of suffix of this file to ignore when hashing. */
+ char const *suffix_begin;
+
+ /* Vector, indexed by line number, containing an equivalence code for
+ each line. It is this vector that is actually compared with that
+ of another file to generate differences. */
+ lin *equivs;
+
+ /* Vector, like the previous one except that
+ the elements for discarded lines have been squeezed out. */
+ lin *undiscarded;
+
+ /* Vector mapping virtual line numbers (not counting discarded lines)
+ to real ones (counting those lines). Both are origin-0. */
+ lin *realindexes;
+
+ /* Total number of nondiscarded lines. */
+ lin 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;
+
+ /* 1 if file ends in a line with no final newline. */
+ bool missing_newline;
+
+ /* 1 if at end of file. */
+ bool eof;
+
+ /* 1 more than the maximum equivalence value used for this or its
+ sibling file. */
+ lin equiv_max;
+};
+
+/* The file buffer, considered as an array of bytes rather than
+ as an array of words. */
+#define FILE_BUFFER(f) ((char *) (f)->buffer)
+
+/* Data on two input files being compared. */
+
+struct comparison
+ {
+ struct file_data file[2];
+ struct comparison const *parent; /* parent, if a recursive comparison */
+ };
+
+/* Describe the two files currently being compared. */
+
+XTERN struct file_data files[2];
+
+/* Stdio stream to output diffs to. */
+
+XTERN FILE *outfile;
+
+/* Declare various functions. */
+
+/* analyze.c */
+extern int diff_2_files (struct comparison *);
+
+/* context.c */
+extern void print_context_header (struct file_data[], char const * const *, bool);
+extern void print_context_script (struct change *, bool);
+
+/* dir.c */
+extern int diff_dirs (struct comparison const *,
+ int (*) (struct comparison const *,
+ char const *, char const *));
+extern char *find_dir_file_pathname (char const *, char const *);
+
+/* ed.c */
+extern void print_ed_script (struct change *);
+extern void pr_forward_ed_script (struct change *);
+
+/* ifdef.c */
+extern void print_ifdef_script (struct change *);
+
+/* io.c */
+extern void file_block_read (struct file_data *, size_t);
+extern bool read_files (struct file_data[], bool);
+
+/* normal.c */
+extern void print_normal_script (struct change *);
+
+/* rcs.c */
+extern void print_rcs_script (struct change *);
+
+/* side.c */
+extern void print_sdiff_script (struct change *);
+
+/* util.c */
+extern char const change_letter[4];
+extern char const pr_program[];
+extern char *concat (char const *, char const *, char const *);
+extern bool lines_differ (char const *, char const *) _GL_ATTRIBUTE_PURE;
+extern lin translate_line_number (struct file_data const *, lin);
+extern struct change *find_change (struct change *);
+extern struct change *find_reverse_change (struct change *);
+extern void *zalloc (size_t);
+extern enum changes analyze_hunk (struct change *, lin *, lin *, lin *, lin *);
+extern void begin_output (void);
+extern void debug_script (struct change *);
+extern void fatal (char const *) __attribute__((noreturn));
+extern void finish_output (void);
+extern void message (char const *, char const *, char const *);
+extern void message5 (char const *, char const *, char const *,
+ char const *, char const *);
+extern void output_1_line (char const *, char const *, char const *,
+ char const *);
+extern void perror_with_name (char const *);
+extern void pfatal_with_name (char const *) __attribute__((noreturn));
+extern void print_1_line (char const *, char const * const *);
+extern void print_1_line_nl (char const *, char const * const *, bool);
+extern void print_message_queue (void);
+extern void print_number_range (char, struct file_data *, lin, lin);
+extern void print_script (struct change *, struct change * (*) (struct change *),
+ void (*) (struct change *));
+extern void setup_output (char const *, char const *, bool);
+extern void translate_range (struct file_data const *, lin, lin,
+ long int *, long int *);
+
+enum color_context
+{
+ HEADER_CONTEXT,
+ ADD_CONTEXT,
+ DELETE_CONTEXT,
+ RESET_CONTEXT,
+ LINE_NUMBER_CONTEXT,
+};
+
+XTERN bool presume_output_tty;
+
+extern void set_color_context (enum color_context color_context);
+extern void set_color_palette (char const *palette);
diff --git a/src/diff3.c b/src/diff3.c
new file mode 100644
index 0000000..b80aeb3
--- /dev/null
+++ b/src/diff3.c
@@ -0,0 +1,1791 @@
+/* diff3 - compare three files line by line
+
+ Copyright (C) 1988-1989, 1992-1996, 1998, 2001-2002, 2004, 2006, 2009-2013,
+ 2015-2016 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "paths.h"
+
+#include <stdio.h>
+#include <unlocked-io.h>
+
+#include <c-stack.h>
+#include <cmpbuf.h>
+#include <error.h>
+#include <exitfail.h>
+#include <file-type.h>
+#include <getopt.h>
+#include <progname.h>
+#include <system-quote.h>
+#include <version-etc.h>
+#include <xalloc.h>
+#include <xfreopen.h>
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "diff3"
+
+#define AUTHORS \
+ proper_name ("Randy Smith")
+
+/* Internal data structures and macros for the diff3 program; includes
+ data structures for both diff3 diffs and normal diffs. */
+
+/* Different files within a three way diff. */
+#define FILE0 0
+#define FILE1 1
+#define FILE2 2
+
+/* A three way diff is built from two two-way diffs; the file which
+ the two two-way diffs share is: */
+#define FILEC FILE2
+
+/* Different files within a two way diff.
+ FC is the common file, FO the other file. */
+#define FO 0
+#define FC 1
+
+/* The ranges are indexed by */
+#define RANGE_START 0
+#define RANGE_END 1
+
+enum diff_type {
+ ERROR, /* Should not be used */
+ ADD, /* Two way diff add */
+ CHANGE, /* Two way diff change */
+ DELETE, /* Two way diff delete */
+ DIFF_ALL, /* All three are different */
+ DIFF_1ST, /* Only the first is different */
+ DIFF_2ND, /* Only the second */
+ DIFF_3RD /* Only the third */
+};
+
+/* Two way diff */
+struct diff_block {
+ lin ranges[2][2]; /* Ranges are inclusive */
+ char **lines[2]; /* The actual lines (may contain nulls) */
+ size_t *lengths[2]; /* Line lengths (including newlines, if any) */
+ struct diff_block *next;
+#ifdef lint
+ struct diff_block *n2; /* Used only when freeing. */
+#endif
+};
+
+/* Three way diff */
+
+struct diff3_block {
+ enum diff_type correspond; /* Type of diff */
+ lin ranges[3][2]; /* Ranges are inclusive */
+ char **lines[3]; /* The actual lines (may contain nulls) */
+ size_t *lengths[3]; /* Line lengths (including newlines, if any) */
+ struct diff3_block *next;
+};
+
+/* Access the ranges on a diff block. */
+#define D_LOWLINE(diff, filenum) \
+ ((diff)->ranges[filenum][RANGE_START])
+#define D_HIGHLINE(diff, filenum) \
+ ((diff)->ranges[filenum][RANGE_END])
+#define D_NUMLINES(diff, filenum) \
+ (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
+
+/* Access the line numbers in a file in a diff by relative line
+ numbers (i.e. line number within the diff itself). Note that these
+ are lvalues and can be used for assignment. */
+#define D_RELNUM(diff, filenum, linenum) \
+ ((diff)->lines[filenum][linenum])
+#define D_RELLEN(diff, filenum, linenum) \
+ ((diff)->lengths[filenum][linenum])
+
+/* And get at them directly, when that should be necessary. */
+#define D_LINEARRAY(diff, filenum) \
+ ((diff)->lines[filenum])
+#define D_LENARRAY(diff, filenum) \
+ ((diff)->lengths[filenum])
+
+/* Next block. */
+#define D_NEXT(diff) ((diff)->next)
+
+/* Access the type of a diff3 block. */
+#define D3_TYPE(diff) ((diff)->correspond)
+
+/* Line mappings based on diffs. The first maps off the top of the
+ diff, the second off of the bottom. */
+#define D_HIGH_MAPLINE(diff, fromfile, tofile, linenum) \
+ ((linenum) \
+ - D_HIGHLINE ((diff), (fromfile)) \
+ + D_HIGHLINE ((diff), (tofile)))
+
+#define D_LOW_MAPLINE(diff, fromfile, tofile, linenum) \
+ ((linenum) \
+ - D_LOWLINE ((diff), (fromfile)) \
+ + D_LOWLINE ((diff), (tofile)))
+
+/* Options variables for flags set on command line. */
+
+/* If nonzero, treat all files as text files, never as binary. */
+static bool text;
+
+/* Remove trailing carriage returns from input. */
+static bool strip_trailing_cr;
+
+/* If nonzero, write out an ed script instead of the standard diff3 format. */
+static bool edscript;
+
+/* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
+ preserve the lines which would normally be deleted from
+ file 1 with a special flagging mechanism. */
+static bool flagging;
+
+/* Use a tab to align output lines (-T). */
+static bool initial_tab;
+
+/* If nonzero, do not output information for overlapping diffs. */
+static bool simple_only;
+
+/* If nonzero, do not output information for non-overlapping diffs. */
+static bool overlap_only;
+
+/* If nonzero, show information for DIFF_2ND diffs. */
+static bool show_2nd;
+
+/* If nonzero, include ':wq' at the end of the script
+ to write out the file being edited. */
+static bool finalwrite;
+
+/* If nonzero, output a merged file. */
+static bool merge;
+
+static char *read_diff (char const *, char const *, char **);
+static char *scan_diff_line (char *, char **, size_t *, char *, char);
+static enum diff_type process_diff_control (char **, struct diff_block *);
+static bool compare_line_list (char * const[], size_t const[], char * const[], size_t const[], lin);
+static bool copy_stringlist (char * const[], size_t const[], char *[], size_t[], lin);
+static bool output_diff3_edscript (FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
+static bool output_diff3_merge (FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
+static struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin);
+static struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *);
+static struct diff3_block *reverse_diff3_blocklist (struct diff3_block *);
+static struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *);
+static struct diff_block *process_diff (char const *, char const *, struct diff_block **, char **);
+static void check_stdout (void);
+static void fatal (char const *) __attribute__((noreturn));
+static void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]);
+static void perror_with_exit (char const *) __attribute__((noreturn));
+static void try_help (char const *, char const *) __attribute__((noreturn));
+static void usage (void);
+
+static char const *diff_program = DEFAULT_DIFF_PROGRAM;
+
+/* Values for long options that do not have single-letter equivalents. */
+enum
+{
+ DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
+ HELP_OPTION,
+ STRIP_TRAILING_CR_OPTION
+};
+
+static struct option const longopts[] =
+{
+ {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
+ {"easy-only", 0, 0, '3'},
+ {"ed", 0, 0, 'e'},
+ {"help", 0, 0, HELP_OPTION},
+ {"initial-tab", 0, 0, 'T'},
+ {"label", 1, 0, 'L'},
+ {"merge", 0, 0, 'm'},
+ {"overlap-only", 0, 0, 'x'},
+ {"show-all", 0, 0, 'A'},
+ {"show-overlap", 0, 0, 'E'},
+ {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
+ {"text", 0, 0, 'a'},
+ {"version", 0, 0, 'v'},
+ {0, 0, 0, 0}
+};
+
+static void
+free_diff_block (struct diff_block *p)
+{
+#ifndef lint
+ (void)p;
+#else
+ while (p)
+ {
+ free (p->lines[0]);
+ free (p->lines[1]);
+ free (p->lengths[0]);
+ free (p->lengths[1]);
+ struct diff_block *next = p->n2;
+ free (p);
+ p = next;
+ }
+#endif
+}
+
+/* Copy each next pointer to n2, since make_3way_diff would clobber the former,
+ yet we will still need something to free these buffers. */
+static void
+next_to_n2 (struct diff_block *p)
+{
+#ifndef lint
+ (void)p;
+#else
+ while (p)
+ p = p->n2 = p->next;
+#endif
+}
+
+int
+main (int argc, char **argv)
+{
+ int c, i;
+ int common;
+ int mapping[3];
+ int rev_mapping[3];
+ int incompat = 0;
+ bool conflicts_found;
+ struct diff_block *thread0, *thread1, *last_block;
+ struct diff3_block *diff3;
+ int tag_count = 0;
+ char *tag_strings[3];
+ char *commonname;
+ char **file;
+ struct stat statb;
+
+ exit_failure = EXIT_TROUBLE;
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+ c_stack_action (0);
+
+ while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ text = true;
+ break;
+ case 'A':
+ show_2nd = true;
+ flagging = true;
+ incompat++;
+ break;
+ case 'x':
+ overlap_only = true;
+ incompat++;
+ break;
+ case '3':
+ simple_only = true;
+ incompat++;
+ break;
+ case 'i':
+ finalwrite = true;
+ break;
+ case 'm':
+ merge = true;
+ break;
+ case 'X':
+ overlap_only = true;
+ /* Fall through. */
+ case 'E':
+ flagging = true;
+ /* Fall through. */
+ case 'e':
+ incompat++;
+ break;
+ case 'T':
+ initial_tab = true;
+ break;
+ case STRIP_TRAILING_CR_OPTION:
+ strip_trailing_cr = true;
+ break;
+ case 'v':
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
+ AUTHORS, (char *) NULL);
+ check_stdout ();
+ return EXIT_SUCCESS;
+ case DIFF_PROGRAM_OPTION:
+ diff_program = optarg;
+ break;
+ case HELP_OPTION:
+ usage ();
+ check_stdout ();
+ return EXIT_SUCCESS;
+ case 'L':
+ /* Handle up to three -L options. */
+ if (tag_count < 3)
+ {
+ tag_strings[tag_count++] = optarg;
+ break;
+ }
+ try_help ("too many file label options", 0);
+ default:
+ try_help (0, 0);
+ }
+ }
+
+ edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */
+ show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */
+ flagging |= ~incompat & merge;
+
+ if (incompat > 1 /* Ensure at most one of -AeExX3. */
+ || finalwrite & merge /* -i -m would rewrite input file. */
+ || (tag_count && ! flagging)) /* -L requires one of -AEX. */
+ try_help ("incompatible options", 0);
+
+ if (argc - optind != 3)
+ {
+ if (argc - optind < 3)
+ try_help ("missing operand after '%s'", argv[argc - 1]);
+ else
+ try_help ("extra operand '%s'", argv[optind + 3]);
+ }
+
+ file = &argv[optind];
+
+ for (i = tag_count; i < 3; i++)
+ tag_strings[i] = file[i];
+
+ /* Always compare file1 to file2, even if file2 is "-".
+ This is needed for -mAeExX3. Using the file0 as
+ the common file would produce wrong results, because if the
+ file0-file1 diffs didn't line up with the file0-file2 diffs
+ (which is entirely possible since we don't use diff's -n option),
+ diff3 might report phantom changes from file1 to file2.
+
+ Also, try to compare file0 to file1, because this is where
+ changes are expected to come from. Diffing between these pairs
+ of files is more likely to avoid phantom changes from file0 to file1.
+
+ Historically, the default common file was file2, so some older
+ applications (e.g. Emacs ediff) used file2 as the ancestor. So,
+ for compatibility, if this is a 3-way diff (not a merge or
+ edscript), prefer file2 as the common file. */
+
+ common = 2 - (edscript | merge);
+
+ if (STREQ (file[common], "-"))
+ {
+ /* Sigh. We've got standard input as the common file. We can't
+ call diff twice on stdin. Use the other arg as the common
+ file instead. */
+ common = 3 - common;
+ if (STREQ (file[0], "-") || STREQ (file[common], "-"))
+ fatal ("'-' specified for more than one input file");
+ }
+
+ mapping[0] = 0;
+ mapping[1] = 3 - common;
+ mapping[2] = common;
+
+ for (i = 0; i < 3; i++)
+ rev_mapping[mapping[i]] = i;
+
+ for (i = 0; i < 3; i++)
+ if (! STREQ (file[i], "-"))
+ {
+ if (stat (file[i], &statb) < 0)
+ perror_with_exit (file[i]);
+ else if (S_ISDIR (statb.st_mode))
+ error (EXIT_TROUBLE, EISDIR, "%s", file[i]);
+ }
+
+#ifdef SIGCHLD
+ /* System V fork+wait does not work if SIGCHLD is ignored. */
+ signal (SIGCHLD, SIG_DFL);
+#endif
+
+ /* Invoke diff twice on two pairs of input files, combine the two
+ diffs, and output them. */
+
+ char *b0, *b1;
+ commonname = file[rev_mapping[FILEC]];
+ thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block, &b1);
+ thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block, &b0);
+
+ next_to_n2 (thread0);
+ next_to_n2 (thread1);
+
+ diff3 = make_3way_diff (thread0, thread1);
+
+ free_diff_block (thread0);
+ free_diff_block (thread1);
+
+ if (edscript)
+ conflicts_found
+ = output_diff3_edscript (stdout, diff3, mapping, rev_mapping,
+ tag_strings[0], tag_strings[1], tag_strings[2]);
+ else if (merge)
+ {
+ xfreopen (file[rev_mapping[FILE0]], "r", stdin);
+ conflicts_found
+ = output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping,
+ tag_strings[0], tag_strings[1], tag_strings[2]);
+ if (ferror (stdin))
+ fatal ("read failed");
+ }
+ else
+ {
+ output_diff3 (stdout, diff3, mapping, rev_mapping);
+ conflicts_found = false;
+ }
+
+ free (b0);
+ free (b1);
+ check_stdout ();
+ exit (conflicts_found);
+}
+
+static void
+try_help (char const *reason_msgid, char const *operand)
+{
+ if (reason_msgid)
+ error (0, 0, _(reason_msgid), operand);
+ error (EXIT_TROUBLE, 0,
+ _("Try '%s --help' for more information."), program_name);
+ abort ();
+}
+
+static void
+check_stdout (void)
+{
+ if (ferror (stdout))
+ fatal ("write failed");
+ else if (fclose (stdout) != 0)
+ perror_with_exit (_("standard output"));
+}
+
+static char const * const option_help_msgid[] = {
+ N_("-A, --show-all output all changes, bracketing conflicts"),
+ "",
+ N_("-e, --ed output ed script incorporating changes\n"
+ " from OLDFILE to YOURFILE into MYFILE"),
+ N_("-E, --show-overlap like -e, but bracket conflicts"),
+ N_("-3, --easy-only like -e, but incorporate only nonoverlapping changes"),
+ N_("-x, --overlap-only like -e, but incorporate only overlapping changes"),
+ N_("-X like -x, but bracket conflicts"),
+ N_("-i append 'w' and 'q' commands to ed scripts"),
+ "",
+ N_("-m, --merge output actual merged file, according to\n"
+ " -A if no other options are given"),
+ "",
+ N_("-a, --text treat all files as text"),
+ N_(" --strip-trailing-cr strip trailing carriage return on input"),
+ N_("-T, --initial-tab make tabs line up by prepending a tab"),
+ N_(" --diff-program=PROGRAM use PROGRAM to compare files"),
+ N_("-L, --label=LABEL use LABEL instead of file name\n"
+ " (can be repeated up to three times)"),
+ "",
+ N_(" --help display this help and exit"),
+ N_("-v, --version output version information and exit"),
+ 0
+};
+
+static void
+usage (void)
+{
+ char const * const *p;
+
+ printf (_("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n"),
+ program_name);
+ printf ("%s\n\n", _("Compare three files line by line."));
+
+ fputs (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+ for (p = option_help_msgid; *p; p++)
+ if (**p)
+ printf (" %s\n", _(*p));
+ else
+ putchar ('\n');
+ fputs (_("\n\
+The default output format is a somewhat human-readable representation of\n\
+the changes.\n\
+\n\
+The -e, -E, -x, -X (and corresponding long) options cause an ed script\n\
+to be output instead of the default.\n\
+\n\
+Finally, the -m (--merge) option causes diff3 to do the merge internally\n\
+and output the actual merged file. For unusual input, this is more\n\
+robust than using ed.\n"), stdout);
+ printf ("\n%s\n%s\n",
+ _("If a FILE is '-', read standard input."),
+ _("Exit status is 0 if successful, 1 if conflicts, 2 if trouble."));
+ emit_bug_reporting_address ();
+}
+
+/* Combine the two diffs together into one.
+ Here is the algorithm:
+
+ File2 is shared in common between the two diffs.
+ Diff02 is the diff between 0 and 2.
+ Diff12 is the diff between 1 and 2.
+
+ 1) Find the range for the first block in File2.
+ a) Take the lowest of the two ranges (in File2) in the two
+ current blocks (one from each diff) as being the low
+ water mark. Assign the upper end of this block as
+ being the high water mark and move the current block up
+ one. Mark the block just moved over as to be used.
+ b) Check the next block in the diff that the high water
+ mark is *not* from.
+
+ *If* the high water mark is above
+ the low end of the range in that block,
+
+ mark that block as to be used and move the current
+ block up. Set the high water mark to the max of
+ the high end of this block and the current. Repeat b.
+
+ 2) Find the corresponding ranges in File0 (from the blocks
+ in diff02; line per line outside of diffs) and in File1.
+ Create a diff3_block, reserving space as indicated by the ranges.
+
+ 3) Copy all of the pointers for file2 in. At least for now,
+ do memcmp's between corresponding strings in the two diffs.
+
+ 4) Copy all of the pointers for file0 and 1 in. Get what is
+ needed from file2 (when there isn't a diff block, it's
+ identical to file2 within the range between diff blocks).
+
+ 5) If the diff blocks used came from only one of the two
+ strings of diffs, then that file (i.e. the one other than
+ the common file in that diff) is the odd person out. If
+ diff blocks are used from both sets, check to see if files
+ 0 and 1 match:
+
+ Same number of lines? If so, do a set of memcmp's (if
+ a memcmp matches; copy the pointer over; it'll be easier
+ later during comparisons). If they match, 0 & 1 are the
+ same. If not, all three different.
+
+ Then do it again, until the blocks are exhausted. */
+
+
+/* Make a three way diff (chain of diff3_block's) from two two way
+ diffs (chains of diff_block's). Assume that each of the two diffs
+ passed are onto the same file (i.e. that each of the diffs were
+ made "to" the same file). Return a three way diff pointer with
+ numbering FILE0 = the other file in diff02, FILE1 = the other file
+ in diff12, and FILEC = the common file. */
+
+static struct diff3_block *
+make_3way_diff (struct diff_block *thread0, struct diff_block *thread1)
+{
+ /* Work on the two diffs passed to it as threads. Thread number 0
+ is diff02, thread number 1 is diff12. USING is the base of the
+ list of blocks to be used to construct each block of the three
+ way diff; if no blocks from a particular thread are to be used,
+ that element of USING is 0. LAST_USING contains the last
+ elements on each of the using lists.
+
+ HIGH_WATER_MARK is the highest line number in the common file
+ described in any of the diffs in either of the USING lists.
+ HIGH_WATER_THREAD names the thread. Similarly BASE_WATER_MARK
+ and BASE_WATER_THREAD describe the lowest line number in the
+ common file described in any of the diffs in either of the USING
+ lists. HIGH_WATER_DIFF is the diff from which the
+ HIGH_WATER_MARK was taken.
+
+ HIGH_WATER_DIFF should always be equal to
+ LAST_USING[HIGH_WATER_THREAD]. OTHER_DIFF is the next diff to
+ check for higher water, and should always be equal to
+ CURRENT[HIGH_WATER_THREAD ^ 1]. OTHER_THREAD is the thread in
+ which the OTHER_DIFF is, and hence should always be equal to
+ HIGH_WATER_THREAD ^ 1.
+
+ LAST_DIFF is the last diff block produced by this routine, for
+ line correspondence purposes between that diff and the one
+ currently being worked on. It is ZERO_DIFF before any blocks
+ have been created. */
+
+ struct diff_block *using[2];
+ struct diff_block *last_using[2];
+ struct diff_block *current[2];
+
+ lin high_water_mark;
+
+ int high_water_thread;
+ int base_water_thread;
+ int other_thread;
+
+ struct diff_block *high_water_diff;
+ struct diff_block *other_diff;
+
+ struct diff3_block *result;
+ struct diff3_block *tmpblock;
+ struct diff3_block **result_end;
+
+ struct diff3_block const *last_diff3;
+
+ static struct diff3_block const zero_diff3;
+
+ /* Initialization */
+ result = 0;
+ result_end = &result;
+ current[0] = thread0; current[1] = thread1;
+ last_diff3 = &zero_diff3;
+
+ /* Sniff up the threads until we reach the end */
+
+ while (current[0] || current[1])
+ {
+ using[0] = using[1] = last_using[0] = last_using[1] = 0;
+
+ /* Setup low and high water threads, diffs, and marks. */
+ if (!current[0])
+ base_water_thread = 1;
+ else if (!current[1])
+ base_water_thread = 0;
+ else
+ base_water_thread =
+ (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
+
+ high_water_thread = base_water_thread;
+
+ high_water_diff = current[high_water_thread];
+
+ high_water_mark = D_HIGHLINE (high_water_diff, FC);
+
+ /* Make the diff you just got info from into the using class */
+ using[high_water_thread]
+ = last_using[high_water_thread]
+ = high_water_diff;
+ current[high_water_thread] = high_water_diff->next;
+ last_using[high_water_thread]->next = 0;
+
+ /* And mark the other diff */
+ other_thread = high_water_thread ^ 0x1;
+ other_diff = current[other_thread];
+
+ /* Shuffle up the ladder, checking the other diff to see if it
+ needs to be incorporated. */
+ while (other_diff
+ && D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
+ {
+
+ /* Incorporate this diff into the using list. Note that
+ this doesn't take it off the current list */
+ if (using[other_thread])
+ last_using[other_thread]->next = other_diff;
+ else
+ using[other_thread] = other_diff;
+ last_using[other_thread] = other_diff;
+
+ /* Take it off the current list. Note that this following
+ code assumes that other_diff enters it equal to
+ current[high_water_thread ^ 0x1] */
+ current[other_thread] = current[other_thread]->next;
+ other_diff->next = 0;
+
+ /* Set the high_water stuff
+ If this comparison is equal, then this is the last pass
+ through this loop; since diff blocks within a given
+ thread cannot overlap, the high_water_mark will be
+ *below* the range_start of either of the next diffs. */
+
+ if (high_water_mark < D_HIGHLINE (other_diff, FC))
+ {
+ high_water_thread ^= 1;
+ high_water_mark = D_HIGHLINE (other_diff, FC);
+ }
+
+ /* Set the other diff */
+ other_thread = high_water_thread ^ 0x1;
+ other_diff = current[other_thread];
+ }
+
+ /* The using lists contain a list of all of the blocks to be
+ included in this diff3_block. Create it. */
+
+ tmpblock = using_to_diff3_block (using, last_using,
+ base_water_thread, high_water_thread,
+ last_diff3);
+
+ if (!tmpblock)
+ fatal ("internal error: screwup in format of diff blocks");
+
+ /* Put it on the list. */
+ *result_end = tmpblock;
+ result_end = &tmpblock->next;
+
+ /* Set up corresponding lines correctly. */
+ last_diff3 = tmpblock;
+ }
+ return result;
+}
+
+/* Take two lists of blocks (from two separate diff threads) and put
+ them together into one diff3 block. Return a pointer to this diff3
+ block or 0 for failure.
+
+ All arguments besides using are for the convenience of the routine;
+ they could be derived from the using array. LAST_USING is a pair
+ of pointers to the last blocks in the using structure. LOW_THREAD
+ and HIGH_THREAD tell which threads contain the lowest and highest
+ line numbers for File0. LAST_DIFF3 contains the last diff produced
+ in the calling routine. This is used for lines mappings that
+ would still be identical to the state that diff ended in.
+
+ A distinction should be made in this routine between the two diffs
+ that are part of a normal two diff block, and the three diffs that
+ are part of a diff3_block. */
+
+static struct diff3_block *
+using_to_diff3_block (struct diff_block *using[2],
+ struct diff_block *last_using[2],
+ int low_thread, int high_thread,
+ struct diff3_block const *last_diff3)
+{
+ lin low[2], high[2];
+ struct diff3_block *result;
+ struct diff_block *ptr;
+ int d;
+ lin i;
+
+ /* Find the range in the common file. */
+ lin lowc = D_LOWLINE (using[low_thread], FC);
+ lin highc = D_HIGHLINE (last_using[high_thread], FC);
+
+ /* Find the ranges in the other files.
+ If using[d] is null, that means that the file to which that diff
+ refers is equivalent to the common file over this range. */
+
+ for (d = 0; d < 2; d++)
+ if (using[d])
+ {
+ low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
+ high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
+ }
+ else
+ {
+ low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
+ high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
+ }
+
+ /* Create a block with the appropriate sizes */
+ result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
+
+ /* Copy information for the common file.
+ Return with a zero if any of the compares failed. */
+
+ for (d = 0; d < 2; d++)
+ for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
+ {
+ lin result_offset = D_LOWLINE (ptr, FC) - lowc;
+
+ if (!copy_stringlist (D_LINEARRAY (ptr, FC),
+ D_LENARRAY (ptr, FC),
+ D_LINEARRAY (result, FILEC) + result_offset,
+ D_LENARRAY (result, FILEC) + result_offset,
+ D_NUMLINES (ptr, FC)))
+ return 0;
+ }
+
+ /* Copy information for file d. First deal with anything that might be
+ before the first diff. */
+
+ for (d = 0; d < 2; d++)
+ {
+ struct diff_block *u = using[d];
+ lin lo = low[d], hi = high[d];
+
+ for (i = 0;
+ i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
+ i++)
+ {
+ D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
+ D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
+ }
+
+ for (ptr = u; ptr; ptr = D_NEXT (ptr))
+ {
+ lin result_offset = D_LOWLINE (ptr, FO) - lo;
+ lin linec;
+
+ if (!copy_stringlist (D_LINEARRAY (ptr, FO),
+ D_LENARRAY (ptr, FO),
+ D_LINEARRAY (result, FILE0 + d) + result_offset,
+ D_LENARRAY (result, FILE0 + d) + result_offset,
+ D_NUMLINES (ptr, FO)))
+ return 0;
+
+ /* Catch the lines between here and the next diff */
+ linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
+ for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
+ i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
+ i++)
+ {
+ D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
+ D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
+ linec++;
+ }
+ }
+ }
+
+ /* Set correspond */
+ if (!using[0])
+ D3_TYPE (result) = DIFF_2ND;
+ else if (!using[1])
+ D3_TYPE (result) = DIFF_1ST;
+ else
+ {
+ lin nl0 = D_NUMLINES (result, FILE0);
+ lin nl1 = D_NUMLINES (result, FILE1);
+
+ if (nl0 != nl1
+ || !compare_line_list (D_LINEARRAY (result, FILE0),
+ D_LENARRAY (result, FILE0),
+ D_LINEARRAY (result, FILE1),
+ D_LENARRAY (result, FILE1),
+ nl0))
+ D3_TYPE (result) = DIFF_ALL;
+ else
+ D3_TYPE (result) = DIFF_3RD;
+ }
+
+ return result;
+}
+
+/* Copy pointers from a list of strings to a different list of
+ strings. If a spot in the second list is already filled, make sure
+ that it is filled with the same string; if not, return false, the copy
+ incomplete. Upon successful completion of the copy, return true. */
+
+static bool
+copy_stringlist (char * const fromptrs[], size_t const fromlengths[],
+ char *toptrs[], size_t tolengths[],
+ lin copynum)
+{
+ register char * const *f = fromptrs;
+ register char **t = toptrs;
+ register size_t const *fl = fromlengths;
+ register size_t *tl = tolengths;
+
+ while (copynum--)
+ {
+ if (*t)
+ {
+ if (*fl != *tl || memcmp (*f, *t, *fl) != 0)
+ return false;
+ }
+ else
+ {
+ *t = *f;
+ *tl = *fl;
+ }
+
+ t++; f++; tl++; fl++;
+ }
+
+ return true;
+}
+
+/* Create a diff3_block, with ranges as specified in the arguments.
+ Allocate the arrays for the various pointers (and zero them) based
+ on the arguments passed. Return the block as a result. */
+
+static struct diff3_block *
+create_diff3_block (lin low0, lin high0,
+ lin low1, lin high1,
+ lin low2, lin high2)
+{
+ struct diff3_block *result = xmalloc (sizeof *result);
+ lin numlines;
+
+ D3_TYPE (result) = ERROR;
+ D_NEXT (result) = 0;
+
+ /* Assign ranges */
+ D_LOWLINE (result, FILE0) = low0;
+ D_HIGHLINE (result, FILE0) = high0;
+ D_LOWLINE (result, FILE1) = low1;
+ D_HIGHLINE (result, FILE1) = high1;
+ D_LOWLINE (result, FILE2) = low2;
+ D_HIGHLINE (result, FILE2) = high2;
+
+ /* Allocate and zero space */
+ numlines = D_NUMLINES (result, FILE0);
+ if (numlines)
+ {
+ D_LINEARRAY (result, FILE0) = xcalloc (numlines, sizeof (char *));
+ D_LENARRAY (result, FILE0) = xcalloc (numlines, sizeof (size_t));
+ }
+ else
+ {
+ D_LINEARRAY (result, FILE0) = 0;
+ D_LENARRAY (result, FILE0) = 0;
+ }
+
+ numlines = D_NUMLINES (result, FILE1);
+ if (numlines)
+ {
+ D_LINEARRAY (result, FILE1) = xcalloc (numlines, sizeof (char *));
+ D_LENARRAY (result, FILE1) = xcalloc (numlines, sizeof (size_t));
+ }
+ else
+ {
+ D_LINEARRAY (result, FILE1) = 0;
+ D_LENARRAY (result, FILE1) = 0;
+ }
+
+ numlines = D_NUMLINES (result, FILE2);
+ if (numlines)
+ {
+ D_LINEARRAY (result, FILE2) = xcalloc (numlines, sizeof (char *));
+ D_LENARRAY (result, FILE2) = xcalloc (numlines, sizeof (size_t));
+ }
+ else
+ {
+ D_LINEARRAY (result, FILE2) = 0;
+ D_LENARRAY (result, FILE2) = 0;
+ }
+
+ /* Return */
+ return result;
+}
+
+/* Compare two lists of lines of text.
+ Return 1 if they are equivalent, 0 if not. */
+
+static bool
+compare_line_list (char * const list1[], size_t const lengths1[],
+ char * const list2[], size_t const lengths2[],
+ lin nl)
+{
+ char * const *l1 = list1;
+ char * const *l2 = list2;
+ size_t const *lgths1 = lengths1;
+ size_t const *lgths2 = lengths2;
+
+ while (nl--)
+ if (!*l1 || !*l2 || *lgths1 != *lgths2++
+ || memcmp (*l1++, *l2++, *lgths1++) != 0)
+ return false;
+ return true;
+}
+
+/* Input and parse two way diffs. */
+
+static struct diff_block *
+process_diff (char const *filea,
+ char const *fileb,
+ struct diff_block **last_block,
+ char **buf_to_free)
+{
+ char *diff_contents;
+ char *diff_limit;
+ char *scan_diff;
+ enum diff_type dt;
+ lin i;
+ struct diff_block *block_list;
+ struct diff_block **block_list_end = &block_list;
+ struct diff_block *bptr IF_LINT (= NULL);
+ size_t too_many_lines = (PTRDIFF_MAX
+ / MIN (sizeof *bptr->lines[1],
+ sizeof *bptr->lengths[1]));
+
+ diff_limit = read_diff (filea, fileb, &diff_contents);
+ *buf_to_free = diff_contents;
+ scan_diff = diff_contents;
+
+ while (scan_diff < diff_limit)
+ {
+ bptr = xmalloc (sizeof *bptr);
+ bptr->lines[0] = bptr->lines[1] = 0;
+ bptr->lengths[0] = bptr->lengths[1] = 0;
+
+ dt = process_diff_control (&scan_diff, bptr);
+ if (dt == ERROR || *scan_diff != '\n')
+ {
+ fprintf (stderr, _("%s: diff failed: "), program_name);
+ do
+ {
+ putc (*scan_diff, stderr);
+ }
+ while (*scan_diff++ != '\n');
+ exit (EXIT_TROUBLE);
+ }
+ scan_diff++;
+
+ /* Force appropriate ranges to be null, if necessary */
+ switch (dt)
+ {
+ case ADD:
+ bptr->ranges[0][0]++;
+ break;
+ case DELETE:
+ bptr->ranges[1][0]++;
+ break;
+ case CHANGE:
+ break;
+ default:
+ fatal ("internal error: invalid diff type in process_diff");
+ break;
+ }
+
+ /* Allocate space for the pointers for the lines from filea, and
+ parcel them out among these pointers */
+ if (dt != ADD)
+ {
+ lin numlines = D_NUMLINES (bptr, 0);
+ if (too_many_lines <= numlines)
+ xalloc_die ();
+ bptr->lines[0] = xmalloc (numlines * sizeof *bptr->lines[0]);
+ bptr->lengths[0] = xmalloc (numlines * sizeof *bptr->lengths[0]);
+ for (i = 0; i < numlines; i++)
+ scan_diff = scan_diff_line (scan_diff,
+ &(bptr->lines[0][i]),
+ &(bptr->lengths[0][i]),
+ diff_limit,
+ '<');
+ }
+
+ /* Get past the separator for changes */
+ if (dt == CHANGE)
+ {
+ if (strncmp (scan_diff, "---\n", 4))
+ fatal ("invalid diff format; invalid change separator");
+ scan_diff += 4;
+ }
+
+ /* Allocate space for the pointers for the lines from fileb, and
+ parcel them out among these pointers */
+ if (dt != DELETE)
+ {
+ lin numlines = D_NUMLINES (bptr, 1);
+ if (too_many_lines <= numlines)
+ xalloc_die ();
+ bptr->lines[1] = xmalloc (numlines * sizeof *bptr->lines[1]);
+ bptr->lengths[1] = xmalloc (numlines * sizeof *bptr->lengths[1]);
+ for (i = 0; i < numlines; i++)
+ scan_diff = scan_diff_line (scan_diff,
+ &(bptr->lines[1][i]),
+ &(bptr->lengths[1][i]),
+ diff_limit,
+ '>');
+ }
+
+ /* Place this block on the blocklist. */
+ *block_list_end = bptr;
+ block_list_end = &bptr->next;
+ }
+
+ *block_list_end = NULL;
+ *last_block = bptr;
+ return block_list;
+}
+
+/* Skip tabs and spaces, and return the first character after them. */
+
+static char * _GL_ATTRIBUTE_PURE
+skipwhite (char *s)
+{
+ while (*s == ' ' || *s == '\t')
+ s++;
+ return s;
+}
+
+/* Read a nonnegative line number from S, returning the address of the
+ first character after the line number, and storing the number into
+ *PNUM. Return 0 if S does not point to a valid line number. */
+
+static char *
+readnum (char *s, lin *pnum)
+{
+ unsigned char c = *s;
+ lin num = 0;
+
+ if (! ISDIGIT (c))
+ return 0;
+
+ do
+ {
+ num = c - '0' + num * 10;
+ c = *++s;
+ }
+ while (ISDIGIT (c));
+
+ *pnum = num;
+ return s;
+}
+
+/* Parse a normal format diff control string. Return the type of the
+ diff (ERROR if the format is bad). All of the other important
+ information is filled into to the structure pointed to by db, and
+ the string pointer (whose location is passed to this routine) is
+ updated to point beyond the end of the string parsed. Note that
+ only the ranges in the diff_block will be set by this routine.
+
+ If some specific pair of numbers has been reduced to a single
+ number, then both corresponding numbers in the diff block are set
+ to that number. In general these numbers are interpreted as ranges
+ inclusive, unless being used by the ADD or DELETE commands. It is
+ assumed that these will be special cased in a superior routine. */
+
+static enum diff_type
+process_diff_control (char **string, struct diff_block *db)
+{
+ char *s = *string;
+ enum diff_type type;
+
+ /* Read first set of digits */
+ s = readnum (skipwhite (s), &db->ranges[0][RANGE_START]);
+ if (! s)
+ return ERROR;
+
+ /* Was that the only digit? */
+ s = skipwhite (s);
+ if (*s == ',')
+ {
+ s = readnum (s + 1, &db->ranges[0][RANGE_END]);
+ if (! s)
+ return ERROR;
+ }
+ else
+ db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START];
+
+ /* Get the letter */
+ s = skipwhite (s);
+ switch (*s)
+ {
+ case 'a':
+ type = ADD;
+ break;
+ case 'c':
+ type = CHANGE;
+ break;
+ case 'd':
+ type = DELETE;
+ break;
+ default:
+ return ERROR; /* Bad format */
+ }
+ s++; /* Past letter */
+
+ /* Read second set of digits */
+ s = readnum (skipwhite (s), &db->ranges[1][RANGE_START]);
+ if (! s)
+ return ERROR;
+
+ /* Was that the only digit? */
+ s = skipwhite (s);
+ if (*s == ',')
+ {
+ s = readnum (s + 1, &db->ranges[1][RANGE_END]);
+ if (! s)
+ return ERROR;
+ s = skipwhite (s); /* To move to end */
+ }
+ else
+ db->ranges[1][RANGE_END] = db->ranges[1][RANGE_START];
+
+ *string = s;
+ return type;
+}
+
+static char *
+read_diff (char const *filea,
+ char const *fileb,
+ char **output_placement)
+{
+ char *diff_result;
+ size_t current_chunk_size, total;
+ int fd, wstatus, status;
+ int werrno = 0;
+ struct stat pipestat;
+ char const *argv[9];
+ char const **ap;
+#if HAVE_WORKING_FORK
+ int fds[2];
+ pid_t pid;
+#else
+ FILE *fpipe;
+ char *command;
+#endif
+
+ ap = argv;
+ *ap++ = diff_program;
+ if (text)
+ *ap++ = "-a";
+ if (strip_trailing_cr)
+ *ap++ = "--strip-trailing-cr";
+ *ap++ = "--horizon-lines=100";
+ *ap++ = "--";
+ *ap++ = filea;
+ *ap++ = fileb;
+ *ap = 0;
+
+#if HAVE_WORKING_FORK
+
+ if (pipe (fds) != 0)
+ perror_with_exit ("pipe");
+
+ pid = fork ();
+ if (pid == 0)
+ {
+ /* Child */
+ close (fds[0]);
+ if (fds[1] != STDOUT_FILENO)
+ {
+ dup2 (fds[1], STDOUT_FILENO);
+ close (fds[1]);
+ }
+
+ /* The cast to (char **) is needed for portability to older
+ hosts with a nonstandard prototype for execvp. */
+ execvp (diff_program, (char **) argv);
+
+ _exit (errno == ENOENT ? 127 : 126);
+ }
+
+ if (pid == -1)
+ perror_with_exit ("fork");
+
+ close (fds[1]); /* Prevent erroneous lack of EOF */
+ fd = fds[0];
+
+#else
+
+ command = system_quote_argv (SCI_SYSTEM, (char **) argv);
+ errno = 0;
+ fpipe = popen (command, "r");
+ if (!fpipe)
+ perror_with_exit (command);
+ free (command);
+ fd = fileno (fpipe);
+
+#endif
+
+ if (fstat (fd, &pipestat) != 0)
+ perror_with_exit ("fstat");
+ current_chunk_size = MAX (1, STAT_BLOCKSIZE (pipestat));
+ diff_result = xmalloc (current_chunk_size);
+ total = 0;
+
+ for (;;)
+ {
+ size_t bytes_to_read = current_chunk_size - total;
+ size_t bytes = block_read (fd, diff_result + total, bytes_to_read);
+ total += bytes;
+ if (bytes != bytes_to_read)
+ {
+ if (bytes == SIZE_MAX)
+ perror_with_exit (_("read failed"));
+ break;
+ }
+ if (PTRDIFF_MAX / 2 <= current_chunk_size)
+ xalloc_die ();
+ current_chunk_size *= 2;
+ diff_result = xrealloc (diff_result, current_chunk_size);
+ }
+
+ if (total != 0 && diff_result[total-1] != '\n')
+ fatal ("invalid diff format; incomplete last line");
+
+ *output_placement = diff_result;
+
+#if ! HAVE_WORKING_FORK
+
+ wstatus = pclose (fpipe);
+ if (wstatus == -1)
+ werrno = errno;
+
+#else
+
+ if (close (fd) != 0)
+ perror_with_exit ("close");
+ if (waitpid (pid, &wstatus, 0) < 0)
+ perror_with_exit ("waitpid");
+
+#endif
+
+ status = ! werrno && WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : INT_MAX;
+
+ if (EXIT_TROUBLE <= status)
+ error (EXIT_TROUBLE, werrno,
+ _(status == 126
+ ? "subsidiary program '%s' could not be invoked"
+ : status == 127
+ ? "subsidiary program '%s' not found"
+ : status == INT_MAX
+ ? "subsidiary program '%s' failed"
+ : "subsidiary program '%s' failed (exit status %d)"),
+ diff_program, status);
+
+ return diff_result + total;
+}
+
+
+/* Scan a regular diff line (consisting of > or <, followed by a
+ space, followed by text (including nulls) up to a newline.
+
+ This next routine began life as a macro and many parameters in it
+ are used as call-by-reference values. */
+static char *
+scan_diff_line (char *scan_ptr, char **set_start, size_t *set_length,
+ char *limit, char leadingchar)
+{
+ char *line_ptr;
+
+ if (!(scan_ptr[0] == leadingchar
+ && scan_ptr[1] == ' '))
+ fatal ("invalid diff format; incorrect leading line chars");
+
+ *set_start = line_ptr = scan_ptr + 2;
+ while (*line_ptr++ != '\n')
+ continue;
+
+ /* Include newline if the original line ended in a newline,
+ or if an edit script is being generated.
+ Copy any missing newline message to stderr if an edit script is being
+ generated, because edit scripts cannot handle missing newlines.
+ Return the beginning of the next line. */
+ *set_length = line_ptr - *set_start;
+ if (line_ptr < limit && *line_ptr == '\\')
+ {
+ if (edscript)
+ fprintf (stderr, "%s:", program_name);
+ else
+ --*set_length;
+ line_ptr++;
+ do
+ {
+ if (edscript)
+ putc (*line_ptr, stderr);
+ }
+ while (*line_ptr++ != '\n');
+ }
+
+ return line_ptr;
+}
+
+/* Output a three way diff passed as a list of diff3_block's. The
+ argument MAPPING is indexed by external file number (in the
+ argument list) and contains the internal file number (from the diff
+ passed). This is important because the user expects outputs in
+ terms of the argument list number, and the diff passed may have
+ been done slightly differently (if the last argument was "-", for
+ example). REV_MAPPING is the inverse of MAPPING. */
+
+static void
+output_diff3 (FILE *outputfile, struct diff3_block *diff,
+ int const mapping[3], int const rev_mapping[3])
+{
+ int i;
+ int oddoneout;
+ char *cp;
+ struct diff3_block *ptr;
+ lin line;
+ size_t length;
+ int dontprint;
+ static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
+ char const *line_prefix = initial_tab ? "\t" : " ";
+
+ for (ptr = diff; ptr; ptr = D_NEXT (ptr))
+ {
+ char x[2];
+
+ switch (ptr->correspond)
+ {
+ case DIFF_ALL:
+ x[0] = 0;
+ dontprint = 3; /* Print them all */
+ oddoneout = 3; /* Nobody's odder than anyone else */
+ break;
+ case DIFF_1ST:
+ case DIFF_2ND:
+ case DIFF_3RD:
+ oddoneout = rev_mapping[ptr->correspond - DIFF_1ST];
+
+ x[0] = oddoneout + '1';
+ x[1] = 0;
+ dontprint = oddoneout == 0;
+ break;
+ default:
+ fatal ("internal error: invalid diff type passed to output");
+ }
+ fprintf (outputfile, "====%s\n", x);
+
+ /* Go 0, 2, 1 if the first and third outputs are equivalent. */
+ for (i = 0; i < 3;
+ i = (oddoneout == 1 ? skew_increment[i] : i + 1))
+ {
+ int realfile = mapping[i];
+ lin lowt = D_LOWLINE (ptr, realfile);
+ lin hight = D_HIGHLINE (ptr, realfile);
+ long int llowt = lowt;
+ long int lhight = hight;
+
+ fprintf (outputfile, "%d:", i + 1);
+ switch (lowt - hight)
+ {
+ case 1:
+ fprintf (outputfile, "%lda\n", llowt - 1);
+ break;
+ case 0:
+ fprintf (outputfile, "%ldc\n", llowt);
+ break;
+ default:
+ fprintf (outputfile, "%ld,%ldc\n", llowt, lhight);
+ break;
+ }
+
+ if (i == dontprint) continue;
+
+ if (lowt <= hight)
+ {
+ line = 0;
+ do
+ {
+ fputs (line_prefix, outputfile);
+ cp = D_RELNUM (ptr, realfile, line);
+ length = D_RELLEN (ptr, realfile, line);
+ fwrite (cp, sizeof (char), length, outputfile);
+ }
+ while (++line < hight - lowt + 1);
+ if (cp[length - 1] != '\n')
+ fprintf (outputfile, "\n\\ %s\n",
+ _("No newline at end of file"));
+ }
+ }
+ }
+}
+
+
+/* Output to OUTPUTFILE the lines of B taken from FILENUM. Double any
+ initial '.'s; yield nonzero if any initial '.'s were doubled. */
+
+static bool
+dotlines (FILE *outputfile, struct diff3_block *b, int filenum)
+{
+ lin i;
+ bool leading_dot = false;
+
+ for (i = 0;
+ i < D_NUMLINES (b, filenum);
+ i++)
+ {
+ char *line = D_RELNUM (b, filenum, i);
+ if (line[0] == '.')
+ {
+ leading_dot = true;
+ fputc ('.', outputfile);
+ }
+ fwrite (line, sizeof (char),
+ D_RELLEN (b, filenum, i), outputfile);
+ }
+
+ return leading_dot;
+}
+
+/* Output to OUTPUTFILE a '.' line. If LEADING_DOT is true, also
+ output a command that removes initial '.'s starting with line START
+ and continuing for NUM lines. (START is long int, not lin, for
+ convenience with printf %ld formats.) */
+
+static void
+undotlines (FILE *outputfile, bool leading_dot, long int start, lin num)
+{
+ fputs (".\n", outputfile);
+ if (leading_dot)
+ {
+ if (num == 1)
+ fprintf (outputfile, "%lds/^\\.//\n", start);
+ else
+ fprintf (outputfile, "%ld,%lds/^\\.//\n", start, start + num - 1);
+ }
+}
+
+/* Output a diff3 set of blocks as an ed script. This script applies
+ the changes between file's 2 & 3 to file 1. Take the precise
+ format of the ed script to be output from global variables set
+ during options processing. Reverse the order of
+ the set of diff3 blocks in DIFF; this gets
+ around the problems involved with changing line numbers in an ed
+ script.
+
+ As in 'output_diff3', the variable MAPPING maps from file number
+ according to the argument list to file number according to the diff
+ passed. All files listed below are in terms of the argument list.
+ REV_MAPPING is the inverse of MAPPING.
+
+ FILE0, FILE1 and FILE2 are the strings to print as the names of the
+ three files. These may be the actual names, or may be the
+ arguments specified with -L.
+
+ Return 1 if conflicts were found. */
+
+static bool
+output_diff3_edscript (FILE *outputfile, struct diff3_block *diff,
+ int const mapping[3], int const rev_mapping[3],
+ char const *file0, char const *file1, char const *file2)
+{
+ bool leading_dot;
+ bool conflicts_found = false;
+ bool conflict;
+ struct diff3_block *b;
+
+ for (b = reverse_diff3_blocklist (diff); b; b = b->next)
+ {
+ /* Must do mapping correctly. */
+ enum diff_type type
+ = (b->correspond == DIFF_ALL
+ ? DIFF_ALL
+ : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
+
+ long int low0, high0;
+
+ /* If we aren't supposed to do this output block, skip it. */
+ switch (type)
+ {
+ default: continue;
+ case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
+ case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
+ case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
+ }
+
+ low0 = D_LOWLINE (b, mapping[FILE0]);
+ high0 = D_HIGHLINE (b, mapping[FILE0]);
+
+ if (conflict)
+ {
+ conflicts_found = true;
+
+
+ /* Mark end of conflict. */
+
+ fprintf (outputfile, "%lda\n", high0);
+ leading_dot = false;
+ if (type == DIFF_ALL)
+ {
+ if (show_2nd)
+ {
+ /* Append lines from FILE1. */
+ fprintf (outputfile, "||||||| %s\n", file1);
+ leading_dot = dotlines (outputfile, b, mapping[FILE1]);
+ }
+ /* Append lines from FILE2. */
+ fputs ("=======\n", outputfile);
+ leading_dot |= dotlines (outputfile, b, mapping[FILE2]);
+ }
+ fprintf (outputfile, ">>>>>>> %s\n", file2);
+ undotlines (outputfile, leading_dot, high0 + 2,
+ (D_NUMLINES (b, mapping[FILE1])
+ + D_NUMLINES (b, mapping[FILE2]) + 1));
+
+
+ /* Mark start of conflict. */
+
+ fprintf (outputfile, "%lda\n<<<<<<< %s\n", low0 - 1,
+ type == DIFF_ALL ? file0 : file1);
+ leading_dot = false;
+ if (type == DIFF_2ND)
+ {
+ /* Prepend lines from FILE1. */
+ leading_dot = dotlines (outputfile, b, mapping[FILE1]);
+ fputs ("=======\n", outputfile);
+ }
+ undotlines (outputfile, leading_dot, low0 + 1,
+ D_NUMLINES (b, mapping[FILE1]));
+ }
+ else if (D_NUMLINES (b, mapping[FILE2]) == 0)
+ /* Write out a delete */
+ {
+ if (low0 == high0)
+ fprintf (outputfile, "%ldd\n", low0);
+ else
+ fprintf (outputfile, "%ld,%ldd\n", low0, high0);
+ }
+ else
+ /* Write out an add or change */
+ {
+ switch (high0 - low0)
+ {
+ case -1:
+ fprintf (outputfile, "%lda\n", high0);
+ break;
+ case 0:
+ fprintf (outputfile, "%ldc\n", high0);
+ break;
+ default:
+ fprintf (outputfile, "%ld,%ldc\n", low0, high0);
+ break;
+ }
+
+ undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]),
+ low0, D_NUMLINES (b, mapping[FILE2]));
+ }
+ }
+ if (finalwrite)
+ fputs ("w\nq\n", outputfile);
+ return conflicts_found;
+}
+
+/* Read from INFILE and output to OUTPUTFILE a set of diff3_blocks
+ DIFF as a merged file. This acts like 'ed file0
+ <[output_diff3_edscript]', except that it works even for binary
+ data or incomplete lines.
+
+ As before, MAPPING maps from arg list file number to diff file
+ number, REV_MAPPING is its inverse, and FILE0, FILE1, and FILE2 are
+ the names of the files.
+
+ Return 1 if conflicts were found. */
+
+static bool
+output_diff3_merge (FILE *infile, FILE *outputfile, struct diff3_block *diff,
+ int const mapping[3], int const rev_mapping[3],
+ char const *file0, char const *file1, char const *file2)
+{
+ int c;
+ lin i;
+ bool conflicts_found = false;
+ bool conflict;
+ struct diff3_block *b;
+ lin linesread = 0;
+
+ for (b = diff; b; b = b->next)
+ {
+ /* Must do mapping correctly. */
+ enum diff_type type
+ = ((b->correspond == DIFF_ALL)
+ ? DIFF_ALL
+ : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
+ char const *format_2nd = "<<<<<<< %s\n";
+
+ /* If we aren't supposed to do this output block, skip it. */
+ switch (type)
+ {
+ default: continue;
+ case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
+ case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
+ case DIFF_ALL: if (simple_only) continue; conflict = flagging;
+ format_2nd = "||||||| %s\n";
+ break;
+ }
+
+ /* Copy I lines from file 0. */
+ i = D_LOWLINE (b, FILE0) - linesread - 1;
+ linesread += i;
+ while (0 <= --i)
+ do
+ {
+ c = getc (infile);
+ if (c == EOF)
+ {
+ if (ferror (infile))
+ perror_with_exit (_("read failed"));
+ else if (feof (infile))
+ fatal ("input file shrank");
+ }
+ putc (c, outputfile);
+ }
+ while (c != '\n');
+
+ if (conflict)
+ {
+ conflicts_found = true;
+
+ if (type == DIFF_ALL)
+ {
+ /* Put in lines from FILE0 with bracket. */
+ fprintf (outputfile, "<<<<<<< %s\n", file0);
+ for (i = 0;
+ i < D_NUMLINES (b, mapping[FILE0]);
+ i++)
+ fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
+ D_RELLEN (b, mapping[FILE0], i), outputfile);
+ }
+
+ if (show_2nd)
+ {
+ /* Put in lines from FILE1 with bracket. */
+ fprintf (outputfile, format_2nd, file1);
+ for (i = 0;
+ i < D_NUMLINES (b, mapping[FILE1]);
+ i++)
+ fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char),
+ D_RELLEN (b, mapping[FILE1], i), outputfile);
+ }
+
+ fputs ("=======\n", outputfile);
+ }
+
+ /* Put in lines from FILE2. */
+ for (i = 0;
+ i < D_NUMLINES (b, mapping[FILE2]);
+ i++)
+ fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
+ D_RELLEN (b, mapping[FILE2], i), outputfile);
+
+ if (conflict)
+ fprintf (outputfile, ">>>>>>> %s\n", file2);
+
+ /* Skip I lines in file 0. */
+ i = D_NUMLINES (b, FILE0);
+ linesread += i;
+ while (0 <= --i)
+ while ((c = getc (infile)) != '\n')
+ if (c == EOF)
+ {
+ if (ferror (infile))
+ perror_with_exit (_("read failed"));
+ else if (feof (infile))
+ {
+ if (i || b->next)
+ fatal ("input file shrank");
+ return conflicts_found;
+ }
+ }
+ }
+ /* Copy rest of common file. */
+ while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
+ putc (c, outputfile);
+ return conflicts_found;
+}
+
+/* Reverse the order of the list of diff3 blocks. */
+
+static struct diff3_block *
+reverse_diff3_blocklist (struct diff3_block *diff)
+{
+ register struct diff3_block *tmp, *next, *prev;
+
+ for (tmp = diff, prev = 0; tmp; tmp = next)
+ {
+ next = tmp->next;
+ tmp->next = prev;
+ prev = tmp;
+ }
+
+ return prev;
+}
+
+static void
+fatal (char const *msgid)
+{
+ error (EXIT_TROUBLE, 0, "%s", _(msgid));
+ abort ();
+}
+
+static void
+perror_with_exit (char const *string)
+{
+ error (EXIT_TROUBLE, errno, "%s", string);
+ abort ();
+}
diff --git a/src/dir.c b/src/dir.c
new file mode 100644
index 0000000..c8aa6a5
--- /dev/null
+++ b/src/dir.c
@@ -0,0 +1,385 @@
+/* Read, sort and compare two directories. Used for GNU DIFF.
+
+ Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007,
+ 2009-2013, 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "diff.h"
+#include <error.h>
+#include <exclude.h>
+#include <filenamecat.h>
+#include <setjmp.h>
+#include <xalloc.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
+{
+ size_t nnames; /* Number of names. */
+ char const **names; /* Sorted names of files in dir, followed by 0. */
+ char *data; /* Allocated storage for file names. */
+};
+
+/* Whether file names in directories should be compared with
+ locale-specific sorting. */
+static bool locale_specific_sorting;
+
+/* Where to go if locale-specific sorting fails. */
+static jmp_buf failed_locale_specific_sorting;
+
+static bool dir_loop (struct comparison const *, int);
+
+
+/* Read a directory and get its vector of names. */
+
+static bool
+dir_read (struct file_data const *dir, struct dirdata *dirdata)
+{
+ register struct dirent *next;
+ register size_t 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 false;
+
+ /* Initialize the table of filenames. */
+
+ data_alloc = 512;
+ 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 = _D_EXACT_NAMLEN (next) + 1;
+
+ /* Ignore "." and "..". */
+ if (d_name[0] == '.'
+ && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
+ continue;
+
+ if (excluded_file_name (excluded, d_name))
+ continue;
+
+ while (data_alloc < data_used + d_size)
+ {
+ if (PTRDIFF_MAX / 2 <= data_alloc)
+ xalloc_die ();
+ 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 false;
+ }
+#if CLOSEDIR_VOID
+ closedir (reading);
+#else
+ if (closedir (reading) != 0)
+ return false;
+#endif
+ }
+
+ /* Create the 'names' table from the 'data' table. */
+ if (PTRDIFF_MAX / sizeof *names - 1 <= nnames)
+ xalloc_die ();
+ dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names);
+ dirdata->nnames = nnames;
+ for (i = 0; i < nnames; i++)
+ {
+ names[i] = data;
+ data += strlen (data) + 1;
+ }
+ names[nnames] = 0;
+ return true;
+}
+
+/* Compare strings in a locale-specific way, returning a value
+ compatible with strcmp. */
+
+static int
+compare_collated (char const *name1, char const *name2)
+{
+ int r;
+ errno = 0;
+ if (ignore_file_name_case)
+ r = strcasecoll (name1, name2);
+ else
+ r = strcoll (name1, name2);
+ if (errno)
+ {
+ error (0, errno, _("cannot compare file names '%s' and '%s'"),
+ name1, name2);
+ longjmp (failed_locale_specific_sorting, 1);
+ }
+ return r;
+}
+
+/* Compare file names, returning a value compatible with strcmp. */
+
+static int
+compare_names (char const *name1, char const *name2)
+{
+ if (locale_specific_sorting)
+ {
+ int diff = compare_collated (name1, name2);
+ if (diff || ignore_file_name_case)
+ return diff;
+ }
+ return file_name_cmp (name1, name2);
+}
+
+/* Compare names FILE1 and FILE2 when sorting a directory.
+ Prefer filtered comparison, breaking ties with file_name_cmp. */
+
+static int
+compare_names_for_qsort (void const *file1, void const *file2)
+{
+ char const *const *f1 = file1;
+ char const *const *f2 = file2;
+ char const *name1 = *f1;
+ char const *name2 = *f2;
+ if (locale_specific_sorting)
+ {
+ int diff = compare_collated (name1, name2);
+ if (diff)
+ return diff;
+ }
+ return file_name_cmp (name1, name2);
+}
+
+/* Compare the contents of two directories named in CMP.
+ This is a top-level routine; it does everything necessary for diff
+ on two directories.
+
+ CMP->file[0].desc == -1 says directory CMP->file[0] doesn't exist,
+ but pretend it is empty. Likewise for CMP->file[1].
+
+ HANDLE_FILE is a caller-provided subroutine called to handle each file.
+ It gets three operands: CMP, name of file in dir 0, name of file in dir 1.
+ These names are relative to the original working directory.
+
+ For a file that appears in only one of the dirs, one of the name-args
+ to HANDLE_FILE is zero.
+
+ Returns the maximum of all the values returned by HANDLE_FILE,
+ or EXIT_TROUBLE if trouble is encountered in opening files. */
+
+int
+diff_dirs (struct comparison const *cmp,
+ int (*handle_file) (struct comparison const *,
+ char const *, char const *))
+{
+ struct dirdata dirdata[2];
+ int volatile val = EXIT_SUCCESS;
+ int i;
+
+ if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0))
+ && (cmp->file[1].desc == -1 || dir_loop (cmp, 1)))
+ {
+ error (0, 0, _("%s: recursive directory loop"),
+ cmp->file[cmp->file[0].desc == -1].name);
+ return EXIT_TROUBLE;
+ }
+
+ /* Get contents of both dirs. */
+ for (i = 0; i < 2; i++)
+ if (! dir_read (&cmp->file[i], &dirdata[i]))
+ {
+ perror_with_name (cmp->file[i].name);
+ val = EXIT_TROUBLE;
+ }
+
+ if (val == EXIT_SUCCESS)
+ {
+ char const **volatile names[2];
+ names[0] = dirdata[0].names;
+ names[1] = dirdata[1].names;
+
+ /* Use locale-specific sorting if possible, else native byte order. */
+ locale_specific_sorting = true;
+ if (setjmp (failed_locale_specific_sorting))
+ locale_specific_sorting = false;
+
+ /* Sort the directories. */
+ for (i = 0; i < 2; i++)
+ qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names,
+ compare_names_for_qsort);
+
+ /* If '-S name' was given, and this is the topmost level of comparison,
+ ignore all file names less than the specified starting name. */
+
+ if (starting_file && ! cmp->parent)
+ {
+ while (*names[0] && compare_names (*names[0], starting_file) < 0)
+ names[0]++;
+ while (*names[1] && compare_names (*names[1], starting_file) < 0)
+ names[1]++;
+ }
+
+ /* Loop while files remain in one or both dirs. */
+ while (*names[0] || *names[1])
+ {
+ /* 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 = (!*names[0] ? 1 : !*names[1] ? -1
+ : compare_names (*names[0], *names[1]));
+
+ /* Prefer a file_name_cmp match if available. This algorithm is
+ O(N**2), where N is the number of names in a directory
+ that compare_names says are all equal, but in practice N
+ is so small it's not worth tuning. */
+ if (nameorder == 0 && ignore_file_name_case)
+ {
+ int raw_order = file_name_cmp (*names[0], *names[1]);
+ if (raw_order != 0)
+ {
+ int greater_side = raw_order < 0;
+ int lesser_side = 1 - greater_side;
+ char const **lesser = names[lesser_side];
+ char const *greater_name = *names[greater_side];
+ char const **p;
+
+ for (p = lesser + 1;
+ *p && compare_names (*p, greater_name) == 0;
+ p++)
+ {
+ int c = file_name_cmp (*p, greater_name);
+ if (0 <= c)
+ {
+ if (c == 0)
+ {
+ memmove (lesser + 1, lesser,
+ (char *) p - (char *) lesser);
+ *lesser = greater_name;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ int v1 = (*handle_file) (cmp,
+ 0 < nameorder ? 0 : *names[0]++,
+ nameorder < 0 ? 0 : *names[1]++);
+ if (val < v1)
+ val = v1;
+ }
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ free (dirdata[i].names);
+ free (dirdata[i].data);
+ }
+
+ return val;
+}
+
+/* Return nonzero if CMP is looping recursively in argument I. */
+
+static bool _GL_ATTRIBUTE_PURE
+dir_loop (struct comparison const *cmp, int i)
+{
+ struct comparison const *p = cmp;
+ while ((p = p->parent))
+ if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat))
+ return true;
+ return false;
+}
+
+/* Find a matching filename in a directory. */
+
+char *
+find_dir_file_pathname (char const *dir, char const *file)
+{
+ /* The 'IF_LINT (volatile)' works around what appears to be a bug in
+ gcc 4.8.0 20120825; see
+ <http://lists.gnu.org/archive/html/bug-diffutils/2012-08/msg00007.html>.
+ */
+ char const * IF_LINT (volatile) match = file;
+
+ char *val;
+ struct dirdata dirdata;
+ dirdata.names = NULL;
+ dirdata.data = NULL;
+
+ if (ignore_file_name_case)
+ {
+ struct file_data filedata;
+ filedata.name = dir;
+ filedata.desc = 0;
+
+ if (dir_read (&filedata, &dirdata))
+ {
+ locale_specific_sorting = true;
+ if (setjmp (failed_locale_specific_sorting))
+ match = file; /* longjmp may mess up MATCH. */
+ else
+ {
+ for (char const **p = dirdata.names; *p; p++)
+ if (compare_names (*p, file) == 0)
+ {
+ if (file_name_cmp (*p, file) == 0)
+ {
+ match = *p;
+ break;
+ }
+ if (match == file)
+ match = *p;
+ }
+ }
+ }
+ }
+
+ val = file_name_concat (dir, match, NULL);
+ free (dirdata.names);
+ free (dirdata.data);
+ return val;
+}
diff --git a/src/ed.c b/src/ed.c
new file mode 100644
index 0000000..1fae2b8
--- /dev/null
+++ b/src/ed.c
@@ -0,0 +1,175 @@
+/* Output routines for ed-script format.
+
+ Copyright (C) 1988-1989, 1991-1993, 1995, 1998, 2001, 2004, 2006, 2009-2013,
+ 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "diff.h"
+
+static void print_ed_hunk (struct change *);
+static void print_rcs_hunk (struct change *);
+static void pr_forward_ed_hunk (struct change *);
+
+/* Print our script as ed commands. */
+
+void
+print_ed_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 (struct change *hunk)
+{
+ lin f0, l0, f1, l1;
+ enum changes changes;
+
+#ifdef DEBUG
+ debug_script (hunk);
+#endif
+
+ /* Determine range of line numbers involved in each file. */
+ changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
+ if (!changes)
+ return;
+
+ begin_output ();
+
+ /* Print out the line number header for this hunk */
+ print_number_range (',', &files[0], f0, l0);
+ fputc (change_letter[changes], outfile);
+ fputc ('\n', outfile);
+
+ /* Print new/changed lines from second file, if needed */
+ if (changes != OLD)
+ {
+ lin i;
+ bool insert_mode = true;
+
+ for (i = f1; i <= l1; i++)
+ {
+ if (!insert_mode)
+ {
+ fputs ("a\n", outfile);
+ insert_mode = true;
+ }
+ if (files[1].linbuf[i][0] == '.' && files[1].linbuf[i][1] == '\n')
+ {
+ /* The file's line is just a dot, and it would exit
+ insert mode. Precede the dot with another dot, exit
+ insert mode and remove the extra dot. */
+ fputs ("..\n.\ns/.//\n", outfile);
+ insert_mode = false;
+ }
+ else
+ print_1_line ("", &files[1].linbuf[i]);
+ }
+
+ if (insert_mode)
+ fputs (".\n", outfile);
+ }
+}
+
+/* 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.
+ Because of the issue with lines containing just a dot, the output
+ is not even parseable. */
+
+void
+pr_forward_ed_script (struct change *script)
+{
+ print_script (script, find_change, pr_forward_ed_hunk);
+}
+
+static void
+pr_forward_ed_hunk (struct change *hunk)
+{
+ lin i, f0, l0, f1, l1;
+
+ /* Determine range of line numbers involved in each file. */
+ enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
+ if (!changes)
+ return;
+
+ begin_output ();
+
+ fputc (change_letter[changes], outfile);
+ print_number_range (' ', files, f0, l0);
+ fputc ('\n', outfile);
+
+ /* If deletion only, print just the number range. */
+
+ if (changes == OLD)
+ 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]);
+
+ fputs (".\n", outfile);
+}
+
+/* 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 (struct change *script)
+{
+ print_script (script, find_change, print_rcs_hunk);
+}
+
+/* Print a hunk of an RCS diff */
+
+static void
+print_rcs_hunk (struct change *hunk)
+{
+ lin i, f0, l0, f1, l1;
+ long int tf0, tl0, tf1, tl1;
+
+ /* Determine range of line numbers involved in each file. */
+ enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
+ if (!changes)
+ return;
+
+ begin_output ();
+
+ translate_range (&files[0], f0, l0, &tf0, &tl0);
+
+ if (changes & OLD)
+ {
+ /* For deletion, print just the starting line number from file 0
+ and the number of lines deleted. */
+ fprintf (outfile, "d%ld %ld\n", tf0, tf0 <= tl0 ? tl0 - tf0 + 1 : 1);
+ }
+
+ if (changes & NEW)
+ {
+ /* Take last-line-number from file 0 and # lines from file 1. */
+ translate_range (&files[1], f1, l1, &tf1, &tl1);
+ fprintf (outfile, "a%ld %ld\n", tl0, tf1 <= tl1 ? 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/ifdef.c b/src/ifdef.c
new file mode 100644
index 0000000..b8b084f
--- /dev/null
+++ b/src/ifdef.c
@@ -0,0 +1,430 @@
+/* #ifdef-format output routines for GNU DIFF.
+
+ Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2016
+ 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 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 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"
+
+#include <xalloc.h>
+
+struct group
+{
+ struct file_data const *file;
+ lin from, upto; /* start and limit lines for this group of lines */
+};
+
+static char const *format_group (FILE *, char const *, char,
+ struct group const *);
+static char const *do_printf_spec (FILE *, char const *,
+ struct file_data const *, lin,
+ struct group const *);
+static char const *scan_char_literal (char const *, char *);
+static lin groups_letter_value (struct group const *, char);
+static void format_ifdef (char const *, lin, lin, lin, lin);
+static void print_ifdef_hunk (struct change *);
+static void print_ifdef_lines (FILE *, char const *, struct group const *);
+
+static lin next_line0;
+static lin next_line1;
+
+/* Print the edit-script SCRIPT as a merged #ifdef file. */
+
+void
+print_ifdef_script (struct change *script)
+{
+ next_line0 = next_line1 = - files[0].prefix_lines;
+ print_script (script, find_change, print_ifdef_hunk);
+ if (next_line0 < files[0].valid_lines
+ || next_line1 < files[1].valid_lines)
+ {
+ begin_output ();
+ format_ifdef (group_format[UNCHANGED],
+ next_line0, files[0].valid_lines,
+ next_line1, 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 (struct change *hunk)
+{
+ lin first0, last0, first1, last1;
+
+ /* Determine range of line numbers involved in each file. */
+ enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
+ if (!changes)
+ return;
+
+ begin_output ();
+
+ /* Print lines up to this change. */
+ if (next_line0 < first0 || next_line1 < first1)
+ format_ifdef (group_format[UNCHANGED],
+ next_line0, first0,
+ next_line1, first1);
+
+ /* Print this change. */
+ next_line0 = last0 + 1;
+ next_line1 = last1 + 1;
+ format_ifdef (group_format[changes],
+ first0, next_line0,
+ first1, next_line1);
+}
+
+/* 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 (char const *format, lin beg0, lin end0, lin beg1, lin 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 const *
+format_group (register FILE *out, char const *format, char endchar,
+ struct group const *groups)
+{
+ register char c;
+ register char const *f = format;
+
+ while ((c = *f) != endchar && c != 0)
+ {
+ char const *f1 = ++f;
+ if (c == '%')
+ switch ((c = *f++))
+ {
+ case '%':
+ break;
+
+ case '(':
+ /* Print if-then-else format e.g. '%(n=1?thenpart:elsepart)'. */
+ {
+ int i;
+ uintmax_t value[2];
+ FILE *thenout, *elseout;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (ISDIGIT (*f))
+ {
+ char *fend;
+ errno = 0;
+ value[i] = strtoumax (f, &fend, 10);
+ if (errno)
+ goto bad_format;
+ f = fend;
+ }
+ else
+ {
+ value[i] = groups_letter_value (groups, *f);
+ if (value[i] == -1)
+ 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:
+ f = do_printf_spec (out, f - 2, 0, 0, groups);
+ if (f)
+ continue;
+ /* Fall through. */
+ bad_format:
+ c = '%';
+ f = f1;
+ 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 lin
+groups_letter_value (struct group const *g, char letter)
+{
+ switch (letter)
+ {
+ case 'E': letter = 'e'; g++; break;
+ case 'F': letter = 'f'; g++; break;
+ case 'L': letter = 'l'; g++; break;
+ case 'M': letter = 'm'; g++; break;
+ case 'N': letter = 'n'; g++; break;
+ }
+
+ 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 (register FILE *out, char const *format,
+ struct group const *group)
+{
+ struct file_data const *file = group->file;
+ char const * const *linbuf = file->linbuf;
+ lin from = group->from, upto = group->upto;
+
+ if (!out)
+ return;
+
+ /* If possible, use a single fwrite; it's faster. */
+ if (!expand_tabs && format[0] == '%')
+ {
+ if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
+ {
+ 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 const *f = format;
+
+ while ((c = *f++) != 0)
+ {
+ char const *f1 = f;
+ if (c == '%')
+ 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:
+ f = do_printf_spec (out, f - 2, file, from, 0);
+ if (f)
+ continue;
+ c = '%';
+ f = f1;
+ break;
+ }
+
+ putc (c, out);
+ }
+ }
+}
+
+static char const *
+do_printf_spec (FILE *out, char const *spec,
+ struct file_data const *file, lin n,
+ struct group const *groups)
+{
+ char const *f = spec;
+ char c;
+ char c1;
+
+ /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
+ /* assert (*f == '%'); */
+ f++;
+ while ((c = *f++) == '-' || c == '\'' || c == '0')
+ continue;
+ while (ISDIGIT (c))
+ c = *f++;
+ if (c == '.')
+ while (ISDIGIT (c = *f++))
+ continue;
+ c1 = *f++;
+
+ switch (c)
+ {
+ case 'c':
+ if (c1 != '\'')
+ return 0;
+ else
+ {
+ char value IF_LINT (= 0);
+ f = scan_char_literal (f, &value);
+ if (!f)
+ return 0;
+ if (out)
+ putc (value, out);
+ }
+ break;
+
+ case 'd': case 'o': case 'x': case 'X':
+ {
+ lin value;
+
+ if (file)
+ {
+ if (c1 != 'n')
+ return 0;
+ value = translate_line_number (file, n);
+ }
+ else
+ {
+ value = groups_letter_value (groups, c1);
+ if (value < 0)
+ return 0;
+ }
+
+ if (out)
+ {
+ /* For example, if the spec is "%3xn", use the printf
+ format spec "%3lx". Here the spec prefix is "%3". */
+ long int long_value = value;
+ size_t spec_prefix_len = f - spec - 2;
+#if HAVE_C_VARARRAYS
+ char format[spec_prefix_len + 3];
+#else
+ char *format = xmalloc (spec_prefix_len + 3);
+#endif
+ char *p = format + spec_prefix_len;
+ memcpy (format, spec, spec_prefix_len);
+ *p++ = 'l';
+ *p++ = c;
+ *p = '\0';
+ fprintf (out, format, long_value);
+#if ! HAVE_C_VARARRAYS
+ free (format);
+#endif
+ }
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return f;
+}
+
+/* Scan the character literal represented in the string LIT; LIT points just
+ after the initial apostrophe. Put the literal's value into *VALPTR.
+ Yield the address of the first character after the closing apostrophe,
+ or a null pointer if the literal is ill-formed. */
+static char const *
+scan_char_literal (char const *lit, char *valptr)
+{
+ register char const *p = lit;
+ char value;
+ ptrdiff_t digits;
+ char c = *p++;
+
+ switch (c)
+ {
+ case 0:
+ case '\'':
+ return NULL;
+
+ case '\\':
+ value = 0;
+ while ((c = *p++) != '\'')
+ {
+ unsigned int digit = c - '0';
+ if (8 <= digit)
+ return NULL;
+ value = 8 * value + digit;
+ }
+ digits = p - lit - 2;
+ if (! (1 <= digits && digits <= 3))
+ return NULL;
+ break;
+
+ default:
+ value = c;
+ if (*p++ != '\'')
+ return NULL;
+ break;
+ }
+
+ *valptr = value;
+ return p;
+}
diff --git a/src/io.c b/src/io.c
new file mode 100644
index 0000000..410bfef
--- /dev/null
+++ b/src/io.c
@@ -0,0 +1,830 @@
+/* File I/O for GNU DIFF.
+
+ Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
+ 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "diff.h"
+#include <binary-io.h>
+#include <cmpbuf.h>
+#include <file-type.h>
+#include <xalloc.h>
+
+/* Rotate an unsigned value to the left. */
+#define ROL(v, n) ((v) << (n) | (v) >> (sizeof (v) * CHAR_BIT - (n)))
+
+/* Given a hash value and a new character, return a new hash value. */
+#define HASH(h, c) ((c) + ROL (h, 7))
+
+/* The type of a hash value. */
+typedef size_t hash_value;
+verify (! TYPE_SIGNED (hash_value));
+
+/* Lines are put into equivalence classes of lines that match in lines_differ.
+ 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
+{
+ lin next; /* Next item in this bucket. */
+ hash_value hash; /* Hash of lines in this class. */
+ char const *line; /* A line that fits this class. */
+ size_t length; /* That line's length, not counting its newline. */
+};
+
+/* Hash-table: array of buckets, each being a chain of equivalence classes.
+ buckets[-1] is reserved for incomplete lines. */
+static lin *buckets;
+
+/* Number of buckets in the hash table array, not counting buckets[-1]. */
+static size_t nbuckets;
+
+/* Array in which the equivalence classes are allocated.
+ The bucket-chains go through the elements in this array.
+ The number of an equivalence class is its index in this array. */
+static struct equivclass *equivs;
+
+/* Index of first free element in the array 'equivs'. */
+static lin equivs_index;
+
+/* Number of elements allocated in the array 'equivs'. */
+static lin equivs_alloc;
+
+/* Read a block of data into a file buffer, checking for EOF and error. */
+
+void
+file_block_read (struct file_data *current, size_t size)
+{
+ if (size && ! current->eof)
+ {
+ size_t s = block_read (current->desc,
+ FILE_BUFFER (current) + current->buffered, size);
+ if (s == SIZE_MAX)
+ pfatal_with_name (current->name);
+ current->buffered += s;
+ current->eof = s < size;
+ }
+}
+
+/* Check for binary files and compare them for exact identity. */
+
+/* Return 1 if BUF contains a non text character.
+ SIZE is the number of characters in BUF. */
+
+#define binary_file_p(buf, size) (memchr (buf, 0, size) != 0)
+
+/* Get ready to read the current file.
+ Return nonzero if SKIP_TEST is zero,
+ and if it appears to be a binary file. */
+
+static bool
+sip (struct file_data *current, bool skip_test)
+{
+ /* If we have a nonexistent file at this stage, treat it as empty. */
+ if (current->desc < 0)
+ {
+ /* Leave room for a sentinel. */
+ current->bufsize = sizeof (word);
+ current->buffer = xmalloc (current->bufsize);
+ }
+ else
+ {
+ current->bufsize = buffer_lcm (sizeof (word),
+ STAT_BLOCKSIZE (current->stat),
+ PTRDIFF_MAX - 2 * sizeof (word));
+ current->buffer = xmalloc (current->bufsize);
+
+#ifdef __KLIBC__
+ /* Skip test if seek is not possible */
+ skip_test = skip_test
+ || (lseek (current->desc, 0, SEEK_CUR) < 0
+ && errno == ESPIPE);
+#endif
+
+ if (! skip_test)
+ {
+ /* Check first part of file to see if it's a binary file. */
+
+ int prev_mode = set_binary_mode (current->desc, O_BINARY);
+ off_t buffered;
+ file_block_read (current, current->bufsize);
+ buffered = current->buffered;
+
+ if (prev_mode != O_BINARY)
+ {
+ /* Revert to text mode and seek back to the start to reread
+ the file. Use relative seek, since file descriptors
+ like stdin might not start at offset zero. */
+ if (lseek (current->desc, - buffered, SEEK_CUR) < 0)
+ pfatal_with_name (current->name);
+ set_binary_mode (current->desc, prev_mode);
+ current->buffered = 0;
+ current->eof = false;
+ }
+
+ return binary_file_p (current->buffer, buffered);
+ }
+ }
+
+ current->buffered = 0;
+ current->eof = false;
+ return false;
+}
+
+/* Slurp the rest of the current file completely into memory. */
+
+static void
+slurp (struct file_data *current)
+{
+ size_t cc;
+
+ if (current->desc < 0)
+ {
+ /* The file is nonexistent. */
+ return;
+ }
+
+ if (S_ISREG (current->stat.st_mode))
+ {
+ /* It's a regular file; slurp in the rest all at once. */
+
+ /* Get the size out of the stat block.
+ Allocate just enough room for appended newline plus word sentinel,
+ plus word-alignment since we want the buffer word-aligned. */
+ size_t file_size = current->stat.st_size;
+ cc = file_size + 2 * sizeof (word) - file_size % sizeof (word);
+ if (file_size != current->stat.st_size || cc < file_size
+ || PTRDIFF_MAX <= cc)
+ xalloc_die ();
+
+ if (current->bufsize < cc)
+ {
+ current->bufsize = cc;
+ current->buffer = xrealloc (current->buffer, cc);
+ }
+
+ /* Try to read at least 1 more byte than the size indicates, to
+ detect whether the file is growing. This is a nicety for
+ users who run 'diff' on files while they are changing. */
+
+ if (current->buffered <= file_size)
+ {
+ file_block_read (current, file_size + 1 - current->buffered);
+ if (current->buffered <= file_size)
+ return;
+ }
+ }
+
+ /* It's not a regular file, or it's a growing regular file; read it,
+ growing the buffer as needed. */
+
+ file_block_read (current, current->bufsize - current->buffered);
+
+ if (current->buffered)
+ {
+ while (current->buffered == current->bufsize)
+ {
+ if (PTRDIFF_MAX / 2 - sizeof (word) < current->bufsize)
+ xalloc_die ();
+ current->bufsize *= 2;
+ current->buffer = xrealloc (current->buffer, current->bufsize);
+ file_block_read (current, current->bufsize - current->buffered);
+ }
+
+ /* Allocate just enough room for appended newline plus word
+ sentinel, plus word-alignment. */
+ cc = current->buffered + 2 * sizeof (word);
+ current->bufsize = cc - cc % sizeof (word);
+ current->buffer = xrealloc (current->buffer, current->bufsize);
+ }
+}
+
+/* Split the file into lines, simultaneously computing the equivalence
+ class for each line. */
+
+static void
+find_and_hash_each_line (struct file_data *current)
+{
+ char const *p = current->prefix_end;
+ lin i, *bucket;
+ size_t length;
+
+ /* Cache often-used quantities in local variables to help the compiler. */
+ char const **linbuf = current->linbuf;
+ lin alloc_lines = current->alloc_lines;
+ lin line = 0;
+ lin linbuf_base = current->linbuf_base;
+ lin *cureqs = xmalloc (alloc_lines * sizeof *cureqs);
+ struct equivclass *eqs = equivs;
+ lin eqs_index = equivs_index;
+ lin eqs_alloc = equivs_alloc;
+ char const *suffix_begin = current->suffix_begin;
+ char const *bufend = FILE_BUFFER (current) + current->buffered;
+ bool ig_case = ignore_case;
+ enum DIFF_white_space ig_white_space = ignore_white_space;
+ bool diff_length_compare_anyway =
+ ig_white_space != IGNORE_NO_WHITE_SPACE;
+ bool same_length_diff_contents_compare_anyway =
+ diff_length_compare_anyway | ig_case;
+
+ while (p < suffix_begin)
+ {
+ char const *ip = p;
+ hash_value h = 0;
+ unsigned char c;
+
+ /* Hash this line until we find a newline. */
+ switch (ig_white_space)
+ {
+ case IGNORE_ALL_SPACE:
+ while ((c = *p++) != '\n')
+ if (! isspace (c))
+ h = HASH (h, ig_case ? tolower (c) : c);
+ break;
+
+ case IGNORE_SPACE_CHANGE:
+ while ((c = *p++) != '\n')
+ {
+ if (isspace (c))
+ {
+ do
+ if ((c = *p++) == '\n')
+ goto hashing_done;
+ while (isspace (c));
+
+ h = HASH (h, ' ');
+ }
+
+ /* C is now the first non-space. */
+ h = HASH (h, ig_case ? tolower (c) : c);
+ }
+ break;
+
+ case IGNORE_TAB_EXPANSION:
+ case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
+ case IGNORE_TRAILING_SPACE:
+ {
+ size_t column = 0;
+ while ((c = *p++) != '\n')
+ {
+ if (ig_white_space & IGNORE_TRAILING_SPACE
+ && isspace (c))
+ {
+ char const *p1 = p;
+ unsigned char c1;
+ do
+ if ((c1 = *p1++) == '\n')
+ {
+ p = p1;
+ goto hashing_done;
+ }
+ while (isspace (c1));
+ }
+
+ size_t repetitions = 1;
+
+ if (ig_white_space & IGNORE_TAB_EXPANSION)
+ switch (c)
+ {
+ case '\b':
+ column -= 0 < column;
+ break;
+
+ case '\t':
+ c = ' ';
+ repetitions = tabsize - column % tabsize;
+ column = (column + repetitions < column
+ ? 0
+ : column + repetitions);
+ break;
+
+ case '\r':
+ column = 0;
+ break;
+
+ default:
+ column++;
+ break;
+ }
+
+ if (ig_case)
+ c = tolower (c);
+
+ do
+ h = HASH (h, c);
+ while (--repetitions != 0);
+ }
+ }
+ break;
+
+ default:
+ if (ig_case)
+ while ((c = *p++) != '\n')
+ h = HASH (h, tolower (c));
+ else
+ while ((c = *p++) != '\n')
+ h = HASH (h, c);
+ break;
+ }
+
+ hashing_done:;
+
+ bucket = &buckets[h % nbuckets];
+ length = p - ip - 1;
+
+ if (p == bufend
+ && current->missing_newline
+ && ROBUST_OUTPUT_STYLE (output_style))
+ {
+ /* The last line is incomplete and we do not silently
+ complete lines. If the line cannot compare equal to any
+ complete line, put it into buckets[-1] so that it can
+ compare equal only to the other file's incomplete line
+ (if one exists). */
+ if (ig_white_space < IGNORE_TRAILING_SPACE)
+ bucket = &buckets[-1];
+ }
+
+ for (i = *bucket; ; i = eqs[i].next)
+ if (!i)
+ {
+ /* Create a new equivalence class in this bucket. */
+ i = eqs_index++;
+ if (i == eqs_alloc)
+ {
+ if (PTRDIFF_MAX / (2 * sizeof *eqs) <= eqs_alloc)
+ xalloc_die ();
+ eqs_alloc *= 2;
+ eqs = xrealloc (eqs, eqs_alloc * sizeof *eqs);
+ }
+ eqs[i].next = *bucket;
+ eqs[i].hash = h;
+ eqs[i].line = ip;
+ eqs[i].length = length;
+ *bucket = i;
+ break;
+ }
+ else if (eqs[i].hash == h)
+ {
+ char const *eqline = eqs[i].line;
+
+ /* Reuse existing class if lines_differ reports the lines
+ equal. */
+ if (eqs[i].length == length)
+ {
+ /* Reuse existing equivalence class if the lines are identical.
+ This detects the common case of exact identity
+ faster than lines_differ would. */
+ if (memcmp (eqline, ip, length) == 0)
+ break;
+ if (!same_length_diff_contents_compare_anyway)
+ continue;
+ }
+ else if (!diff_length_compare_anyway)
+ continue;
+
+ if (! lines_differ (eqline, ip))
+ break;
+ }
+
+ /* Maybe increase the size of the line table. */
+ if (line == alloc_lines)
+ {
+ /* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */
+ if (PTRDIFF_MAX / 3 <= alloc_lines
+ || PTRDIFF_MAX / sizeof *cureqs <= 2 * alloc_lines - linbuf_base
+ || PTRDIFF_MAX / sizeof *linbuf <= alloc_lines - linbuf_base)
+ xalloc_die ();
+ alloc_lines = 2 * alloc_lines - linbuf_base;
+ cureqs = xrealloc (cureqs, alloc_lines * sizeof *cureqs);
+ linbuf += linbuf_base;
+ linbuf = xrealloc (linbuf,
+ (alloc_lines - linbuf_base) * sizeof *linbuf);
+ 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. */
+ if (PTRDIFF_MAX / 3 <= alloc_lines
+ || PTRDIFF_MAX / sizeof *cureqs <= 2 * alloc_lines - linbuf_base
+ || PTRDIFF_MAX / sizeof *linbuf <= alloc_lines - linbuf_base)
+ xalloc_die ();
+ alloc_lines = 2 * alloc_lines - linbuf_base;
+ linbuf += linbuf_base;
+ linbuf = xrealloc (linbuf,
+ (alloc_lines - linbuf_base) * sizeof *linbuf);
+ linbuf -= linbuf_base;
+ }
+ linbuf[line] = p;
+
+ if (p == bufend)
+ {
+ /* If the last line is incomplete and we do not silently
+ complete lines, don't count its appended newline. */
+ if (current->missing_newline && ROBUST_OUTPUT_STYLE (output_style))
+ linbuf[line]--;
+ break;
+ }
+
+ if (context <= i && no_diff_means_no_output)
+ break;
+
+ line++;
+
+ while (*p++ != '\n')
+ continue;
+ }
+
+ /* Done with cache in local variables. */
+ current->linbuf = linbuf;
+ current->valid_lines = line;
+ current->alloc_lines = alloc_lines;
+ current->equivs = cureqs;
+ equivs = eqs;
+ equivs_alloc = eqs_alloc;
+ equivs_index = eqs_index;
+}
+
+/* Prepare the text. Make sure the text end is initialized.
+ Make sure text ends in a newline,
+ but remember that we had to add one.
+ Strip trailing CRs, if that was requested. */
+
+static void
+prepare_text (struct file_data *current)
+{
+ size_t buffered = current->buffered;
+ char *p = FILE_BUFFER (current);
+
+ if (buffered == 0 || p[buffered - 1] == '\n')
+ current->missing_newline = false;
+ else
+ {
+ p[buffered++] = '\n';
+ current->missing_newline = true;
+ }
+
+ if (!p)
+ return;
+
+ /* Don't use uninitialized storage when planting or using sentinels. */
+ memset (p + buffered, 0, sizeof (word));
+
+ if (strip_trailing_cr)
+ {
+ char *dst;
+ char *srclim = p + buffered;
+ *srclim = '\r';
+ dst = rawmemchr (p, '\r');
+
+ if (dst != srclim)
+ {
+ char const *src = dst;
+ do
+ {
+ *dst = *src++;
+ dst += ! (*dst == '\r' && *src == '\n');
+ }
+ while (src < srclim);
+
+ buffered -= src - dst;
+ }
+ }
+
+ current->buffered = buffered;
+}
+
+/* We have found N lines in a buffer of size S; guess the
+ proportionate number of lines that will be found in a buffer of
+ size T. However, do not guess a number of lines so large that the
+ resulting line table might cause overflow in size calculations. */
+static lin
+guess_lines (lin n, size_t s, size_t t)
+{
+ size_t guessed_bytes_per_line = n < 10 ? 32 : s / (n - 1);
+ lin guessed_lines = MAX (1, t / guessed_bytes_per_line);
+ return MIN (guessed_lines, PTRDIFF_MAX / (2 * sizeof (char *) + 1) - 5) + 5;
+}
+
+/* Given a vector of two file_data objects, find the identical
+ prefixes and suffixes of each object. */
+
+static void
+find_identical_ends (struct file_data filevec[])
+{
+ word *w0, *w1;
+ char *p0, *p1, *buffer0, *buffer1;
+ char const *end0, *beg0;
+ char const **linbuf0, **linbuf1;
+ lin i, lines;
+ size_t n0, n1;
+ lin alloc_lines0, alloc_lines1;
+ bool prefix_needed;
+ lin buffered_prefix, prefix_count, prefix_mask;
+ lin middle_guess, suffix_guess;
+
+ slurp (&filevec[0]);
+ prepare_text (&filevec[0]);
+ if (filevec[0].desc != filevec[1].desc)
+ {
+ slurp (&filevec[1]);
+ prepare_text (&filevec[1]);
+ }
+ else
+ {
+ filevec[1].buffer = filevec[0].buffer;
+ filevec[1].bufsize = filevec[0].bufsize;
+ filevec[1].buffered = filevec[0].buffered;
+ filevec[1].missing_newline = filevec[0].missing_newline;
+ }
+
+ /* Find identical prefix. */
+
+ w0 = filevec[0].buffer;
+ w1 = filevec[1].buffer;
+ p0 = buffer0 = (char *) w0;
+ p1 = buffer1 = (char *) w1;
+ n0 = filevec[0].buffered;
+ n1 = filevec[1].buffered;
+
+ 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. */
+ 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')
+ continue;
+
+ p1 += p0 - beg0;
+ }
+
+ /* Record the suffix. */
+ filevec[0].suffix_begin = p0;
+ filevec[1].suffix_begin = p1;
+
+ /* Calculate number of lines of prefix to save.
+
+ prefix_count == 0 means save the whole prefix;
+ we need this for options like -D that output the whole file,
+ or for enormous contexts (to avoid worrying about arithmetic overflow).
+ 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.fastmap
+ && context < LIN_MAX / 4 && context < n0)
+ {
+ middle_guess = guess_lines (0, 0, p0 - filevec[0].prefix_end);
+ suffix_guess = guess_lines (0, 0, buffer0 + n0 - p0);
+ for (prefix_count = 1; prefix_count <= context; prefix_count *= 2)
+ continue;
+ alloc_lines0 = (prefix_count + middle_guess
+ + MIN (context, suffix_guess));
+ }
+ else
+ {
+ prefix_count = 0;
+ alloc_lines0 = guess_lines (0, 0, n0);
+ }
+
+ prefix_mask = prefix_count - 1;
+ lines = 0;
+ linbuf0 = xmalloc (alloc_lines0 * sizeof *linbuf0);
+ prefix_needed = ! (no_diff_means_no_output
+ && filevec[0].prefix_end == p0
+ && filevec[1].prefix_end == p1);
+ p0 = buffer0;
+
+ /* If the prefix is needed, find the prefix lines. */
+ if (prefix_needed)
+ {
+ end0 = filevec[0].prefix_end;
+ while (p0 != end0)
+ {
+ lin l = lines++ & prefix_mask;
+ if (l == alloc_lines0)
+ {
+ if (PTRDIFF_MAX / (2 * sizeof *linbuf0) <= alloc_lines0)
+ xalloc_die ();
+ alloc_lines0 *= 2;
+ linbuf0 = xrealloc (linbuf0, alloc_lines0 * sizeof *linbuf0);
+ }
+ linbuf0[l] = p0;
+ while (*p0++ != '\n')
+ continue;
+ }
+ }
+ buffered_prefix = prefix_count && context < lines ? context : lines;
+
+ /* Allocate line buffer 1. */
+
+ middle_guess = guess_lines (lines, p0 - buffer0, p1 - filevec[1].prefix_end);
+ suffix_guess = guess_lines (lines, p0 - buffer0, buffer1 + n1 - p1);
+ alloc_lines1 = buffered_prefix + middle_guess + MIN (context, suffix_guess);
+ if (alloc_lines1 < buffered_prefix
+ || PTRDIFF_MAX / sizeof *linbuf1 <= alloc_lines1)
+ xalloc_die ();
+ linbuf1 = 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;
+}
+
+/* If 1 < k, then (2**k - prime_offset[k]) is the largest prime less
+ than 2**k. This table is derived from Chris K. Caldwell's list
+ <http://www.utm.edu/research/primes/lists/2small/>. */
+
+static unsigned char const prime_offset[] =
+{
+ 0, 0, 1, 1, 3, 1, 3, 1, 5, 3, 3, 9, 3, 1, 3, 19, 15, 1, 5, 1, 3, 9, 3,
+ 15, 3, 39, 5, 39, 57, 3, 35, 1, 5, 9, 41, 31, 5, 25, 45, 7, 87, 21,
+ 11, 57, 17, 55, 21, 115, 59, 81, 27, 129, 47, 111, 33, 55, 5, 13, 27,
+ 55, 93, 1, 57, 25
+};
+
+/* Verify that this host's size_t is not too wide for the above table. */
+
+verify (sizeof (size_t) * CHAR_BIT <= sizeof prime_offset);
+
+/* Given a vector of two file_data objects, read the file associated
+ with each one, and build the table of equivalence classes.
+ Return nonzero if either file appears to be a binary file.
+ If PRETEND_BINARY is nonzero, pretend they are binary regardless. */
+
+bool
+read_files (struct file_data filevec[], bool pretend_binary)
+{
+ int i;
+ bool skip_test = text | pretend_binary;
+ bool 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 = filevec[0].buffered;
+ }
+ if (appears_binary)
+ {
+ set_binary_mode (filevec[0].desc, O_BINARY);
+ set_binary_mode (filevec[1].desc, O_BINARY);
+ return true;
+ }
+
+ find_identical_ends (filevec);
+
+ equivs_alloc = filevec[0].alloc_lines + filevec[1].alloc_lines + 1;
+ if (PTRDIFF_MAX / sizeof *equivs <= equivs_alloc)
+ xalloc_die ();
+ equivs = xmalloc (equivs_alloc * sizeof *equivs);
+ /* Equivalence class 0 is permanently safe for lines that were not
+ hashed. Real equivalence classes start at 1. */
+ equivs_index = 1;
+
+ /* Allocate (one plus) a prime number of hash buckets. Use a prime
+ number between 1/3 and 2/3 of the value of equiv_allocs,
+ approximately. */
+ for (i = 9; (size_t) 1 << i < equivs_alloc / 3; i++)
+ continue;
+ nbuckets = ((size_t) 1 << i) - prime_offset[i];
+ if (PTRDIFF_MAX / sizeof *buckets <= nbuckets)
+ xalloc_die ();
+ buckets = zalloc ((nbuckets + 1) * sizeof *buckets);
+ buckets++;
+
+ for (i = 0; i < 2; i++)
+ find_and_hash_each_line (&filevec[i]);
+
+ filevec[0].equiv_max = filevec[1].equiv_max = equivs_index;
+
+ free (equivs);
+ free (buckets - 1);
+
+ return false;
+}
diff --git a/src/normal.c b/src/normal.c
new file mode 100644
index 0000000..852a767
--- /dev/null
+++ b/src/normal.c
@@ -0,0 +1,91 @@
+/* Normal-format output routines for GNU DIFF.
+
+ Copyright (C) 1988-1989, 1993, 1995, 1998, 2001, 2006, 2009-2013, 2015-2016
+ Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "diff.h"
+
+static void print_normal_hunk (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 (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 (struct change *hunk)
+{
+ lin first0, last0, first1, last1;
+ register lin i;
+
+ /* Determine range of line numbers involved in each file. */
+ enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
+ if (!changes)
+ return;
+
+ begin_output ();
+
+ /* Print out the line number header for this hunk */
+ set_color_context (LINE_NUMBER_CONTEXT);
+ print_number_range (',', &files[0], first0, last0);
+ fputc (change_letter[changes], outfile);
+ print_number_range (',', &files[1], first1, last1);
+ set_color_context (RESET_CONTEXT);
+ fputc ('\n', outfile);
+
+ /* Print the lines that the first file has. */
+ if (changes & OLD)
+ {
+ if (first0 <= last0)
+ set_color_context (DELETE_CONTEXT);
+ for (i = first0; i <= last0; i++)
+ {
+ print_1_line_nl ("<", &files[0].linbuf[i], true);
+ if (i == last0)
+ set_color_context (RESET_CONTEXT);
+ if (files[0].linbuf[i + 1][-1] == '\n')
+ putc ('\n', outfile);
+ }
+ }
+
+ if (changes == CHANGED)
+ fputs ("---\n", outfile);
+
+ /* Print the lines that the second file has. */
+ if (changes & NEW)
+ {
+ if (first1 <= last1)
+ set_color_context (ADD_CONTEXT);
+ for (i = first1; i <= last1; i++)
+ {
+ print_1_line_nl (">", &files[1].linbuf[i], true);
+ if (i == last1)
+ set_color_context (RESET_CONTEXT);
+ if (files[1].linbuf[i + 1][-1] == '\n')
+ putc ('\n', outfile);
+ }
+ }
+}
diff --git a/src/sdiff.c b/src/sdiff.c
new file mode 100644
index 0000000..22d6e5b
--- /dev/null
+++ b/src/sdiff.c
@@ -0,0 +1,1173 @@
+/* sdiff - side-by-side merge of file differences
+
+ Copyright (C) 1992-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
+ 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "system.h"
+#include "paths.h"
+
+#include <stdio.h>
+#include <unlocked-io.h>
+
+#include <c-stack.h>
+#include <dirname.h>
+#include <error.h>
+#include <exitfail.h>
+#include <file-type.h>
+#include <getopt.h>
+#include <progname.h>
+#include <system-quote.h>
+#include <version-etc.h>
+#include <xalloc.h>
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "sdiff"
+
+#define AUTHORS \
+ proper_name ("Thomas Lord")
+
+/* Size of chunks read from files which must be parsed into lines. */
+#define SDIFF_BUFSIZE ((size_t) 65536)
+
+static char const *editor_program = DEFAULT_EDITOR_PROGRAM;
+static char const **diffargv;
+
+static char * volatile tmpname;
+static FILE *tmp;
+
+#if HAVE_WORKING_FORK
+static pid_t volatile diffpid;
+#endif
+
+struct line_filter;
+
+static void catchsig (int);
+static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);
+static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);
+static void checksigs (void);
+static void diffarg (char const *);
+static void fatal (char const *) __attribute__((noreturn));
+static void perror_fatal (char const *) __attribute__((noreturn));
+static void trapsigs (void);
+static void untrapsig (int);
+
+static int const sigs[] = {
+#ifdef SIGHUP
+ SIGHUP,
+#endif
+#ifdef SIGQUIT
+ SIGQUIT,
+#endif
+#ifdef SIGTERM
+ SIGTERM,
+#endif
+#ifdef SIGXCPU
+ SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ SIGXFSZ,
+#endif
+#ifdef SIGPIPE
+ SIGPIPE,
+#endif
+ SIGINT
+};
+enum
+ {
+ NUM_SIGS = sizeof sigs / sizeof *sigs,
+ handler_index_of_SIGINT = NUM_SIGS - 1
+ };
+
+#if HAVE_SIGACTION
+ /* Prefer 'sigaction' if available, since 'signal' can lose signals. */
+ static struct sigaction initial_action[NUM_SIGS];
+# define initial_handler(i) (initial_action[i].sa_handler)
+ static void signal_handler (int, void (*) (int));
+#else
+ static void (*initial_action[NUM_SIGS]) ();
+# define initial_handler(i) (initial_action[i])
+# define signal_handler(sig, handler) signal (sig, handler)
+#endif
+
+static bool diraccess (char const *);
+static int temporary_file (void);
+
+/* Options: */
+
+/* Name of output file if -o specified. */
+static char const *output;
+
+/* Do not print common lines. */
+static bool suppress_common_lines;
+
+/* Value for the long option that does not have single-letter equivalents. */
+enum
+{
+ DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
+ HELP_OPTION,
+ STRIP_TRAILING_CR_OPTION,
+ TABSIZE_OPTION
+};
+
+static struct option const longopts[] =
+{
+ {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
+ {"expand-tabs", 0, 0, 't'},
+ {"help", 0, 0, HELP_OPTION},
+ {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
+ {"ignore-blank-lines", 0, 0, 'B'},
+ {"ignore-case", 0, 0, 'i'},
+ {"ignore-matching-lines", 1, 0, 'I'},
+ {"ignore-space-change", 0, 0, 'b'},
+ {"ignore-tab-expansion", 0, 0, 'E'},
+ {"ignore-trailing-space", 0, 0, 'Z'},
+ {"left-column", 0, 0, 'l'},
+ {"minimal", 0, 0, 'd'},
+ {"output", 1, 0, 'o'},
+ {"speed-large-files", 0, 0, 'H'},
+ {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
+ {"suppress-common-lines", 0, 0, 's'},
+ {"tabsize", 1, 0, TABSIZE_OPTION},
+ {"text", 0, 0, 'a'},
+ {"version", 0, 0, 'v'},
+ {"width", 1, 0, 'w'},
+ {0, 0, 0, 0}
+};
+
+static void try_help (char const *, char const *) __attribute__((noreturn));
+static void
+try_help (char const *reason_msgid, char const *operand)
+{
+ if (reason_msgid)
+ error (0, 0, _(reason_msgid), operand);
+ error (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
+ program_name);
+ abort ();
+}
+
+static void
+check_stdout (void)
+{
+ if (ferror (stdout))
+ fatal ("write failed");
+ else if (fclose (stdout) != 0)
+ perror_fatal (_("standard output"));
+}
+
+static char const * const option_help_msgid[] = {
+ N_("-o, --output=FILE operate interactively, sending output to FILE"),
+ "",
+ N_("-i, --ignore-case consider upper- and lower-case to be the same"),
+ N_("-E, --ignore-tab-expansion ignore changes due to tab expansion"),
+ N_("-Z, --ignore-trailing-space ignore white space at line end"),
+ N_("-b, --ignore-space-change ignore changes in the amount of white space"),
+ N_("-W, --ignore-all-space ignore all white space"),
+ N_("-B, --ignore-blank-lines ignore changes whose lines are all blank"),
+ N_("-I, --ignore-matching-lines=RE ignore changes all whose lines match RE"),
+ N_(" --strip-trailing-cr strip trailing carriage return on input"),
+ N_("-a, --text treat all files as text"),
+ "",
+ N_("-w, --width=NUM output at most NUM (default 130) print columns"),
+ N_("-l, --left-column output only the left column of common lines"),
+ N_("-s, --suppress-common-lines do not output common lines"),
+ "",
+ N_("-t, --expand-tabs expand tabs to spaces in output"),
+ N_(" --tabsize=NUM tab stops at every NUM (default 8) print columns"),
+ "",
+ N_("-d, --minimal try hard to find a smaller set of changes"),
+ N_("-H, --speed-large-files assume large files, many scattered small changes"),
+ N_(" --diff-program=PROGRAM use PROGRAM to compare files"),
+ "",
+ N_(" --help display this help and exit"),
+ N_("-v, --version output version information and exit"),
+ 0
+};
+
+static void
+usage (void)
+{
+ char const * const *p;
+
+ printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
+ printf ("%s\n\n",
+ _("Side-by-side merge of differences between FILE1 and FILE2."));
+
+ fputs (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+ for (p = option_help_msgid; *p; p++)
+ if (**p)
+ printf (" %s\n", _(*p));
+ else
+ putchar ('\n');
+ printf ("\n%s\n%s\n",
+ _("If a FILE is '-', read standard input."),
+ _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."));
+ emit_bug_reporting_address ();
+}
+
+/* Clean up after a signal or other failure. This function is
+ async-signal-safe. */
+static void
+cleanup (int signo __attribute__((unused)))
+{
+#if HAVE_WORKING_FORK
+ if (0 < diffpid)
+ kill (diffpid, SIGPIPE);
+#endif
+ if (tmpname)
+ unlink (tmpname);
+}
+
+static void exiterr (void) __attribute__((noreturn));
+static void
+exiterr (void)
+{
+ cleanup (0);
+ untrapsig (0);
+ checksigs ();
+ exit (EXIT_TROUBLE);
+}
+
+static void
+fatal (char const *msgid)
+{
+ error (0, 0, "%s", _(msgid));
+ exiterr ();
+}
+
+static void
+perror_fatal (char const *msg)
+{
+ int e = errno;
+ checksigs ();
+ error (0, e, "%s", msg);
+ exiterr ();
+}
+
+static void
+check_child_status (int werrno, int wstatus, int max_ok_status,
+ char const *subsidiary_program)
+{
+ int status = (! werrno && WIFEXITED (wstatus)
+ ? WEXITSTATUS (wstatus)
+ : INT_MAX);
+
+ if (max_ok_status < status)
+ {
+ error (0, werrno,
+ _(status == 126
+ ? "subsidiary program '%s' could not be invoked"
+ : status == 127
+ ? "subsidiary program '%s' not found"
+ : status == INT_MAX
+ ? "subsidiary program '%s' failed"
+ : "subsidiary program '%s' failed (exit status %d)"),
+ subsidiary_program, status);
+ exiterr ();
+ }
+}
+
+static FILE *
+ck_fopen (char const *fname, char const *type)
+{
+ FILE *r = fopen (fname, type);
+ if (! r)
+ perror_fatal (fname);
+ return r;
+}
+
+static void
+ck_fclose (FILE *f)
+{
+ if (fclose (f))
+ perror_fatal ("fclose");
+}
+
+static size_t
+ck_fread (char *buf, size_t size, FILE *f)
+{
+ size_t r = fread (buf, sizeof (char), size, f);
+ if (r == 0 && ferror (f))
+ perror_fatal (_("read failed"));
+ return r;
+}
+
+static void
+ck_fwrite (char const *buf, size_t size, FILE *f)
+{
+ if (fwrite (buf, sizeof (char), size, f) != size)
+ perror_fatal (_("write failed"));
+}
+
+static void
+ck_fflush (FILE *f)
+{
+ if (fflush (f) != 0)
+ perror_fatal (_("write failed"));
+}
+
+static char const *
+expand_name (char *name, bool is_dir, char const *other_name)
+{
+ if (STREQ (name, "-"))
+ fatal ("cannot interactively merge standard input");
+ if (! is_dir)
+ return name;
+ else
+ {
+ /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */
+ char const *base = last_component (other_name);
+ size_t namelen = strlen (name), baselen = base_len (base);
+ bool insert_slash = *last_component (name) && name[namelen - 1] != '/';
+ char *r = xmalloc (namelen + insert_slash + baselen + 1);
+ memcpy (r, name, namelen);
+ r[namelen] = '/';
+ memcpy (r + namelen + insert_slash, base, baselen);
+ r[namelen + insert_slash + baselen] = '\0';
+ return r;
+ }
+}
+
+struct line_filter {
+ FILE *infile;
+ char *bufpos;
+ char *buffer;
+ char *buflim;
+};
+
+static void
+lf_init (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 (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 (struct line_filter *lf, lin lines, FILE *outfile)
+{
+ char *start = lf->bufpos;
+
+ while (lines)
+ {
+ lf->bufpos = rawmemchr (lf->bufpos, '\n');
+ if (lf->bufpos == lf->buflim)
+ {
+ 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 (struct line_filter *lf, lin lines)
+{
+ while (lines)
+ {
+ lf->bufpos = rawmemchr (lf->bufpos, '\n');
+ if (lf->bufpos == lf->buflim)
+ {
+ 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 (struct line_filter *lf, char *buffer, size_t bufsize)
+{
+ for (;;)
+ {
+ char *start = lf->bufpos;
+ char *next = rawmemchr (start, '\n');
+ 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;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ int opt;
+ char const *prog;
+
+ exit_failure = EXIT_TROUBLE;
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+ c_stack_action (cleanup);
+
+ prog = getenv ("EDITOR");
+ if (prog)
+ editor_program = prog;
+
+ diffarg (DEFAULT_DIFF_PROGRAM);
+
+ /* parse command line args */
+ while ((opt = getopt_long (argc, argv, "abBdEHiI:lo:stvw:WZ", longopts, 0))
+ != -1)
+ {
+ switch (opt)
+ {
+ case 'a':
+ diffarg ("-a");
+ break;
+
+ case 'b':
+ diffarg ("-b");
+ break;
+
+ case 'B':
+ diffarg ("-B");
+ break;
+
+ case 'd':
+ diffarg ("-d");
+ break;
+
+ case 'E':
+ diffarg ("-E");
+ 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':
+ output = optarg;
+ break;
+
+ case 's':
+ suppress_common_lines = true;
+ break;
+
+ case 't':
+ diffarg ("-t");
+ break;
+
+ case 'v':
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
+ AUTHORS, (char *) NULL);
+ check_stdout ();
+ return EXIT_SUCCESS;
+
+ case 'w':
+ diffarg ("-W");
+ diffarg (optarg);
+ break;
+
+ case 'W':
+ diffarg ("-w");
+ break;
+
+ case 'Z':
+ diffarg ("-Z");
+ break;
+
+ case DIFF_PROGRAM_OPTION:
+ diffargv[0] = optarg;
+ break;
+
+ case HELP_OPTION:
+ usage ();
+ check_stdout ();
+ return EXIT_SUCCESS;
+
+ case STRIP_TRAILING_CR_OPTION:
+ diffarg ("--strip-trailing-cr");
+ break;
+
+ case TABSIZE_OPTION:
+ diffarg ("--tabsize");
+ diffarg (optarg);
+ break;
+
+ default:
+ try_help (0, 0);
+ }
+ }
+
+ if (argc - optind != 2)
+ {
+ if (argc - optind < 2)
+ try_help ("missing operand after '%s'", argv[argc - 1]);
+ else
+ try_help ("extra operand '%s'", argv[optind + 2]);
+ }
+
+ if (! output)
+ {
+ /* easy case: diff does everything for us */
+ if (suppress_common_lines)
+ diffarg ("--suppress-common-lines");
+ diffarg ("-y");
+ diffarg ("--");
+ diffarg (argv[optind]);
+ diffarg (argv[optind + 1]);
+ diffarg (0);
+ execvp (diffargv[0], (char **) diffargv);
+ perror_fatal (diffargv[0]);
+ }
+ else
+ {
+ char const *lname, *rname;
+ FILE *left, *right, *out, *diffout;
+ bool interact_ok;
+ struct line_filter lfilt;
+ struct line_filter rfilt;
+ struct line_filter diff_filt;
+ bool leftdir = diraccess (argv[optind]);
+ bool rightdir = diraccess (argv[optind + 1]);
+
+ if (leftdir & rightdir)
+ fatal ("both files to be compared are directories");
+
+ lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
+ left = ck_fopen (lname, "r");
+ rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
+ right = ck_fopen (rname, "r");
+ out = ck_fopen (output, "w");
+
+ diffarg ("--sdiff-merge-assist");
+ diffarg ("--");
+ diffarg (argv[optind]);
+ diffarg (argv[optind + 1]);
+ diffarg (0);
+
+ trapsigs ();
+
+#if ! HAVE_WORKING_FORK
+ {
+ char *command = system_quote_argv (SCI_SYSTEM, (char **) diffargv);
+ errno = 0;
+ diffout = popen (command, "r");
+ if (! diffout)
+ perror_fatal (command);
+ free (command);
+ }
+#else
+ {
+ int diff_fds[2];
+
+ if (pipe (diff_fds) != 0)
+ perror_fatal ("pipe");
+
+ diffpid = fork ();
+ if (diffpid < 0)
+ perror_fatal ("fork");
+ if (! diffpid)
+ {
+ /* Alter the child's SIGINT and SIGPIPE handlers;
+ this may munge the parent.
+ The child ignores SIGINT in case the user interrupts the editor.
+ The child does not ignore SIGPIPE, even if the parent does. */
+ if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
+ signal_handler (SIGINT, SIG_IGN);
+ signal_handler (SIGPIPE, SIG_DFL);
+ close (diff_fds[0]);
+ if (diff_fds[1] != STDOUT_FILENO)
+ {
+ dup2 (diff_fds[1], STDOUT_FILENO);
+ close (diff_fds[1]);
+ }
+
+ execvp (diffargv[0], (char **) diffargv);
+ _exit (errno == ENOENT ? 127 : 126);
+ }
+
+ close (diff_fds[1]);
+ diffout = fdopen (diff_fds[0], "r");
+ if (! diffout)
+ perror_fatal ("fdopen");
+ }
+#endif
+
+ lf_init (&diff_filt, diffout);
+ lf_init (&lfilt, left);
+ lf_init (&rfilt, right);
+
+ interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
+
+ ck_fclose (left);
+ ck_fclose (right);
+ ck_fclose (out);
+
+ {
+ int wstatus;
+ int werrno = 0;
+
+#if ! HAVE_WORKING_FORK
+ wstatus = pclose (diffout);
+ if (wstatus == -1)
+ werrno = errno;
+#else
+ ck_fclose (diffout);
+ while (waitpid (diffpid, &wstatus, 0) < 0)
+ if (errno == EINTR)
+ checksigs ();
+ else
+ perror_fatal ("waitpid");
+ diffpid = 0;
+#endif
+
+ if (tmpname)
+ {
+ unlink (tmpname);
+ tmpname = 0;
+ }
+
+ if (! interact_ok)
+ exiterr ();
+
+ check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]);
+ untrapsig (0);
+ checksigs ();
+ exit (WEXITSTATUS (wstatus));
+ }
+ }
+ return EXIT_SUCCESS; /* Fool '-Wall'. */
+}
+
+static void
+diffarg (char const *a)
+{
+ static size_t diffargs, diffarglim;
+
+ if (diffargs == diffarglim)
+ {
+ if (! diffarglim)
+ diffarglim = 16;
+ else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
+ xalloc_die ();
+ else
+ diffarglim *= 2;
+ diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
+ }
+ diffargv[diffargs++] = a;
+}
+
+/* Signal handling */
+
+static bool volatile ignore_SIGINT;
+static int volatile signal_received;
+static bool sigs_trapped;
+
+static void
+catchsig (int s)
+{
+#if ! HAVE_SIGACTION
+ signal (s, SIG_IGN);
+#endif
+ if (! (s == SIGINT && ignore_SIGINT))
+ signal_received = s;
+}
+
+#if HAVE_SIGACTION
+static struct sigaction catchaction;
+
+static void
+signal_handler (int sig, void (*handler) (int))
+{
+ catchaction.sa_handler = handler;
+ sigaction (sig, &catchaction, 0);
+}
+#endif
+
+static void
+trapsigs (void)
+{
+ int i;
+
+#if HAVE_SIGACTION
+ catchaction.sa_flags = SA_RESTART;
+ sigemptyset (&catchaction.sa_mask);
+ for (i = 0; i < NUM_SIGS; i++)
+ sigaddset (&catchaction.sa_mask, sigs[i]);
+#endif
+
+ for (i = 0; i < NUM_SIGS; i++)
+ {
+#if HAVE_SIGACTION
+ sigaction (sigs[i], 0, &initial_action[i]);
+#else
+ initial_action[i] = signal (sigs[i], SIG_IGN);
+#endif
+ if (initial_handler (i) != SIG_IGN)
+ signal_handler (sigs[i], catchsig);
+ }
+
+#ifdef SIGCHLD
+ /* System V fork+wait does not work if SIGCHLD is ignored. */
+ signal (SIGCHLD, SIG_DFL);
+#endif
+
+ sigs_trapped = true;
+}
+
+/* Untrap signal S, or all trapped signals if S is zero. */
+static void
+untrapsig (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 (void)
+{
+ int s = signal_received;
+ if (s)
+ {
+ cleanup (0);
+
+ /* 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 (EXIT_TROUBLE);
+ }
+}
+
+static void
+give_help (void)
+{
+ fprintf (stderr, "%s", _("\
+ed:\tEdit then use both versions, each decorated with a header.\n\
+eb:\tEdit then use both versions.\n\
+el or e1:\tEdit then use the left version.\n\
+er or e2:\tEdit then use the right version.\n\
+e:\tDiscard both versions then edit a new one.\n\
+l or 1:\tUse the left version.\n\
+r or 2:\tUse the right version.\n\
+s:\tSilently include common lines.\n\
+v:\tVerbosely include common lines.\n\
+q:\tQuit.\n\
+"));
+}
+
+static int
+skip_white (void)
+{
+ int c;
+ for (;;)
+ {
+ c = getchar ();
+ if (! isspace (c) || c == '\n')
+ break;
+ checksigs ();
+ }
+ if (ferror (stdin))
+ perror_fatal (_("read failed"));
+ return c;
+}
+
+static void
+flush_line (void)
+{
+ int c;
+ while ((c = getchar ()) != '\n' && c != EOF)
+ continue;
+ if (ferror (stdin))
+ perror_fatal (_("read failed"));
+}
+
+
+/* interpret an edit command */
+static bool
+edit (struct line_filter *left, char const *lname, lin lline, lin llen,
+ struct line_filter *right, char const *rname, lin rline, lin rlen,
+ FILE *outfile)
+{
+ for (;;)
+ {
+ int cmd0 IF_LINT (= 0);
+ int cmd1 IF_LINT (= 0);
+ bool gotcmd = false;
+
+ while (! gotcmd)
+ {
+ if (putchar ('%') != '%')
+ perror_fatal (_("write failed"));
+ ck_fflush (stdout);
+
+ cmd0 = skip_white ();
+ switch (cmd0)
+ {
+ case '1': case '2': case 'l': case 'r':
+ case 's': case 'v': case 'q':
+ if (skip_white () != '\n')
+ {
+ give_help ();
+ flush_line ();
+ continue;
+ }
+ gotcmd = true;
+ break;
+
+ case 'e':
+ cmd1 = skip_white ();
+ switch (cmd1)
+ {
+ case '1': case '2': case 'b': case 'd': case 'l': case 'r':
+ if (skip_white () != '\n')
+ {
+ give_help ();
+ flush_line ();
+ continue;
+ }
+ gotcmd = true;
+ break;
+ case '\n':
+ gotcmd = true;
+ break;
+ default:
+ give_help ();
+ flush_line ();
+ continue;
+ }
+ break;
+
+ case EOF:
+ if (feof (stdin))
+ {
+ gotcmd = true;
+ cmd0 = 'q';
+ break;
+ }
+ /* Fall through. */
+ default:
+ flush_line ();
+ /* Fall through. */
+ case '\n':
+ give_help ();
+ continue;
+ }
+ }
+
+ switch (cmd0)
+ {
+ case '1': case 'l':
+ lf_copy (left, llen, outfile);
+ lf_skip (right, rlen);
+ return true;
+ case '2': case 'r':
+ lf_copy (right, rlen, outfile);
+ lf_skip (left, llen);
+ return true;
+ case 's':
+ suppress_common_lines = true;
+ break;
+ case 'v':
+ suppress_common_lines = false;
+ break;
+ case 'q':
+ return false;
+ case 'e':
+ {
+ int fd;
+
+ if (tmpname)
+ tmp = fopen (tmpname, "w");
+ else
+ {
+ if ((fd = temporary_file ()) < 0)
+ perror_fatal ("mkstemp");
+ tmp = fdopen (fd, "w");
+ }
+
+ if (! tmp)
+ perror_fatal (tmpname);
+
+ switch (cmd1)
+ {
+ case 'd':
+ if (llen)
+ {
+ if (llen == 1)
+ fprintf (tmp, "--- %s %ld\n", lname, (long int) lline);
+ else
+ fprintf (tmp, "--- %s %ld,%ld\n", lname,
+ (long int) lline,
+ (long int) (lline + llen - 1));
+ }
+ /* Fall through. */
+ case '1': case 'b': case 'l':
+ lf_copy (left, llen, tmp);
+ break;
+
+ default:
+ lf_skip (left, llen);
+ break;
+ }
+
+ switch (cmd1)
+ {
+ case 'd':
+ if (rlen)
+ {
+ if (rlen == 1)
+ fprintf (tmp, "+++ %s %ld\n", rname, (long int) rline);
+ else
+ fprintf (tmp, "+++ %s %ld,%ld\n", rname,
+ (long int) rline,
+ (long int) (rline + rlen - 1));
+ }
+ /* Fall through. */
+ case '2': case 'b': case 'r':
+ lf_copy (right, rlen, tmp);
+ break;
+
+ default:
+ lf_skip (right, rlen);
+ break;
+ }
+
+ ck_fclose (tmp);
+
+ {
+ int wstatus;
+ int werrno = 0;
+ char const *argv[3];
+
+ ignore_SIGINT = true;
+ checksigs ();
+ argv[0] = editor_program;
+ argv[1] = tmpname;
+ argv[2] = 0;
+
+ {
+#if ! HAVE_WORKING_FORK
+ char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
+ wstatus = system (command);
+ if (wstatus == -1)
+ werrno = errno;
+ free (command);
+#else
+ pid_t pid;
+
+ pid = fork ();
+ if (pid == 0)
+ {
+ execvp (editor_program, (char **) argv);
+ _exit (errno == ENOENT ? 127 : 126);
+ }
+
+ if (pid < 0)
+ perror_fatal ("fork");
+
+ while (waitpid (pid, &wstatus, 0) < 0)
+ if (errno == EINTR)
+ checksigs ();
+ else
+ perror_fatal ("waitpid");
+#endif
+ }
+
+ ignore_SIGINT = false;
+ check_child_status (werrno, wstatus, EXIT_SUCCESS,
+ editor_program);
+ }
+
+ {
+ char buf[SDIFF_BUFSIZE];
+ size_t size;
+ tmp = ck_fopen (tmpname, "r");
+ while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
+ {
+ checksigs ();
+ ck_fwrite (buf, size, outfile);
+ }
+ ck_fclose (tmp);
+ }
+ return true;
+ }
+ default:
+ give_help ();
+ break;
+ }
+ }
+}
+
+/* Alternately reveal bursts of diff output and handle user commands. */
+static bool
+interact (struct line_filter *diff,
+ struct line_filter *left, char const *lname,
+ struct line_filter *right, char const *rname,
+ FILE *outfile)
+{
+ lin lline = 1, rline = 1;
+
+ for (;;)
+ {
+ char diff_help[256];
+ int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
+
+ if (snarfed <= 0)
+ return snarfed != 0;
+
+ checksigs ();
+
+ if (diff_help[0] == ' ')
+ puts (diff_help + 1);
+ else
+ {
+ char *numend;
+ uintmax_t val;
+ lin llen, rlen, lenmax;
+ errno = 0;
+ val = strtoumax (diff_help + 1, &numend, 10);
+ if (LIN_MAX < val || errno || *numend != ',')
+ fatal (diff_help);
+ llen = val;
+ val = strtoumax (numend + 1, &numend, 10);
+ if (LIN_MAX < val || errno || *numend)
+ fatal (diff_help);
+ rlen = val;
+
+ lenmax = MAX (llen, rlen);
+
+ switch (diff_help[0])
+ {
+ case 'i':
+ if (suppress_common_lines)
+ lf_skip (diff, lenmax);
+ else
+ lf_copy (diff, lenmax, stdout);
+
+ lf_copy (left, llen, outfile);
+ lf_skip (right, rlen);
+ break;
+
+ case 'c':
+ lf_copy (diff, lenmax, stdout);
+ if (! edit (left, lname, lline, llen,
+ right, rname, rline, rlen,
+ outfile))
+ return false;
+ break;
+
+ default:
+ fatal (diff_help);
+ }
+
+ lline += llen;
+ rline += rlen;
+ }
+ }
+}
+
+/* Return true if DIR is an existing directory. */
+static bool
+diraccess (char const *dir)
+{
+ struct stat buf;
+ return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
+}
+
+#ifndef P_tmpdir
+# define P_tmpdir "/tmp"
+#endif
+#ifndef TMPDIR_ENV
+# define TMPDIR_ENV "TMPDIR"
+#endif
+
+/* Open a temporary file and return its file descriptor. Put into
+ tmpname the address of a newly allocated buffer that holds the
+ file's name. Use the prefix "sdiff". */
+static int
+temporary_file (void)
+{
+ char const *tmpdir = getenv (TMPDIR_ENV);
+ char const *dir = tmpdir ? tmpdir : P_tmpdir;
+ char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
+ int fd;
+ sprintf (buf, "%s/sdiffXXXXXX", dir);
+ fd = mkstemp (buf);
+ if (0 <= fd)
+ tmpname = buf;
+ return fd;
+}
diff --git a/src/side.c b/src/side.c
new file mode 100644
index 0000000..2276385
--- /dev/null
+++ b/src/side.c
@@ -0,0 +1,335 @@
+/* sdiff-format output routines for GNU DIFF.
+
+ Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2016 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 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 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"
+
+#include <wchar.h>
+
+static void print_sdiff_common_lines (lin, lin);
+static void print_sdiff_hunk (struct change *);
+
+/* Next line number to be printed in the two input files. */
+static lin next0, next1;
+
+/* Print the edit-script SCRIPT as a sdiff style output. */
+
+void
+print_sdiff_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 size_t
+tab_from_to (size_t from, size_t to)
+{
+ FILE *out = outfile;
+ size_t tab;
+ size_t tab_size = tabsize;
+
+ if (!expand_tabs)
+ for (tab = from + tab_size - from % tab_size; tab <= to; tab += tab_size)
+ {
+ 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. Return the
+ last column written (not the number of chars). */
+
+static size_t
+print_half_line (char const *const *line, size_t indent, size_t out_bound)
+{
+ FILE *out = outfile;
+ register size_t in_position = 0;
+ register size_t out_position = 0;
+ register char const *text_pointer = line[0];
+ register char const *text_limit = line[1];
+ mbstate_t mbstate = { 0 };
+
+ while (text_pointer < text_limit)
+ {
+ char const *tp0 = text_pointer;
+ register char c = *text_pointer++;
+
+ switch (c)
+ {
+ case '\t':
+ {
+ size_t spaces = tabsize - in_position % tabsize;
+ if (in_position == out_position)
+ {
+ size_t tabstop = out_position + spaces;
+ if (expand_tabs)
+ {
+ 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;
+
+ default:
+ {
+ wchar_t wc;
+ size_t bytes = mbrtowc (&wc, tp0, text_limit - tp0, &mbstate);
+
+ if (0 < bytes && bytes < (size_t) -2)
+ {
+ int width = wcwidth (wc);
+ if (0 < width)
+ in_position += width;
+ if (in_position <= out_bound)
+ {
+ out_position = in_position;
+ fwrite (tp0, 1, bytes, stdout);
+ }
+ text_pointer = tp0 + bytes;
+ break;
+ }
+ }
+ /* Fall through. */
+ case '\f':
+ case '\v':
+ if (in_position < out_bound)
+ putc (c, out);
+ break;
+
+ case ' ': case '!': case '"': case '#': case '%':
+ case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case ':': case ';': case '<': case '=': case '>':
+ case '?':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case '[': case '\\': case ']': case '^': case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z': case '{': case '|': case '}': case '~':
+ /* These characters are printable ASCII characters. */
+ 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 (char const *const *left, char sep,
+ char const *const *right)
+{
+ FILE *out = outfile;
+ size_t hw = sdiff_half_width;
+ size_t c2o = sdiff_column2_offset;
+ size_t col = 0;
+ bool put_newline = false;
+ bool color_to_reset = false;
+
+ if (sep == '<')
+ {
+ set_color_context (DELETE_CONTEXT);
+ color_to_reset = true;
+ }
+ else if (sep == '>')
+ {
+ set_color_context (ADD_CONTEXT);
+ color_to_reset = true;
+ }
+
+ if (left)
+ {
+ put_newline |= left[1][-1] == '\n';
+ 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)
+ {
+ put_newline |= right[1][-1] == '\n';
+ if (**right != '\n')
+ {
+ col = tab_from_to (col, c2o);
+ print_half_line (right, col, hw);
+ }
+ }
+
+ if (put_newline)
+ putc ('\n', out);
+
+ if (color_to_reset)
+ set_color_context (RESET_CONTEXT);
+}
+
+/* Print lines common to both files in side-by-side format. */
+static void
+print_sdiff_common_lines (lin limit0, lin limit1)
+{
+ lin i0 = next0, i1 = next1;
+
+ if (!suppress_common_lines && (i0 != limit0 || i1 != limit1))
+ {
+ if (sdiff_merge_assist)
+ {
+ long int len0 = limit0 - i0;
+ long int len1 = limit1 - i1;
+ fprintf (outfile, "i%ld,%ld\n", len0, len1);
+ }
+
+ if (!left_column)
+ {
+ 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 (struct change *hunk)
+{
+ lin first0, last0, first1, last1;
+ register lin i, j;
+
+ /* Determine range of line numbers involved in each file. */
+ enum changes changes =
+ analyze_hunk (hunk, &first0, &last0, &first1, &last1);
+ if (!changes)
+ return;
+
+ /* Print out lines up to this change. */
+ print_sdiff_common_lines (first0, first1);
+
+ if (sdiff_merge_assist)
+ {
+ long int len0 = last0 - first0 + 1;
+ long int len1 = last1 - first1 + 1;
+ fprintf (outfile, "c%ld,%ld\n", len0, len1);
+ }
+
+ /* Print "xxx | xxx " lines. */
+ if (changes == CHANGED)
+ {
+ for (i = first0, j = first1; i <= last0 && j <= last1; i++, j++)
+ print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
+ changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0);
+ next0 = first0 = i;
+ next1 = first1 = j;
+ }
+
+ /* Print " > xxx " lines. */
+ if (changes & NEW)
+ {
+ for (j = first1; j <= last1; ++j)
+ print_1sdiff_line (0, '>', &files[1].linbuf[j]);
+ next1 = j;
+ }
+
+ /* Print "xxx < " lines. */
+ if (changes & OLD)
+ {
+ for (i = first0; i <= last0; ++i)
+ print_1sdiff_line (&files[0].linbuf[i], '<', 0);
+ next0 = i;
+ }
+}
diff --git a/src/system.h b/src/system.h
new file mode 100644
index 0000000..be1c0bd
--- /dev/null
+++ b/src/system.h
@@ -0,0 +1,214 @@
+/* System dependent declarations.
+
+ Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
+ 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Use this to suppress gcc's "...may be used before initialized" warnings. */
+#ifdef lint
+# define IF_LINT(Code) Code
+#else
+# define IF_LINT(Code) /* empty */
+#endif
+
+/* Define '__attribute__' and 'volatile' first
+ so that they're used consistently in all system includes. */
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) || __STRICT_ANSI__
+# define __attribute__(x)
+#endif
+
+#include <verify.h>
+
+#include <sys/types.h>
+
+#include <sys/stat.h>
+#include "stat-macros.h"
+
+#ifndef STAT_BLOCKSIZE
+# if HAVE_STRUCT_STAT_ST_BLKSIZE
+# define STAT_BLOCKSIZE(s) ((s).st_blksize)
+# else
+# define STAT_BLOCKSIZE(s) (8 * 1024)
+# endif
+#endif
+
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <time.h>
+
+#include <sys/wait.h>
+
+#include <dirent.h>
+#ifndef _D_EXACT_NAMLEN
+# define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
+#endif
+
+#include <stdlib.h>
+#define EXIT_TROUBLE 2
+
+#include <limits.h>
+#include <locale.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+#include <string.h>
+#if ! HAVE_STRCASECOLL
+# if HAVE_STRICOLL || defined stricoll
+# define strcasecoll(a, b) stricoll (a, b)
+# else
+# define strcasecoll(a, b) strcasecmp (a, b) /* best we can do */
+# endif
+#endif
+#if ! (HAVE_STRCASECMP || defined strcasecmp)
+int strcasecmp (char const *, char const *);
+#endif
+
+#include <gettext.h>
+#if ! ENABLE_NLS
+# undef textdomain
+# define textdomain(Domainname) /* empty */
+# undef bindtextdomain
+# define bindtextdomain(Domainname, Dirname) /* empty */
+#endif
+
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+#include <ctype.h>
+
+/* ISDIGIT differs from isdigit, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char.
+ - It's guaranteed to evaluate its argument exactly once.
+ - It's typically faster.
+ POSIX 1003.1-2001 says that only '0' through '9' are digits.
+ Prefer ISDIGIT to isdigit unless it's important to use the locale's
+ definition of 'digit' even when the host does not conform to POSIX. */
+#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
+
+#include <errno.h>
+
+#include <signal.h>
+#if !defined SIGCHLD && defined SIGCLD
+# define SIGCHLD SIGCLD
+#endif
+
+#undef MIN
+#undef MAX
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+
+#include <stdbool.h>
+#include <intprops.h>
+#include "propername.h"
+#include "version.h"
+
+/* Type used for fast comparison of several bytes at a time.
+ This used to be uintmax_t, but changing it to size_t
+ made plain 'cmp' 90% faster (GCC 4.8.1, x86). */
+
+#ifndef word
+# define word size_t
+#endif
+
+/* The integer type of a line number. Since files are read into main
+ memory, ptrdiff_t should be wide enough. */
+
+typedef ptrdiff_t lin;
+#define LIN_MAX PTRDIFF_MAX
+verify (TYPE_SIGNED (lin));
+verify (sizeof (ptrdiff_t) <= sizeof (lin));
+verify (sizeof (lin) <= sizeof (long int));
+
+/* Limit so that 2 * CONTEXT + 1 does not overflow. */
+
+#define CONTEXT_MAX ((LIN_MAX - 1) / 2)
+
+
+/* This section contains POSIX-compliant defaults for macros
+ that are meant to be overridden by hand in config.h as needed. */
+
+#ifndef file_name_cmp
+# define file_name_cmp strcmp
+#endif
+
+#ifndef initialize_main
+# define initialize_main(argcp, argvp)
+#endif
+
+#ifndef NULL_DEVICE
+# define NULL_DEVICE "/dev/null"
+#endif
+
+/* Do struct stat *S, *T describe the same special file? */
+#ifndef same_special_file
+# if HAVE_STRUCT_STAT_ST_RDEV && defined S_ISBLK && defined S_ISCHR
+# define same_special_file(s, t) \
+ (((S_ISBLK ((s)->st_mode) && S_ISBLK ((t)->st_mode)) \
+ || (S_ISCHR ((s)->st_mode) && S_ISCHR ((t)->st_mode))) \
+ && (s)->st_rdev == (t)->st_rdev)
+# else
+# define same_special_file(s, t) 0
+# endif
+#endif
+
+/* Do struct stat *S, *T describe the same file? Answer -1 if unknown. */
+#ifndef same_file
+# define same_file(s, t) \
+ ((((s)->st_ino == (t)->st_ino) && ((s)->st_dev == (t)->st_dev)) \
+ || same_special_file (s, t))
+#endif
+
+/* Do struct stat *S, *T have the same file attributes?
+
+ POSIX says that two files are identical if st_ino and st_dev are
+ the same, but many file systems incorrectly assign the same (device,
+ inode) pair to two distinct files, including:
+
+ - GNU/Linux NFS servers that export all local file systems as a
+ single NFS file system, if a local device number (st_dev) exceeds
+ 255, or if a local inode number (st_ino) exceeds 16777215.
+
+ - Network Appliance NFS servers in snapshot directories; see
+ Network Appliance bug #195.
+
+ - ClearCase MVFS; see bug id ATRia04618.
+
+ Check whether two files that purport to be the same have the same
+ attributes, to work around instances of this common bug. Do not
+ inspect all attributes, only attributes useful in checking for this
+ bug.
+
+ It's possible for two distinct files on a buggy file system to have
+ the same attributes, but it's not worth slowing down all
+ implementations (or complicating the configuration) to cater to
+ these rare cases in buggy implementations. */
+
+#ifndef same_file_attributes
+# define same_file_attributes(s, t) \
+ ((s)->st_mode == (t)->st_mode \
+ && (s)->st_nlink == (t)->st_nlink \
+ && (s)->st_uid == (t)->st_uid \
+ && (s)->st_gid == (t)->st_gid \
+ && (s)->st_size == (t)->st_size \
+ && (s)->st_mtime == (t)->st_mtime \
+ && (s)->st_ctime == (t)->st_ctime)
+#endif
+
+#define STREQ(a, b) (strcmp (a, b) == 0)
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..76872cb
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,1577 @@
+/* Support routines for GNU DIFF.
+
+ Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
+ 2015-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU DIFF.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "diff.h"
+#include "argmatch.h"
+#include <dirname.h>
+#include <error.h>
+#include <system-quote.h>
+#include <xalloc.h>
+#include "xvasprintf.h"
+#include <signal.h>
+
+/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
+ present. */
+#ifndef SA_NOCLDSTOP
+# define SA_NOCLDSTOP 0
+# define sigprocmask(How, Set, Oset) /* empty */
+# define sigset_t int
+# if ! HAVE_SIGINTERRUPT
+# define siginterrupt(sig, flag) /* empty */
+# endif
+#endif
+
+#ifndef SA_RESTART
+# define SA_RESTART 0
+#endif
+
+char const pr_program[] = PR_PROGRAM;
+
+/* 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 args[1]; /* Format + 4 args, each '\0' terminated, concatenated. */
+};
+
+/* Head of the chain of queues messages. */
+
+static struct msg *msg_chain;
+
+/* Tail of the chain of queues messages. */
+
+static struct msg **msg_chain_end = &msg_chain;
+
+/* Use when a system call returns non-zero status.
+ NAME should normally be the file name. */
+
+void
+perror_with_name (char const *name)
+{
+ error (0, errno, "%s", name);
+}
+
+/* Use when a system call returns non-zero status and that is fatal. */
+
+void
+pfatal_with_name (char const *name)
+{
+ int e = errno;
+ print_message_queue ();
+ error (EXIT_TROUBLE, e, "%s", name);
+ abort ();
+}
+
+/* Print an error message containing MSGID, then exit. */
+
+void
+fatal (char const *msgid)
+{
+ print_message_queue ();
+ error (EXIT_TROUBLE, 0, "%s", _(msgid));
+ abort ();
+}
+
+/* Like printf, except if -l in effect then save the message and print later.
+ This is used for things like "Only in ...". */
+
+void
+message (char const *format_msgid, char const *arg1, char const *arg2)
+{
+ message5 (format_msgid, arg1, arg2, 0, 0);
+}
+
+void
+message5 (char const *format_msgid, char const *arg1, char const *arg2,
+ char const *arg3, char const *arg4)
+{
+ if (paginate)
+ {
+ char *p;
+ char const *arg[5];
+ int i;
+ size_t size[5];
+ size_t total_size = offsetof (struct msg, args);
+ struct msg *new;
+
+ arg[0] = format_msgid;
+ arg[1] = arg1;
+ arg[2] = arg2;
+ arg[3] = arg3 ? arg3 : "";
+ arg[4] = arg4 ? arg4 : "";
+
+ for (i = 0; i < 5; i++)
+ total_size += size[i] = strlen (arg[i]) + 1;
+
+ new = xmalloc (total_size);
+
+ for (i = 0, p = new->args; i < 5; p += size[i++])
+ memcpy (p, arg[i], size[i]);
+
+ *msg_chain_end = new;
+ new->next = 0;
+ msg_chain_end = &new->next;
+ }
+ else
+ {
+ if (sdiff_merge_assist)
+ putchar (' ');
+ printf (_(format_msgid), arg1, arg2, arg3, arg4);
+ }
+}
+
+/* Output all the messages that were saved up by calls to 'message'. */
+
+void
+print_message_queue (void)
+{
+ char const *arg[5];
+ int i;
+ struct msg *m = msg_chain;
+
+ while (m)
+ {
+ struct msg *next = m->next;
+ arg[0] = m->args;
+ for (i = 0; i < 4; i++)
+ arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
+ printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
+ free (m);
+ m = next;
+ }
+}
+
+/* The set of signals that are caught. */
+
+static sigset_t caught_signals;
+
+/* If nonzero, the value of the pending fatal signal. */
+
+static sig_atomic_t volatile interrupt_signal;
+
+/* A count of the number of pending stop signals that have been received. */
+
+static sig_atomic_t volatile stop_signal_count;
+
+/* An ordinary signal was received; arrange for the program to exit. */
+
+static void
+sighandler (int sig)
+{
+ if (! SA_NOCLDSTOP)
+ signal (sig, SIG_IGN);
+ if (! interrupt_signal)
+ interrupt_signal = sig;
+}
+
+/* A SIGTSTP was received; arrange for the program to suspend itself. */
+
+static void
+stophandler (int sig)
+{
+ if (! SA_NOCLDSTOP)
+ signal (sig, stophandler);
+ if (! interrupt_signal)
+ stop_signal_count++;
+}
+/* Process any pending signals. If signals are caught, this function
+ should be called periodically. Ideally there should never be an
+ unbounded amount of time when signals are not being processed.
+ Signal handling can restore the default colors, so callers must
+ immediately change colors after invoking this function. */
+
+static void
+process_signals (void)
+{
+ while (interrupt_signal || stop_signal_count)
+ {
+ int sig;
+ int stops;
+ sigset_t oldset;
+
+ set_color_context (RESET_CONTEXT);
+ fflush (stdout);
+
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+
+ /* Reload interrupt_signal and stop_signal_count, in case a new
+ signal was handled before sigprocmask took effect. */
+ sig = interrupt_signal;
+ stops = stop_signal_count;
+
+ /* SIGTSTP is special, since the application can receive that signal
+ more than once. In this case, don't set the signal handler to the
+ default. Instead, just raise the uncatchable SIGSTOP. */
+ if (stops)
+ {
+ stop_signal_count = stops - 1;
+ sig = SIGSTOP;
+ }
+ else
+ signal (sig, SIG_DFL);
+
+ /* Exit or suspend the program. */
+ raise (sig);
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+
+ /* If execution reaches here, then the program has been
+ continued (after being suspended). */
+ }
+}
+
+static void
+install_signal_handlers (void)
+{
+ /* The signals that are trapped, and the number of such signals. */
+ static int const sig[] =
+ {
+ /* This one is handled specially. */
+ SIGTSTP,
+
+ /* The usual suspects. */
+ SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
+#ifdef SIGPOLL
+ SIGPOLL,
+#endif
+#ifdef SIGPROF
+ SIGPROF,
+#endif
+#ifdef SIGVTALRM
+ SIGVTALRM,
+#endif
+#ifdef SIGXCPU
+ SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ SIGXFSZ,
+#endif
+ };
+ enum { nsigs = sizeof (sig) / sizeof *(sig) };
+
+#if ! SA_NOCLDSTOP
+ bool caught_sig[nsigs];
+#endif
+ {
+ int j;
+#if SA_NOCLDSTOP
+ struct sigaction act;
+
+ sigemptyset (&caught_signals);
+ for (j = 0; j < nsigs; j++)
+ {
+ sigaction (sig[j], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[j]);
+ }
+
+ act.sa_mask = caught_signals;
+ act.sa_flags = SA_RESTART;
+
+ for (j = 0; j < nsigs; j++)
+ if (sigismember (&caught_signals, sig[j]))
+ {
+ act.sa_handler = sig[j] == SIGTSTP ? stophandler : sighandler;
+ sigaction (sig[j], &act, NULL);
+ }
+#else
+ for (j = 0; j < nsigs; j++)
+ {
+ caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
+ if (caught_sig[j])
+ {
+ signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
+ siginterrupt (sig[j], 0);
+ }
+ }
+#endif
+ }
+}
+
+static char const *current_name0;
+static char const *current_name1;
+static bool currently_recursive;
+static bool colors_enabled;
+
+static struct color_ext_type *color_ext_list = NULL;
+
+struct bin_str
+ {
+ size_t len; /* Number of bytes */
+ const char *string; /* Pointer to the same */
+ };
+
+struct color_ext_type
+ {
+ struct bin_str ext; /* The extension we're looking for */
+ struct bin_str seq; /* The sequence to output when we do */
+ struct color_ext_type *next; /* Next in list */
+ };
+
+/* Parse a string as part of the --palette argument; this may involve
+ decoding all kinds of escape characters. If equals_end is set an
+ unescaped equal sign ends the string, otherwise only a : or \0
+ does. Set *OUTPUT_COUNT to the number of bytes output. Return
+ true if successful.
+
+ The resulting string is *not* null-terminated, but may contain
+ embedded nulls.
+
+ Note that both dest and src are char **; on return they point to
+ the first free byte after the array and the character that ended
+ the input string, respectively. */
+
+static bool
+get_funky_string (char **dest, const char **src, bool equals_end,
+ size_t *output_count)
+{
+ char num; /* For numerical codes */
+ size_t count; /* Something to count with */
+ enum {
+ ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
+ } state;
+ const char *p;
+ char *q;
+
+ p = *src; /* We don't want to double-indirect */
+ q = *dest; /* the whole darn time. */
+
+ count = 0; /* No characters counted in yet. */
+ num = 0;
+
+ state = ST_GND; /* Start in ground state. */
+ while (state < ST_END)
+ {
+ switch (state)
+ {
+ case ST_GND: /* Ground state (no escapes) */
+ switch (*p)
+ {
+ case ':':
+ case '\0':
+ state = ST_END; /* End of string */
+ break;
+ case '\\':
+ state = ST_BACKSLASH; /* Backslash scape sequence */
+ ++p;
+ break;
+ case '^':
+ state = ST_CARET; /* Caret escape */
+ ++p;
+ break;
+ case '=':
+ if (equals_end)
+ {
+ state = ST_END; /* End */
+ break;
+ }
+ /* else fall through */
+ default:
+ *(q++) = *(p++);
+ ++count;
+ break;
+ }
+ break;
+
+ case ST_BACKSLASH: /* Backslash escaped character */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state = ST_OCTAL; /* Octal sequence */
+ num = *p - '0';
+ break;
+ case 'x':
+ case 'X':
+ state = ST_HEX; /* Hex sequence */
+ num = 0;
+ break;
+ case 'a': /* Bell */
+ num = '\a';
+ break;
+ case 'b': /* Backspace */
+ num = '\b';
+ break;
+ case 'e': /* Escape */
+ num = 27;
+ break;
+ case 'f': /* Form feed */
+ num = '\f';
+ break;
+ case 'n': /* Newline */
+ num = '\n';
+ break;
+ case 'r': /* Carriage return */
+ num = '\r';
+ break;
+ case 't': /* Tab */
+ num = '\t';
+ break;
+ case 'v': /* Vtab */
+ num = '\v';
+ break;
+ case '?': /* Delete */
+ num = 127;
+ break;
+ case '_': /* Space */
+ num = ' ';
+ break;
+ case '\0': /* End of string */
+ state = ST_ERROR; /* Error! */
+ break;
+ default: /* Escaped character like \ ^ : = */
+ num = *p;
+ break;
+ }
+ if (state == ST_BACKSLASH)
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ ++p;
+ break;
+
+ case ST_OCTAL: /* Octal sequence */
+ if (*p < '0' || *p > '7')
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ else
+ num = (num << 3) + (*(p++) - '0');
+ break;
+
+ case ST_HEX: /* Hex sequence */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ num = (num << 4) + (*(p++) - '0');
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ num = (num << 4) + (*(p++) - 'a') + 10;
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ num = (num << 4) + (*(p++) - 'A') + 10;
+ break;
+ default:
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ break;
+ }
+ break;
+
+ case ST_CARET: /* Caret escape */
+ state = ST_GND; /* Should be the next state... */
+ if (*p >= '@' && *p <= '~')
+ {
+ *(q++) = *(p++) & 037;
+ ++count;
+ }
+ else if (*p == '?')
+ {
+ *(q++) = 127;
+ ++count;
+ }
+ else
+ state = ST_ERROR;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ *dest = q;
+ *src = p;
+ *output_count = count;
+
+ return state != ST_ERROR;
+}
+
+enum parse_state
+ {
+ PS_START = 1,
+ PS_2,
+ PS_3,
+ PS_4,
+ PS_DONE,
+ PS_FAIL
+ };
+
+#define LEN_STR_PAIR(s) sizeof (s) - 1, s
+
+static struct bin_str color_indicator[] =
+ {
+ { LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
+ { LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
+ { 0, NULL }, /* ec: End color (replaces lc+rs+rc) */
+ { LEN_STR_PAIR ("0") }, /* rs: Reset to ordinary colors */
+ { LEN_STR_PAIR ("1") }, /* hd: Header */
+ { LEN_STR_PAIR ("32") }, /* ad: Add line */
+ { LEN_STR_PAIR ("31") }, /* de: Delete line */
+ { LEN_STR_PAIR ("36") }, /* ln: Line number */
+ };
+
+static const char *const indicator_name[] =
+ {
+ "lc", "rc", "ec", "rs", "hd", "ad", "de", "ln", NULL
+ };
+ARGMATCH_VERIFY (indicator_name, color_indicator);
+
+static char const *color_palette;
+
+void
+set_color_palette (char const *palette)
+{
+ color_palette = palette;
+}
+
+static void
+parse_diff_color (void)
+{
+ char *color_buf;
+ const char *p; /* Pointer to character being parsed */
+ char *buf; /* color_buf buffer pointer */
+ int ind_no; /* Indicator number */
+ char label[3]; /* Indicator label */
+ struct color_ext_type *ext; /* Extension we are working on */
+
+ if ((p = color_palette) == NULL || *p == '\0')
+ return;
+
+ ext = NULL;
+ strcpy (label, "??");
+
+ /* This is an overly conservative estimate, but any possible
+ --palette string will *not* generate a color_buf longer than
+ itself, so it is a safe way of allocating a buffer in
+ advance. */
+ buf = color_buf = xstrdup (p);
+
+ enum parse_state state = PS_START;
+ while (true)
+ {
+ switch (state)
+ {
+ case PS_START: /* First label character */
+ switch (*p)
+ {
+ case ':':
+ ++p;
+ break;
+
+ case '*':
+ /* Allocate new extension block and add to head of
+ linked list (this way a later definition will
+ override an earlier one, which can be useful for
+ having terminal-specific defs override global). */
+
+ ext = xmalloc (sizeof *ext);
+ ext->next = color_ext_list;
+ color_ext_list = ext;
+
+ ++p;
+ ext->ext.string = buf;
+
+ state = (get_funky_string (&buf, &p, true, &ext->ext.len)
+ ? PS_4 : PS_FAIL);
+ break;
+
+ case '\0':
+ state = PS_DONE; /* Done! */
+ goto done;
+
+ default: /* Assume it is file type label */
+ label[0] = *(p++);
+ state = PS_2;
+ break;
+ }
+ break;
+
+ case PS_2: /* Second label character */
+ if (*p)
+ {
+ label[1] = *(p++);
+ state = PS_3;
+ }
+ else
+ state = PS_FAIL; /* Error */
+ break;
+
+ case PS_3: /* Equal sign after indicator label */
+ state = PS_FAIL; /* Assume failure... */
+ if (*(p++) == '=')/* It *should* be... */
+ {
+ for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
+ {
+ if (STREQ (label, indicator_name[ind_no]))
+ {
+ color_indicator[ind_no].string = buf;
+ state = (get_funky_string (&buf, &p, false,
+ &color_indicator[ind_no].len)
+ ? PS_START : PS_FAIL);
+ break;
+ }
+ }
+ if (state == PS_FAIL)
+ error (0, 0, _("unrecognized prefix: %s"), label);
+ }
+ break;
+
+ case PS_4: /* Equal sign after *.ext */
+ if (*(p++) == '=')
+ {
+ ext->seq.string = buf;
+ state = (get_funky_string (&buf, &p, false, &ext->seq.len)
+ ? PS_START : PS_FAIL);
+ }
+ else
+ state = PS_FAIL;
+ break;
+
+ case PS_FAIL:
+ goto done;
+
+ default:
+ abort ();
+ }
+ }
+ done:
+
+ if (state == PS_FAIL)
+ {
+ struct color_ext_type *e;
+ struct color_ext_type *e2;
+
+ error (0, 0,
+ _("unparsable value for --palette"));
+ free (color_buf);
+ for (e = color_ext_list; e != NULL; /* empty */)
+ {
+ e2 = e;
+ e = e->next;
+ free (e2);
+ }
+ colors_enabled = false;
+ }
+}
+
+static void
+check_color_output (bool is_pipe)
+{
+ bool output_is_tty;
+
+ if (! outfile || colors_style == NEVER)
+ return;
+
+ output_is_tty = presume_output_tty || (!is_pipe && isatty (fileno (outfile)));
+
+ colors_enabled = (colors_style == ALWAYS
+ || (colors_style == AUTO && output_is_tty));
+
+ if (colors_enabled)
+ parse_diff_color ();
+
+ if (output_is_tty)
+ install_signal_handlers ();
+}
+
+/* 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. */
+
+void
+setup_output (char const *name0, char const *name1, bool recursive)
+{
+ current_name0 = name0;
+ current_name1 = name1;
+ currently_recursive = recursive;
+ outfile = 0;
+}
+
+#if HAVE_WORKING_FORK
+static pid_t pr_pid;
+#endif
+
+static char c_escape_char (char c)
+{
+ switch (c) {
+ case '\a': return 'a';
+ case '\b': return 'b';
+ case '\t': return 't';
+ case '\n': return 'n';
+ case '\v': return 'v';
+ case '\f': return 'f';
+ case '\r': return 'r';
+ case '"': return '"';
+ case '\\': return '\\';
+ default:
+ return c < 32;
+ }
+}
+
+static char *
+c_escape (char const *str)
+{
+ char const *s;
+ size_t plus = 0;
+ bool must_quote = false;
+
+ for (s = str; *s; s++)
+ {
+ char c = *s;
+
+ if (c == ' ')
+ {
+ must_quote = true;
+ continue;
+ }
+ switch (c_escape_char (*s))
+ {
+ case 1:
+ plus += 3;
+ /* fall through */
+ case 0:
+ break;
+ default:
+ plus++;
+ break;
+ }
+ }
+
+ if (must_quote || plus)
+ {
+ size_t s_len = s - str;
+ char *buffer = xmalloc (s_len + plus + 3);
+ char *b = buffer;
+
+ *b++ = '"';
+ for (s = str; *s; s++)
+ {
+ char c = *s;
+ char escape = c_escape_char (c);
+
+ switch (escape)
+ {
+ case 0:
+ *b++ = c;
+ break;
+ case 1:
+ *b++ = '\\';
+ *b++ = ((c >> 6) & 03) + '0';
+ *b++ = ((c >> 3) & 07) + '0';
+ *b++ = ((c >> 0) & 07) + '0';
+ break;
+ default:
+ *b++ = '\\';
+ *b++ = escape;
+ break;
+ }
+ }
+ *b++ = '"';
+ *b = 0;
+ return buffer;
+ }
+
+ return (char *) str;
+}
+
+void
+begin_output (void)
+{
+ char *names[2];
+ char *name;
+
+ if (outfile != 0)
+ return;
+
+ names[0] = c_escape (current_name0);
+ names[1] = c_escape (current_name1);
+
+ /* Construct the header of this piece of diff. */
+ /* POSIX 1003.1-2001 specifies this format. But there are some bugs in
+ the standard: 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. */
+ name = xasprintf ("diff%s %s %s", switch_string, names[0], names[1]);
+
+ if (paginate)
+ {
+ char const *argv[4];
+
+ if (fflush (stdout) != 0)
+ pfatal_with_name (_("write failed"));
+
+ argv[0] = pr_program;
+ argv[1] = "-h";
+ argv[2] = name;
+ argv[3] = 0;
+
+ /* Make OUTFILE a pipe to a subsidiary 'pr'. */
+ {
+#if HAVE_WORKING_FORK
+ int pipes[2];
+
+ if (pipe (pipes) != 0)
+ pfatal_with_name ("pipe");
+
+ pr_pid = fork ();
+ if (pr_pid < 0)
+ pfatal_with_name ("fork");
+
+ 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]);
+ }
+
+ execv (pr_program, (char **) argv);
+ _exit (errno == ENOENT ? 127 : 126);
+ }
+ else
+ {
+ close (pipes[0]);
+ outfile = fdopen (pipes[1], "w");
+ if (!outfile)
+ pfatal_with_name ("fdopen");
+ check_color_output (true);
+ }
+#else
+ char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
+ errno = 0;
+ outfile = popen (command, "w");
+ if (!outfile)
+ pfatal_with_name (command);
+ check_color_output (true);
+ free (command);
+#endif
+ }
+ }
+ else
+ {
+
+ /* If -l was not specified, output the diff straight to 'stdout'. */
+
+ outfile = stdout;
+ check_color_output (false);
+
+ /* If handling multiple files (because scanning a directory),
+ print which files the following output is about. */
+ if (currently_recursive)
+ 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, (char const *const *)names, false);
+ break;
+
+ case OUTPUT_UNIFIED:
+ print_context_header (files, (char const *const *)names, true);
+ break;
+
+ default:
+ break;
+ }
+
+ if (names[0] != current_name0)
+ free (names[0]);
+ if (names[1] != current_name1)
+ free (names[1]);
+}
+
+/* Call after the end of output of diffs for one file.
+ Close OUTFILE and get rid of the 'pr' subfork. */
+
+void
+finish_output (void)
+{
+ if (outfile != 0 && outfile != stdout)
+ {
+ int status;
+ int wstatus;
+ int werrno = 0;
+ if (ferror (outfile))
+ fatal ("write failed");
+#if ! HAVE_WORKING_FORK
+ wstatus = pclose (outfile);
+ if (wstatus == -1)
+ werrno = errno;
+#else
+ if (fclose (outfile) != 0)
+ pfatal_with_name (_("write failed"));
+ if (waitpid (pr_pid, &wstatus, 0) < 0)
+ pfatal_with_name ("waitpid");
+#endif
+ status = (! werrno && WIFEXITED (wstatus)
+ ? WEXITSTATUS (wstatus)
+ : INT_MAX);
+ if (status)
+ error (EXIT_TROUBLE, werrno,
+ _(status == 126
+ ? "subsidiary program '%s' could not be invoked"
+ : status == 127
+ ? "subsidiary program '%s' not found"
+ : status == INT_MAX
+ ? "subsidiary program '%s' failed"
+ : "subsidiary program '%s' failed (exit status %d)"),
+ pr_program, status);
+ }
+
+ outfile = 0;
+}
+
+/* Compare two lines (typically one from each input file)
+ according to the command line options.
+ For efficiency, this is invoked only when the lines do not match exactly
+ but an option like -i might cause us to ignore the difference.
+ Return nonzero if the lines differ. */
+
+bool
+lines_differ (char const *s1, char const *s2)
+{
+ register char const *t1 = s1;
+ register char const *t2 = s2;
+ size_t column = 0;
+
+ while (1)
+ {
+ register unsigned char c1 = *t1++;
+ register unsigned char c2 = *t2++;
+
+ /* Test for exact char equality first, since it's a common case. */
+ if (c1 != c2)
+ {
+ switch (ignore_white_space)
+ {
+ case IGNORE_ALL_SPACE:
+ /* For -w, just skip past any white space. */
+ while (isspace (c1) && c1 != '\n') c1 = *t1++;
+ while (isspace (c2) && c2 != '\n') c2 = *t2++;
+ break;
+
+ case IGNORE_SPACE_CHANGE:
+ /* For -b, advance past any sequence of white space in
+ line 1 and consider it just one space, or nothing at
+ all if it is at the end of the line. */
+ if (isspace (c1))
+ {
+ while (c1 != '\n')
+ {
+ c1 = *t1++;
+ if (! isspace (c1))
+ {
+ --t1;
+ c1 = ' ';
+ break;
+ }
+ }
+ }
+
+ /* Likewise for line 2. */
+ if (isspace (c2))
+ {
+ while (c2 != '\n')
+ {
+ c2 = *t2++;
+ if (! isspace (c2))
+ {
+ --t2;
+ c2 = ' ';
+ break;
+ }
+ }
+ }
+
+ if (c1 != c2)
+ {
+ /* If we went too far when doing the simple test
+ for equality, go back to the first non-white-space
+ character in both sides and try again. */
+ if (c2 == ' ' && c1 != '\n'
+ && s1 + 1 < t1
+ && isspace ((unsigned char) t1[-2]))
+ {
+ --t1;
+ continue;
+ }
+ if (c1 == ' ' && c2 != '\n'
+ && s2 + 1 < t2
+ && isspace ((unsigned char) t2[-2]))
+ {
+ --t2;
+ continue;
+ }
+ }
+
+ break;
+
+ case IGNORE_TRAILING_SPACE:
+ case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
+ if (isspace (c1) && isspace (c2))
+ {
+ unsigned char c;
+ if (c1 != '\n')
+ {
+ char const *p = t1;
+ while ((c = *p) != '\n' && isspace (c))
+ ++p;
+ if (c != '\n')
+ break;
+ }
+ if (c2 != '\n')
+ {
+ char const *p = t2;
+ while ((c = *p) != '\n' && isspace (c))
+ ++p;
+ if (c != '\n')
+ break;
+ }
+ /* Both lines have nothing but whitespace left. */
+ return false;
+ }
+ if (ignore_white_space == IGNORE_TRAILING_SPACE)
+ break;
+ /* Fall through. */
+ case IGNORE_TAB_EXPANSION:
+ if ((c1 == ' ' && c2 == '\t')
+ || (c1 == '\t' && c2 == ' '))
+ {
+ size_t column2 = column;
+ for (;; c1 = *t1++)
+ {
+ if (c1 == ' ')
+ column++;
+ else if (c1 == '\t')
+ column += tabsize - column % tabsize;
+ else
+ break;
+ }
+ for (;; c2 = *t2++)
+ {
+ if (c2 == ' ')
+ column2++;
+ else if (c2 == '\t')
+ column2 += tabsize - column2 % tabsize;
+ else
+ break;
+ }
+ if (column != column2)
+ return true;
+ }
+ break;
+
+ case IGNORE_NO_WHITE_SPACE:
+ break;
+ }
+
+ /* Lowercase all letters if -i is specified. */
+
+ if (ignore_case)
+ {
+ c1 = tolower (c1);
+ c2 = tolower (c2);
+ }
+
+ if (c1 != c2)
+ break;
+ }
+ if (c1 == '\n')
+ return false;
+
+ column += c1 == '\t' ? tabsize - column % tabsize : 1;
+ }
+
+ return true;
+}
+
+/* Find the consecutive changes at the start of the script START.
+ Return the last link before the first gap. */
+
+struct change * _GL_ATTRIBUTE_CONST
+find_change (struct change *start)
+{
+ return start;
+}
+
+struct change * _GL_ATTRIBUTE_CONST
+find_reverse_change (struct change *start)
+{
+ return start;
+}
+
+/* Divide SCRIPT into pieces by calling HUNKFUN and
+ print each piece with PRINTFUN.
+ Both functions take one arg, an edit script.
+
+ HUNKFUN is called with the tail of the script
+ and returns the last link that belongs together with the start
+ of the tail.
+
+ PRINTFUN takes a subscript which belongs together (with a null
+ link at the end) and prints it. */
+
+void
+print_script (struct change *script,
+ struct change * (*hunkfun) (struct change *),
+ void (*printfun) (struct change *))
+{
+ struct change *next = script;
+
+ while (next)
+ {
+ struct change *this, *end;
+
+ /* Find a set of changes that belong together. */
+ this = next;
+ end = (*hunkfun) (next);
+
+ /* Disconnect them from the rest of the changes,
+ making them a hunk, and remember the rest for next iteration. */
+ next = end->link;
+ end->link = 0;
+#ifdef DEBUG
+ debug_script (this);
+#endif
+
+ /* Print this hunk. */
+ (*printfun) (this);
+
+ /* Reconnect the script so it will all be freed properly. */
+ end->link = next;
+ }
+}
+
+/* Print the text of a single line LINE,
+ flagging it with the characters in LINE_FLAG (which say whether
+ the line is inserted, deleted, changed, etc.). LINE_FLAG must not
+ end in a blank, unless it is a single blank. */
+
+void
+print_1_line (char const *line_flag, char const *const *line)
+{
+ print_1_line_nl (line_flag, line, false);
+}
+
+/* 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.). LINE_FLAG must not
+ end in a blank, unless it is a single blank. If SKIP_NL is set, then
+ the final '\n' is not printed. */
+
+void
+print_1_line_nl (char const *line_flag, char const *const *line, bool skip_nl)
+{
+ char const *base = line[0], *limit = line[1]; /* Help the compiler. */
+ FILE *out = outfile; /* Help the compiler some more. */
+ char const *flag_format = 0;
+
+ /* If -T was specified, use a Tab between the line-flag and the text.
+ Otherwise use a Space (as Unix diff does).
+ Print neither space nor tab if line-flags are empty.
+ But omit trailing blanks if requested. */
+
+ if (line_flag && *line_flag)
+ {
+ char const *flag_format_1 = flag_format = initial_tab ? "%s\t" : "%s ";
+ char const *line_flag_1 = line_flag;
+
+ if (suppress_blank_empty && **line == '\n')
+ {
+ flag_format_1 = "%s";
+
+ /* This hack to omit trailing blanks takes advantage of the
+ fact that the only way that LINE_FLAG can end in a blank
+ is when LINE_FLAG consists of a single blank. */
+ line_flag_1 += *line_flag_1 == ' ';
+ }
+
+ fprintf (out, flag_format_1, line_flag_1);
+ }
+
+ output_1_line (base, limit - (skip_nl && limit[-1] == '\n'), flag_format, line_flag);
+
+ if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
+ {
+ set_color_context (RESET_CONTEXT);
+ fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
+ }
+}
+
+/* Output a line from BASE up to LIMIT.
+ 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 (char const *base, char const *limit, char const *flag_format,
+ char const *line_flag)
+{
+ const size_t MAX_CHUNK = 1024;
+ if (!expand_tabs)
+ {
+ size_t left = limit - base;
+ while (left)
+ {
+ size_t to_write = MIN (left, MAX_CHUNK);
+ size_t written = fwrite (base, sizeof (char), to_write, outfile);
+ if (written < to_write)
+ return;
+ base += written;
+ left -= written;
+ process_signals ();
+ }
+ }
+ else
+ {
+ register FILE *out = outfile;
+ register unsigned char c;
+ register char const *t = base;
+ register size_t column = 0;
+ size_t tab_size = tabsize;
+ size_t counter_proc_signals = 0;
+
+ while (t < limit)
+ {
+ counter_proc_signals++;
+ if (counter_proc_signals == MAX_CHUNK)
+ {
+ process_signals ();
+ counter_proc_signals = 0;
+ }
+
+ switch ((c = *t++))
+ {
+ case '\t':
+ {
+ size_t spaces = tab_size - column % tab_size;
+ 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:
+ column += isprint (c) != 0;
+ putc (c, out);
+ break;
+ }
+ }
+ }
+}
+
+enum indicator_no
+ {
+ C_LEFT, C_RIGHT, C_END, C_RESET, C_HEADER, C_ADD, C_DELETE, C_LINE
+ };
+
+static void
+put_indicator (const struct bin_str *ind)
+{
+ fwrite (ind->string, ind->len, 1, outfile);
+}
+
+static enum color_context last_context = RESET_CONTEXT;
+
+void
+set_color_context (enum color_context color_context)
+{
+ if (color_context != RESET_CONTEXT)
+ process_signals ();
+ if (colors_enabled && last_context != color_context)
+ {
+ put_indicator (&color_indicator[C_LEFT]);
+ switch (color_context)
+ {
+ case HEADER_CONTEXT:
+ put_indicator (&color_indicator[C_HEADER]);
+ break;
+
+ case LINE_NUMBER_CONTEXT:
+ put_indicator (&color_indicator[C_LINE]);
+ break;
+
+ case ADD_CONTEXT:
+ put_indicator (&color_indicator[C_ADD]);
+ break;
+
+ case DELETE_CONTEXT:
+ put_indicator (&color_indicator[C_DELETE]);
+ break;
+
+ case RESET_CONTEXT:
+ put_indicator (&color_indicator[C_RESET]);
+ break;
+
+ default:
+ abort ();
+ }
+ put_indicator (&color_indicator[C_RIGHT]);
+ last_context = color_context;
+ }
+}
+
+
+char const change_letter[] = { 0, 'd', 'a', 'c' };
+
+/* Translate an internal line number (an index into diff's table of lines)
+ into an actual line number in the input file.
+ The internal line number is I. 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. */
+
+lin _GL_ATTRIBUTE_PURE
+translate_line_number (struct file_data const *file, lin i)
+{
+ return i + file->prefix_lines + 1;
+}
+
+/* Translate a line number range. This is always done for printing,
+ so for convenience translate to long int rather than lin, so that the
+ caller can use printf with "%ld" without casting. */
+
+void
+translate_range (struct file_data const *file,
+ lin a, lin b,
+ long int *aptr, long int *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 (char sepchar, struct file_data *file, lin a, lin b)
+{
+ long 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, "%ld%c%ld", trans_a, sepchar, trans_b);
+ else
+ fprintf (outfile, "%ld", trans_b);
+}
+
+/* Look at a hunk of edit script and report the range of lines in each file
+ that it applies to. HUNK is the start of the hunk, which is a chain
+ of 'struct change'. The first and last line numbers of file 0 are stored in
+ *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
+ Note that these are internal line numbers that count from 0.
+
+ If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
+
+ Return UNCHANGED if only ignorable lines are inserted or deleted,
+ OLD if lines of file 0 are deleted,
+ NEW if lines of file 1 are inserted,
+ and CHANGED if both kinds of changes are found. */
+
+enum changes
+analyze_hunk (struct change *hunk,
+ lin *first0, lin *last0,
+ lin *first1, lin *last1)
+{
+ struct change *next;
+ lin l0, l1;
+ lin show_from, show_to;
+ lin i;
+ bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
+ size_t trivial_length = ignore_blank_lines - 1;
+ /* If 0, ignore zero-length lines;
+ if SIZE_MAX, do not ignore lines just because of their length. */
+
+ bool skip_white_space =
+ ignore_blank_lines && IGNORE_TRAILING_SPACE <= ignore_white_space;
+ bool skip_leading_white_space =
+ skip_white_space && IGNORE_SPACE_CHANGE <= ignore_white_space;
+
+ char const * const *linbuf0 = files[0].linbuf; /* Help the compiler. */
+ char const * const *linbuf1 = files[1].linbuf;
+
+ 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++)
+ {
+ char const *line = linbuf0[i];
+ char const *lastbyte = linbuf0[i + 1] - 1;
+ char const *newline = lastbyte + (*lastbyte != '\n');
+ size_t len = newline - line;
+ char const *p = line;
+ if (skip_white_space)
+ for (; *p != '\n'; p++)
+ if (! isspace ((unsigned char) *p))
+ {
+ if (! skip_leading_white_space)
+ p = line;
+ break;
+ }
+ if (newline - p != trivial_length
+ && (! ignore_regexp.fastmap
+ || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
+ trivial = 0;
+ }
+
+ for (i = next->line1; i <= l1 && trivial; i++)
+ {
+ char const *line = linbuf1[i];
+ char const *lastbyte = linbuf1[i + 1] - 1;
+ char const *newline = lastbyte + (*lastbyte != '\n');
+ size_t len = newline - line;
+ char const *p = line;
+ if (skip_white_space)
+ for (; *p != '\n'; p++)
+ if (! isspace ((unsigned char) *p))
+ {
+ if (! skip_leading_white_space)
+ p = line;
+ break;
+ }
+ if (newline - p != trivial_length
+ && (! ignore_regexp.fastmap
+ || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
+ 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)
+ return UNCHANGED;
+
+ return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
+}
+
+/* Concatenate three strings, returning a newly malloc'd string. */
+
+char *
+concat (char const *s1, char const *s2, char const *s3)
+{
+ char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
+ sprintf (new, "%s%s%s", s1, s2, s3);
+ return new;
+}
+
+/* Yield a new block of SIZE bytes, initialized to zero. */
+
+void *
+zalloc (size_t size)
+{
+ void *p = xmalloc (size);
+ memset (p, 0, size);
+ return p;
+}
+
+void
+debug_script (struct change *sp)
+{
+ fflush (stdout);
+
+ for (; sp; sp = sp->link)
+ {
+ long int line0 = sp->line0;
+ long int line1 = sp->line1;
+ long int deleted = sp->deleted;
+ long int inserted = sp->inserted;
+ fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n",
+ line0, line1, deleted, inserted);
+ }
+
+ fflush (stderr);
+}