diff options
| author | wiz <wiz@NetBSD.org> | 2003-01-26 00:43:02 +0000 |
|---|---|---|
| committer | wiz <wiz@NetBSD.org> | 2003-01-26 00:43:02 +0000 |
| commit | b8ffffc7d9d0669c0c11591e108943d9f0af97d5 (patch) | |
| tree | edfd730e7deb5e72331af706e54fb4986ce34a09 /gnu/dist/diffutils/src | |
| parent | 488efef5d4aa3d69ffdaeb4e0efd106c5fdbcf8a (diff) | |
Import of diffutils-2.8.1 (converted with to-be-committed
diffutils2netbsd script).
Diffstat (limited to 'gnu/dist/diffutils/src')
| -rw-r--r-- | gnu/dist/diffutils/src/Makefile.am | 44 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/Makefile.in | 418 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/analyze.c | 1070 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/cmp.c | 676 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/context.c | 480 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/diff.c | 1365 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/diff.h | 377 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/diff3.c | 1747 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/dir.c | 291 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/ed.c | 171 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/ifdef.c | 426 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/io.c | 857 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/normal.c | 73 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/sdiff.c | 1222 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/side.c | 285 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/system.h | 403 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/util.c | 758 | ||||
| -rw-r--r-- | gnu/dist/diffutils/src/version.c | 4 |
18 files changed, 10667 insertions, 0 deletions
diff --git a/gnu/dist/diffutils/src/Makefile.am b/gnu/dist/diffutils/src/Makefile.am new file mode 100644 index 00000000000..9d382a5af0e --- /dev/null +++ b/gnu/dist/diffutils/src/Makefile.am @@ -0,0 +1,44 @@ +# Automakefile for GNU diffutils programs. + +# Copyright (C) 2001, 2002 Free Software Foundation, Inc. + +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2, or (at your option) +## any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +## 02111-1307, USA. + +bin_PROGRAMS = cmp diff diff3 sdiff + +noinst_HEADERS = diff.h system.h + +localedir = $(datadir)/locale + +AM_CPPFLAGS = -I../lib -I$(top_srcdir)/lib +DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ + +LDADD = ../lib/libdiffutils.a @LIBINTL@ +diff_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ + +cmp_SOURCES = cmp.c version.c +diff3_SOURCES = diff3.c version.c +sdiff_SOURCES = sdiff.c version.c +diff_SOURCES = \ + analyze.c context.c diff.c dir.c ed.c ifdef.c io.c \ + normal.c side.c util.c version.c + +MAINTAINERCLEANFILES = $(srcdir)/version.c + +$(srcdir)/version.c: $(top_srcdir)/configure.ac + (echo '#include <config.h>' && \ + echo 'char const version_string[] = "(GNU $(PACKAGE)) $(VERSION)";') \ + >$@ diff --git a/gnu/dist/diffutils/src/Makefile.in b/gnu/dist/diffutils/src/Makefile.in new file mode 100644 index 00000000000..287f368da82 --- /dev/null +++ b/gnu/dist/diffutils/src/Makefile.in @@ -0,0 +1,418 @@ +# Makefile.in generated by automake 1.6 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# 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 Free Software Foundation, Inc. +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CPP = @CPP@ +DATADIRNAME = @DATADIRNAME@ +DEPDIR = @DEPDIR@ +GENCAT = @GENCAT@ +GLIBC21 = @GLIBC21@ +GMSGFMT = @GMSGFMT@ +HAVE_LIB = @HAVE_LIB@ +HELP2MAN = @HELP2MAN@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLBISON = @INTLBISON@ +INTLLIBS = @INTLLIBS@ +INTLOBJS = @INTLOBJS@ +INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@ +LIB = @LIB@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ +LTLIB = @LTLIB@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +PACKAGE = @PACKAGE@ +POSUB = @POSUB@ +PR_PROGRAM = @PR_PROGRAM@ +RANLIB = @RANLIB@ +STRIP = @STRIP@ +U = @U@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +bin_PROGRAMS = cmp diff diff3 sdiff + +noinst_HEADERS = diff.h system.h + +localedir = $(datadir)/locale + +AM_CPPFLAGS = -I../lib -I$(top_srcdir)/lib +DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ + +LDADD = ../lib/libdiffutils.a @LIBINTL@ +diff_LDADD = $(LDADD) @LIB_CLOCK_GETTIME@ + +cmp_SOURCES = cmp.c version.c +diff3_SOURCES = diff3.c version.c +sdiff_SOURCES = sdiff.c version.c +diff_SOURCES = \ + analyze.c context.c diff.c dir.c ed.c ifdef.c io.c \ + normal.c side.c util.c version.c + + +MAINTAINERCLEANFILES = $(srcdir)/version.c +subdir = src +mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +bin_PROGRAMS = cmp$(EXEEXT) diff$(EXEEXT) diff3$(EXEEXT) sdiff$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) + +am_cmp_OBJECTS = cmp.$(OBJEXT) version.$(OBJEXT) +cmp_OBJECTS = $(am_cmp_OBJECTS) +cmp_LDADD = $(LDADD) +cmp_DEPENDENCIES = ../lib/libdiffutils.a +cmp_LDFLAGS = +am_diff_OBJECTS = analyze.$(OBJEXT) context.$(OBJEXT) diff.$(OBJEXT) \ + dir.$(OBJEXT) ed.$(OBJEXT) ifdef.$(OBJEXT) io.$(OBJEXT) \ + normal.$(OBJEXT) side.$(OBJEXT) util.$(OBJEXT) \ + version.$(OBJEXT) +diff_OBJECTS = $(am_diff_OBJECTS) +diff_DEPENDENCIES = ../lib/libdiffutils.a +diff_LDFLAGS = +am_diff3_OBJECTS = diff3.$(OBJEXT) version.$(OBJEXT) +diff3_OBJECTS = $(am_diff3_OBJECTS) +diff3_LDADD = $(LDADD) +diff3_DEPENDENCIES = ../lib/libdiffutils.a +diff3_LDFLAGS = +am_sdiff_OBJECTS = sdiff.$(OBJEXT) version.$(OBJEXT) +sdiff_OBJECTS = $(am_sdiff_OBJECTS) +sdiff_LDADD = $(LDADD) +sdiff_DEPENDENCIES = ../lib/libdiffutils.a +sdiff_LDFLAGS = +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/analyze.Po ./$(DEPDIR)/cmp.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/context.Po ./$(DEPDIR)/diff.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/diff3.Po ./$(DEPDIR)/dir.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/ed.Po ./$(DEPDIR)/ifdef.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/io.Po ./$(DEPDIR)/normal.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/sdiff.Po ./$(DEPDIR)/side.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/util.Po ./$(DEPDIR)/version.Po +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +DIST_SOURCES = $(cmp_SOURCES) $(diff_SOURCES) $(diff3_SOURCES) \ + $(sdiff_SOURCES) +HEADERS = $(noinst_HEADERS) + +DIST_COMMON = $(noinst_HEADERS) Makefile.am Makefile.in +SOURCES = $(cmp_SOURCES) $(diff_SOURCES) $(diff3_SOURCES) $(sdiff_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnits src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + p1=`echo "$$p1" | sed -e 's,^.*/,,'`; \ + f=`echo $$p1|sed '$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) $$p $(DESTDIR)$(bindir)/$$f"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) $$p $(DESTDIR)$(bindir)/$$f; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + f=`echo "$$f" | sed -e 's,^.*/,,'`; \ + echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ + rm -f $(DESTDIR)$(bindir)/$$f; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +cmp$(EXEEXT): $(cmp_OBJECTS) $(cmp_DEPENDENCIES) + @rm -f cmp$(EXEEXT) + $(LINK) $(cmp_LDFLAGS) $(cmp_OBJECTS) $(cmp_LDADD) $(LIBS) +diff$(EXEEXT): $(diff_OBJECTS) $(diff_DEPENDENCIES) + @rm -f diff$(EXEEXT) + $(LINK) $(diff_LDFLAGS) $(diff_OBJECTS) $(diff_LDADD) $(LIBS) +diff3$(EXEEXT): $(diff3_OBJECTS) $(diff3_DEPENDENCIES) + @rm -f diff3$(EXEEXT) + $(LINK) $(diff3_LDFLAGS) $(diff3_OBJECTS) $(diff3_LDADD) $(LIBS) +sdiff$(EXEEXT): $(sdiff_OBJECTS) $(sdiff_DEPENDENCIES) + @rm -f sdiff$(EXEEXT) + $(LINK) $(sdiff_LDFLAGS) $(sdiff_OBJECTS) $(sdiff_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/analyze.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diff3.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifdef.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/normal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sdiff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/side.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ + +distclean-depend: + -rm -rf ./$(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` +CCDEPMODE = @CCDEPMODE@ +uninstall-info-am: + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$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: check-am +all-am: Makefile $(PROGRAMS) $(HEADERS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + +install: 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +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 "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic distclean distclean-compile distclean-depend \ + distclean-generic distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-binPROGRAMS install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-info-am + + +$(srcdir)/version.c: $(top_srcdir)/configure.ac + (echo '#include <config.h>' && \ + echo 'char const version_string[] = "(GNU $(PACKAGE)) $(VERSION)";') \ + >$@ +# 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/gnu/dist/diffutils/src/analyze.c b/gnu/dist/diffutils/src/analyze.c new file mode 100644 index 00000000000..4271d23bf2f --- /dev/null +++ b/gnu/dist/diffutils/src/analyze.c @@ -0,0 +1,1070 @@ +/* $NetBSD: analyze.c,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */ + +/* Analyze file differences for GNU DIFF. + + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* The basic algorithm is described in: + "An O(ND) Difference Algorithm and its Variations", Eugene Myers, + Algorithmica Vol. 1 No. 2, 1986, pp. 251-266; + see especially section 4.2, which describes the variation used below. + Unless the --minimal option is specified, this code uses the TOO_EXPENSIVE + heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N) + at the price of producing suboptimal output for large inputs with + many differences. + + The basic algorithm was independently discovered as described in: + "Algorithms for Approximate String Matching", E. Ukkonen, + Information and Control Vol. 64, 1985, pp. 100-118. */ + +#include "diff.h" +#include <cmpbuf.h> +#include <error.h> +#include <regex.h> +#include <xalloc.h> + +static lin *xvec, *yvec; /* Vectors being compared. */ +static lin *fdiag; /* Vector, indexed by diagonal, containing + 1 + the X coordinate of the point furthest + along the given diagonal in the forward + search of the edit matrix. */ +static lin *bdiag; /* Vector, indexed by diagonal, containing + the X coordinate of the point furthest + along the given diagonal in the backward + search of the edit matrix. */ +static lin too_expensive; /* Edit scripts longer than this are too + expensive to compute. */ + +#define SNAKE_LIMIT 20 /* Snakes bigger than this are considered `big'. */ + +struct partition +{ + lin xmid, ymid; /* Midpoints of this partition. */ + bool lo_minimal; /* Nonzero if low half will be analyzed minimally. */ + bool hi_minimal; /* Likewise for high half. */ +}; + +/* Find the midpoint of the shortest edit script for a specified + portion of the two files. + + Scan from the beginnings of the files, and simultaneously from the ends, + doing a breadth-first search through the space of edit-sequence. + When the two searches meet, we have found the midpoint of the shortest + edit sequence. + + If FIND_MINIMAL is nonzero, find the minimal edit script regardless + of expense. Otherwise, if the search is too expensive, use + heuristics to stop the search and report a suboptimal answer. + + Set PART->(xmid,ymid) to the midpoint (XMID,YMID). The diagonal number + XMID - YMID equals the number of inserted lines minus the number + of deleted lines (counting only lines before the midpoint). + Return the approximate edit cost; this is the total number of + lines inserted or deleted (counting only lines before the midpoint), + unless a heuristic is used to terminate the search prematurely. + + Set PART->lo_minimal to true iff the minimal edit script for the + left half of the partition is known; similarly for PART->hi_minimal. + + This function assumes that the first lines of the specified portions + of the two files do not match, and likewise that the last lines do not + match. The caller must trim matching lines from the beginning and end + of the portions it is going to specify. + + If we return the "wrong" partitions, + the worst this can do is cause suboptimal diff output. + It cannot cause incorrect diff output. */ + +static lin +diag (lin xoff, lin xlim, lin yoff, lin ylim, bool find_minimal, + struct partition *part) +{ + lin *const fd = fdiag; /* Give the compiler a chance. */ + lin *const bd = bdiag; /* Additional help for the compiler. */ + lin const *const xv = xvec; /* Still more help for the compiler. */ + lin const *const yv = yvec; /* And more and more . . . */ + lin const dmin = xoff - ylim; /* Minimum valid diagonal. */ + lin const dmax = xlim - yoff; /* Maximum valid diagonal. */ + lin const fmid = xoff - yoff; /* Center diagonal of top-down search. */ + lin const bmid = xlim - ylim; /* Center diagonal of bottom-up search. */ + lin fmin = fmid, fmax = fmid; /* Limits of top-down search. */ + lin bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */ + lin c; /* Cost. */ + bool odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd + diagonal with respect to the northwest. */ + + fd[fmid] = xoff; + bd[bmid] = xlim; + + for (c = 1;; ++c) + { + lin d; /* Active diagonal. */ + bool big_snake = 0; + + /* Extend the top-down search by an edit step in each diagonal. */ + fmin > dmin ? fd[--fmin - 1] = -1 : ++fmin; + fmax < dmax ? fd[++fmax + 1] = -1 : --fmax; + for (d = fmax; d >= fmin; d -= 2) + { + lin x, y, oldx, tlo = fd[d - 1], thi = fd[d + 1]; + + if (tlo >= thi) + x = tlo + 1; + else + x = thi; + oldx = x; + y = x - d; + while (x < xlim && y < ylim && xv[x] == yv[y]) + ++x, ++y; + if (x - oldx > SNAKE_LIMIT) + big_snake = 1; + fd[d] = x; + if (odd && bmin <= d && d <= bmax && bd[d] <= x) + { + part->xmid = x; + part->ymid = y; + part->lo_minimal = part->hi_minimal = 1; + return 2 * c - 1; + } + } + + /* Similarly extend the bottom-up search. */ + bmin > dmin ? bd[--bmin - 1] = LIN_MAX : ++bmin; + bmax < dmax ? bd[++bmax + 1] = LIN_MAX : --bmax; + for (d = bmax; d >= bmin; d -= 2) + { + lin x, y, oldx, tlo = bd[d - 1], thi = bd[d + 1]; + + if (tlo < thi) + x = tlo; + else + x = thi - 1; + oldx = x; + y = x - d; + while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) + --x, --y; + if (oldx - x > SNAKE_LIMIT) + big_snake = 1; + bd[d] = x; + if (!odd && fmin <= d && d <= fmax && x <= fd[d]) + { + part->xmid = x; + part->ymid = y; + part->lo_minimal = part->hi_minimal = 1; + return 2 * c; + } + } + + if (find_minimal) + continue; + + /* Heuristic: check occasionally for a diagonal that has made + lots of progress compared with the edit distance. + If we have any such, find the one that has made the most + progress and return it as if it had succeeded. + + With this heuristic, for files with a constant small density + of changes, the algorithm is linear in the file size. */ + + if (200 < c && big_snake && speed_large_files) + { + lin best; + + best = 0; + for (d = fmax; d >= fmin; d -= 2) + { + lin dd = d - fmid; + lin x = fd[d]; + lin y = x - d; + lin v = (x - xoff) * 2 - dd; + if (v > 12 * (c + (dd < 0 ? -dd : dd))) + { + if (v > best + && xoff + SNAKE_LIMIT <= x && x < xlim + && yoff + SNAKE_LIMIT <= y && y < ylim) + { + /* We have a good enough best diagonal; + now insist that it end with a significant snake. */ + int k; + + for (k = 1; xv[x - k] == yv[y - k]; k++) + if (k == SNAKE_LIMIT) + { + best = v; + part->xmid = x; + part->ymid = y; + break; + } + } + } + } + if (best > 0) + { + part->lo_minimal = 1; + part->hi_minimal = 0; + return 2 * c - 1; + } + + best = 0; + for (d = bmax; d >= bmin; d -= 2) + { + lin dd = d - bmid; + lin x = bd[d]; + lin y = x - d; + lin v = (xlim - x) * 2 + dd; + if (v > 12 * (c + (dd < 0 ? -dd : dd))) + { + if (v > best + && xoff < x && x <= xlim - SNAKE_LIMIT + && yoff < y && y <= ylim - SNAKE_LIMIT) + { + /* We have a good enough best diagonal; + now insist that it end with a significant snake. */ + int k; + + for (k = 0; xv[x + k] == yv[y + k]; k++) + if (k == SNAKE_LIMIT - 1) + { + best = v; + part->xmid = x; + part->ymid = y; + break; + } + } + } + } + if (best > 0) + { + part->lo_minimal = 0; + part->hi_minimal = 1; + return 2 * c - 1; + } + } + + /* Heuristic: if we've gone well beyond the call of duty, + give up and report halfway between our best results so far. */ + if (c >= too_expensive) + { + lin fxybest, fxbest; + lin bxybest, bxbest; + + fxbest = bxbest = 0; /* Pacify `gcc -Wall'. */ + + /* Find forward diagonal that maximizes X + Y. */ + fxybest = -1; + for (d = fmax; d >= fmin; d -= 2) + { + lin x = MIN (fd[d], xlim); + lin y = x - d; + if (ylim < y) + x = ylim + d, y = ylim; + if (fxybest < x + y) + { + fxybest = x + y; + fxbest = x; + } + } + + /* Find backward diagonal that minimizes X + Y. */ + bxybest = LIN_MAX; + for (d = bmax; d >= bmin; d -= 2) + { + lin x = MAX (xoff, bd[d]); + lin y = x - d; + if (y < yoff) + x = yoff + d, y = yoff; + if (x + y < bxybest) + { + bxybest = x + y; + bxbest = x; + } + } + + /* Use the better of the two diagonals. */ + if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff)) + { + part->xmid = fxbest; + part->ymid = fxybest - fxbest; + part->lo_minimal = 1; + part->hi_minimal = 0; + } + else + { + part->xmid = bxbest; + part->ymid = bxybest - bxbest; + part->lo_minimal = 0; + part->hi_minimal = 1; + } + return 2 * c - 1; + } + } +} + +/* Compare in detail contiguous subsequences of the two files + which are known, as a whole, to match each other. + + The results are recorded in the vectors files[N].changed, by + storing 1 in the element for each line that is an insertion or deletion. + + The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. + + Note that XLIM, YLIM are exclusive bounds. + All line numbers are origin-0 and discarded lines are not counted. + + If FIND_MINIMAL, find a minimal difference no matter how + expensive it is. */ + +static void +compareseq (lin xoff, lin xlim, lin yoff, lin ylim, bool find_minimal) +{ + lin * const xv = xvec; /* Help the compiler. */ + lin * const yv = yvec; + + /* Slide down the bottom initial diagonal. */ + while (xoff < xlim && yoff < ylim && xv[xoff] == yv[yoff]) + ++xoff, ++yoff; + /* Slide up the top initial diagonal. */ + while (xlim > xoff && ylim > yoff && xv[xlim - 1] == yv[ylim - 1]) + --xlim, --ylim; + + /* Handle simple cases. */ + if (xoff == xlim) + while (yoff < ylim) + files[1].changed[files[1].realindexes[yoff++]] = 1; + else if (yoff == ylim) + while (xoff < xlim) + files[0].changed[files[0].realindexes[xoff++]] = 1; + else + { + lin c; + struct partition part; + + /* Find a point of correspondence in the middle of the files. */ + + c = diag (xoff, xlim, yoff, ylim, find_minimal, &part); + + if (c == 1) + { + /* This should be impossible, because it implies that + one of the two subsequences is empty, + and that case was handled above without calling `diag'. + Let's verify that this is true. */ + abort (); +#if 0 + /* The two subsequences differ by a single insert or delete; + record it and we are done. */ + if (part.xmid - part.ymid < xoff - yoff) + files[1].changed[files[1].realindexes[part.ymid - 1]] = 1; + else + files[0].changed[files[0].realindexes[part.xmid]] = 1; +#endif + } + else + { + /* Use the partitions to split this problem into subproblems. */ + compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal); + compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal); + } + } +} + +/* Discard lines from one file that have no matches in the other file. + + A line which is discarded will not be considered by the actual + comparison algorithm; it will be as if that line were not in the file. + The file's `realindexes' table maps virtual line numbers + (which don't count the discarded lines) into real line numbers; + this is how the actual comparison algorithm produces results + that are comprehensible when the discarded lines are counted. + + When we discard a line, we also mark it as a deletion or insertion + so that it will be printed in the output. */ + +static void +discard_confusing_lines (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++) + { + bool *changed = filevec[f].changed; + bool const *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; + bool *changed0 = filevec[0].changed; + bool *changed1 = filevec[1].changed; + lin len0 = filevec[0].buffered_lines; + lin len1 = filevec[1].buffered_lines; + + /* Note that changedN[len0] 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; + bool *changed0 = filevec[0].changed; + bool *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. + Return 2 if trouble, CHANGES otherwise. */ +static int +briefly_report (int changes, struct file_data const filevec[]) +{ + if (changes) + { + char const *label0 = file_label[0] ? file_label[0] : filevec[0].name; + char const *label1 = file_label[1] ? file_label[1] : filevec[1].name; + + if (brief) + message ("Files %s and %s differ\n", label0, label1); + else + { + message ("Binary files %s and %s differ\n", label0, label1); + changes = 2; + } + } + + return changes; +} + +/* Report the differences of two files. */ +int +diff_2_files (struct comparison *cmp) +{ + lin diags; + 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 + && (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; + } + } + } + + changes = briefly_report (changes, cmp->file); + } + else + { + /* Allocate vectors for the results of comparison: + a flag for each line of each file, saying whether that line + is an insertion or deletion. + Allocate an extra element, always 0, at each end of each vector. */ + + size_t s = cmp->file[0].buffered_lines + cmp->file[1].buffered_lines + 4; + bool *flag_space = zalloc (s * sizeof *flag_space); + 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. */ + + xvec = cmp->file[0].undiscarded; + yvec = cmp->file[1].undiscarded; + diags = (cmp->file[0].nondiscarded_lines + + cmp->file[1].nondiscarded_lines + 3); + fdiag = xmalloc (diags * (2 * sizeof *fdiag)); + bdiag = fdiag + diags; + fdiag += cmp->file[1].nondiscarded_lines + 1; + bdiag += cmp->file[1].nondiscarded_lines + 1; + + /* Set TOO_EXPENSIVE to be approximate square root of input size, + bounded below by 256. */ + too_expensive = 1; + for (; diags != 0; diags >>= 2) + too_expensive <<= 1; + too_expensive = MAX (256, too_expensive); + + files[0] = cmp->file[0]; + files[1] = cmp->file[1]; + + compareseq (0, cmp->file[0].nondiscarded_lines, + 0, cmp->file[1].nondiscarded_lines, minimal); + + free (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) + changes = 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, 0); + break; + + case OUTPUT_UNIFIED: + print_context_script (script, 1); + break; + + case OUTPUT_ED: + print_ed_script (script); + break; + + case OUTPUT_FORWARD_ED: + pr_forward_ed_script (script); + break; + + case OUTPUT_RCS: + print_rcs_script (script); + break; + + case OUTPUT_NORMAL: + print_normal_script (script); + break; + + case OUTPUT_IFDEF: + print_ifdef_script (script); + break; + + case OUTPUT_SDIFF: + print_sdiff_script (script); + 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/gnu/dist/diffutils/src/cmp.c b/gnu/dist/diffutils/src/cmp.c new file mode 100644 index 00000000000..d033829dfc6 --- /dev/null +++ b/gnu/dist/diffutils/src/cmp.c @@ -0,0 +1,676 @@ +/* $NetBSD: cmp.c,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */ + +/* cmp - compare two files byte by byte + + Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2001, + 2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" + +#include <stdio.h> +#include <cmpbuf.h> +#include <c-stack.h> +#include <error.h> +#include <exitfail.h> +#include <freesoft.h> +#include <getopt.h> +#include <hard-locale.h> +#include <inttostr.h> +#include <setmode.h> +#include <xalloc.h> +#include <xstrtol.h> + +#if defined LC_MESSAGES && ENABLE_NLS +# define hard_locale_LC_MESSAGES hard_locale (LC_MESSAGES) +#else +# define hard_locale_LC_MESSAGES 0 +#endif + +static char const authorship_msgid[] = + N_("Written by Torbjorn Granlund and David MacKenzie."); + +static char const copyright_string[] = + "Copyright (C) 2002 Free Software Foundation, Inc."; + +extern char const version_string[]; + +static int cmp (void); +static off_t file_position (int); +static size_t block_compare (word const *, word const *); +static size_t block_compare_and_count (word const *, word const *, off_t *); +static void sprintc (char *, unsigned char); + +/* Name under which this program was invoked. */ +char *program_name; + +/* 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_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"; + +/* Parse 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 off_t +parse_ignore_initial (char **argptr, char delimiter) +{ + uintmax_t val; + off_t o; + 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)) + || (o = val) < 0 || o != val || val == UINTMAX_MAX) + try_help ("invalid --ignore-initial value `%s'", arg); + return o; +} + +/* Specify the output format. */ +static void +specify_comparison_type (enum comparison_type t) +{ + if (comparison_type) + 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 SKIP --ignore-initial=SKIP Skip the first SKIP bytes of input."), + N_("-i SKIP1:SKIP2 --ignore-initial=SKIP1:SKIP2"), + N_(" Skip the first SKIP1 bytes of FILE1 and the first SKIP2 bytes of FILE2."), + N_("-l --verbose Output byte numbers and values of all differing bytes."), + N_("-n LIMIT --bytes=LIMIT Compare at most LIMIT bytes."), + N_("-s --quiet --silent Output nothing; yield exit status only."), + N_("-v --version Output version info."), + N_("--help Output this help."), + 0 +}; + +static void +usage (void) +{ + char const * const *p; + + printf (_("Usage: %s [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\n"), + program_name); + printf ("%s\n\n", _("Compare two files byte by byte.")); + for (p = option_help_msgid; *p; p++) + printf (" %s\n", _(*p)); + printf ("\n%s\n%s\n\n%s\n\n%s\n", + _("SKIP1 and SKIP2 are the number of bytes to skip in each file."), + _("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."), + _("Report bugs to <bug-gnu-utils@gnu.org>.")); +} + +int +main (int argc, char **argv) +{ + int c, f, exit_status; + size_t words_per_buffer; + + exit_failure = EXIT_TROUBLE; + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + c_stack_action (c_stack_die); + + /* 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 = 1; + break; + + case 'i': + ignore_initial[0] = parse_ignore_initial (&optarg, ':'); + ignore_initial[1] = (*optarg++ == ':' + ? parse_ignore_initial (&optarg, 0) + : 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': + printf ("cmp %s\n%s\n\n%s\n\n%s\n", + version_string, copyright_string, + _(free_software_msgid), _(authorship_msgid)); + 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++]; + ignore_initial[f] = parse_ignore_initial (&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 ^ (strcmp (file[1], "-") == 0); + + /* Two files with the same name are identical. + But wait until we open the file once, for proper diagnostics. */ + if (f && file_name_cmp (file[0], file[1]) == 0) + return EXIT_SUCCESS; + + file_desc[f1] = (strcmp (file[f1], "-") == 0 + ? STDIN_FILENO + : open (file[f1], O_RDONLY, 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]); + } + + set_binary_mode (file_desc[f1], 1); + } + + /* 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 may assume `-s'. */ + + 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_status; + } + + /* If only a return code is needed, + and if both input descriptors are associated with plain files, + conclude that the files differ if they have different sizes + 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 != 0 && comparison_type != type_status) + 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 ret = EXIT_SUCCESS; + int f; + int offset_width; + + 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]); + + /* Insert sentinels for the block compare. */ + + buf0[read0] = ~buf1[read0]; + buf1[read1] = ~buf0[read1]; + + /* If the line number should be written for differing files, + compare the blocks and count the number of newlines + simultaneously. */ + first_diff = (comparison_type == type_first_diff + ? block_compare_and_count (buffer0, buffer1, &line_number) + : block_compare (buffer0, buffer1)); + + byte_number += first_diff; + smaller = MIN (read0, read1); + + 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 + : "%s %s differ: char %s, line %s\n"), + 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); + ret = EXIT_FAILURE; + break; + } + } + + if (read0 != read1) + { + if (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 (read0 == buf_size); + + return ret; +} + +/* Compare two blocks of memory P0 and P1 until they differ, + and count the number of '\n' occurrences in the common + part of P0 and P1. + If the blocks are not guaranteed to be different, put sentinels at the ends + of the blocks before calling this function. + + Return the offset of the first byte that differs. + Increment *COUNT by the count of '\n' occurrences. */ + +static size_t +block_compare_and_count (word const *p0, word const *p1, off_t *count) +{ + word l; /* One word from first buffer. */ + word const *l0, *l1; /* Pointers into each buffer. */ + char const *c0, *c1; /* Pointers for finding exact address. */ + size_t cnt = 0; /* Number of '\n' occurrences. */ + word nnnn; /* Newline, sizeof (word) times. */ + int i; + + nnnn = 0; + for (i = 0; i < sizeof nnnn; i++) + nnnn = (nnnn << CHAR_BIT) | '\n'; + + /* Find the rough position of the first difference by reading words, + not bytes. */ + + for (l0 = p0, l1 = p1; (l = *l0) == *l1; l0++, l1++) + { + l ^= nnnn; + for (i = 0; i < sizeof l; i++) + { + cnt += ! (unsigned char) l; + l >>= CHAR_BIT; + } + } + + /* Find the exact differing position (endianness independent). */ + + for (c0 = (char const *) l0, c1 = (char const *) l1; + *c0 == *c1; + c0++, c1++) + cnt += *c0 == '\n'; + + *count += cnt; + return c0 - (char const *) p0; +} + +/* 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; +} + +/* 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] = 1; + position[f] = lseek (file_desc[f], ignore_initial[f], SEEK_CUR); + } + return position[f]; +} diff --git a/gnu/dist/diffutils/src/context.c b/gnu/dist/diffutils/src/context.c new file mode 100644 index 00000000000..0b29cd3e929 --- /dev/null +++ b/gnu/dist/diffutils/src/context.c @@ -0,0 +1,480 @@ +/* $NetBSD: context.c,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */ + +/* Context-format output routines for GNU DIFF. + + Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1998, 2001, + 2002 Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "diff.h" +#include <inttostr.h> +#include <regex.h> + +#ifdef ST_MTIM_NSEC +# define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC) +#else +# define TIMESPEC_NS(timespec) 0 +#endif + +size_t nstrftime (char *, size_t, char const *, struct tm const *, int, int); + +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 *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 = TIMESPEC_NS (inf->stat.st_mtim); + if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec))) + { + long sec = inf->stat.st_mtime; + verify (info_preserved, sizeof inf->stat.st_mtime <= sizeof sec); + sprintf (buf, "%ld.%.9d", sec, nsec); + } + fprintf (outfile, "%s %s\t%s\n", mark, inf->name, buf); + } +} + +/* Print a header for a context diff, with the file names and dates. */ + +void +print_context_header (struct file_data inf[], bool unidiff) +{ + if (unidiff) + { + print_context_label ("---", &inf[0], file_label[0]); + print_context_label ("+++", &inf[1], file_label[1]); + } + else + { + print_context_label ("***", &inf[0], file_label[0]); + print_context_label ("---", &inf[1], file_label[1]); + } +} + +/* Print an edit script in context format. */ + +void +print_context_script (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 = 0; + } + + 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 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; + putc (' ', out); + for (i = 0; i < 40 && function[i] != '\n'; i++) + continue; + fwrite (function, 1, 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 = 0; + if (function_regexp.fastmap) + function = find_function (files[0].linbuf, first0); + + begin_output (); + out = outfile; + + fprintf (out, "***************"); + + if (function) + print_context_function (out, function); + + fprintf (out, "\n*** "); + print_context_number_range (&files[0], first0, last0); + fprintf (out, " ****\n"); + + if (changes & OLD) + { + struct change *next = hunk; + + for (i = first0; i <= last0; i++) + { + /* Skip past changes that apply (in file 0) + only to lines before line I. */ + + while (next && next->line0 + next->deleted <= i) + next = next->link; + + /* Compute the marking for line I. */ + + prefix = " "; + if (next && next->line0 <= i) + /* The change NEXT covers this line. + If lines were inserted here in file 1, this is "changed". + Otherwise it is "deleted". */ + prefix = (next->inserted > 0 ? "!" : "-"); + + print_1_line (prefix, &files[0].linbuf[i]); + } + } + + fprintf (out, "--- "); + print_context_number_range (&files[1], first1, last1); + fprintf (out, " ----\n"); + + if (changes & NEW) + { + struct change *next = hunk; + + for (i = first1; i <= last1; i++) + { + /* Skip past changes that apply (in file 1) + only to lines before line I. */ + + while (next && next->line1 + next->inserted <= i) + next = next->link; + + /* Compute the marking for line I. */ + + prefix = " "; + if (next && next->line1 <= i) + /* The change NEXT covers this line. + If lines were deleted here in file 0, this is "changed". + Otherwise it is "inserted". */ + prefix = (next->deleted > 0 ? "!" : "+"); + + print_1_line (prefix, &files[1].linbuf[i]); + } + } +} + +/* Print a pair of line numbers with a comma, translated for file FILE. + If the second number is smaller, use the first in place of it. + If the numbers are equal, print just one number. + + Args A and B are internal line numbers. + We print the translated (real) line numbers. */ + +static void +print_unidiff_number_range (struct file_data const *file, lin a, lin b) +{ + long 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. */ + 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 = 0; + if (function_regexp.fastmap) + function = find_function (files[0].linbuf, first0); + + begin_output (); + out = outfile; + + fprintf (out, "@@ -"); + print_unidiff_number_range (&files[0], first0, last0); + fprintf (out, " +"); + print_unidiff_number_range (&files[1], first1, last1); + fprintf (out, " @@"); + + if (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) + { + putc (initial_tab ? '\t' : ' ', out); + print_1_line (0, &files[0].linbuf[i++]); + j++; + } + else + { + /* For each difference, first output the deleted part. */ + + k = next->deleted; + while (k--) + { + putc ('-', out); + if (initial_tab) + putc ('\t', out); + print_1_line (0, &files[0].linbuf[i++]); + } + + /* Then output the inserted part. */ + + k = next->inserted; + while (k--) + { + putc ('+', out); + if (initial_tab) + putc ('\t', out); + print_1_line (0, &files[1].linbuf[j++]); + } + + /* We're done with this hunk, so on to the next! */ + + next = next->link; + } + } +} + +/* Scan a (forward-ordered) edit script for the first place that more than + 2*CONTEXT unchanged lines appear, and return a pointer + to the `struct change' for the last change before those lines. */ + +static struct change * +find_hunk (struct change *start) +{ + struct change *prev; + lin top0, top1; + lin thresh; + + /* Threshold distance is 2 * CONTEXT + 1 between two non-ignorable + changes, but only CONTEXT if one is ignorable. Watch out for + integer overflow, though. */ + lin non_ignorable_threshold = + (LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1; + lin ignorable_threshold = context; + + 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 = (prev->ignore || (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 = 0; + + /* 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 0 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, 0)) + { + 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 0; +} diff --git a/gnu/dist/diffutils/src/diff.c b/gnu/dist/diffutils/src/diff.c new file mode 100644 index 00000000000..e087fa76969 --- /dev/null +++ b/gnu/dist/diffutils/src/diff.c @@ -0,0 +1,1365 @@ +/* $NetBSD: diff.c,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */ + +/* diff - compare files line by line + + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1996, 1998, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU DIFF; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define GDIFF_MAIN +#include "diff.h" +#include <c-stack.h> +#include <dirname.h> +#include <error.h> +#include <exclude.h> +#include <exitfail.h> +#include <fnmatch.h> +#include <freesoft.h> +#include <getopt.h> +#include <hard-locale.h> +#include <prepargs.h> +#include <quotesys.h> +#include <regex.h> +#include <setmode.h> +#include <xalloc.h> + +static char const authorship_msgid[] = + N_("Written by Paul Eggert, Mike Haertel, David Hayes,\n\ +Richard Stallman, and Len Tower."); + +static char const copyright_string[] = + "Copyright (C) 2002 Free Software Foundation, Inc."; + +#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 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 HAVE_SETMODE_DOS +/* Use binary I/O when reading and writing data (--binary). + On POSIX hosts, this has no effect. */ +static bool binary; +#endif + +/* When comparing directories, if a file appears only in one + directory, treat it as present but empty in the other (-N). + Then `patch' would create the file with appropriate contents. */ +static bool new_file; + +/* When comparing directories, if a file appears only in the second + directory of the two, treat it as present but empty in the other + (--unidirectional-new-file). + Then `patch' would create the file with appropriate contents. */ +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; + + +/* 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 + quote_system_arg ((char *) 0, optionvec[i]); + + p = result = xmalloc (size); + + for (i = 0; i < count; i++) + { + *p++ = ' '; + p += quote_system_arg (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); +} + +static char const shortopts[] = +"0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y"; + +/* 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_IGNORE_FILE_NAME_CASE_OPTION, + NORMAL_OPTION, + SDIFF_MERGE_ASSIST_OPTION, + STRIP_TRAILING_CR_OPTION, + SUPPRESS_COMMON_LINES_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 +}; + +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}, + {"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'}, + {"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-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'}, + {"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-common-lines", 0, 0, SUPPRESS_COMMON_LINES_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'}, + {0, 0, 0, 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 = 0; + int width = 0; + bool show_c_function = 0; + char const *from_file = 0; + char const *to_file = 0; + uintmax_t numval; + char *numend; + + /* Do our initializations. */ + exit_failure = 2; + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + c_stack_action (c_stack_die); + 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, 0)) != -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': + if (! ISDIGIT (prev)) + ocontext = c - '0'; + else if (LIN_MAX / 10 < ocontext + || ((ocontext = 10 * ocontext + c - '0') < 0)) + ocontext = LIN_MAX; + break; + + case 'a': + text = 1; + break; + + case 'b': + if (ignore_white_space < IGNORE_SPACE_CHANGE) + ignore_white_space = IGNORE_SPACE_CHANGE; + break; + + case 'B': + ignore_blank_lines = 1; + break; + + case 'C': /* +context[=lines] */ + case 'U': /* +unified[=lines] */ + { + if (optarg) + { + numval = strtoumax (optarg, &numend, 10); + if (*numend) + try_help ("invalid context length `%s'", optarg); + if (LIN_MAX < numval) + numval = LIN_MAX; + } + else + numval = 3; + + specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); + if (context < numval) + context = numval; + explicit_context = 1; + } + break; + + case 'c': + specify_style (OUTPUT_CONTEXT); + if (context < 3) + context = 3; + break; + + case 'd': + minimal = 1; + 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; 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_TAB_EXPANSION) + 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 = 1; + break; + + case 'i': + ignore_case = 1; + break; + + case 'I': + add_regexp (&ignore_regexp_list, optarg); + break; + + case 'l': + if (!pr_program[0]) + try_help ("pagination not supported on this host", 0); + paginate = 1; +#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 = 1; + break; + + case 'p': + show_c_function = 1; + add_regexp (&function_regexp_list, "^[[:alpha:]$_]"); + break; + + case 'P': + unidirectional_new_file = 1; + break; + + case 'q': + brief = 1; + break; + + case 'r': + recursive = 1; + break; + + case 's': + report_identical_files = 1; + break; + + case 'S': + specify_value (&starting_file, optarg, "-S"); + break; + + case 't': + expand_tabs = 1; + break; + + case 'T': + initial_tab = 1; + break; + + case 'u': + specify_style (OUTPUT_UNIFIED); + if (context < 3) + context = 3; + break; + + case 'v': + printf ("diff %s\n%s\n\n%s\n\n%s\n", + version_string, copyright_string, + _(free_software_msgid), _(authorship_msgid)); + 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 <= INT_MAX) || *numend) + try_help ("invalid width `%s'", optarg); + if (width != numval) + { + if (width) + fatal ("conflicting width options"); + width = numval; + } + break; + + case BINARY_OPTION: +#if HAVE_SETMODE_DOS + binary = 1; + set_binary_mode (STDOUT_FILENO, 1); +#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 = 1; + break; + + case INHIBIT_HUNK_MERGE_OPTION: + /* This option is obsolete, but accept it for backward + compatibility. */ + break; + + case LEFT_COLUMN_OPTION: + left_column = 1; + break; + + case LINE_FORMAT_OPTION: + specify_style (OUTPUT_IFDEF); + for (i = 0; i < sizeof line_format / sizeof *line_format; i++) + specify_value (&line_format[i], optarg, "--line-format"); + break; + + case NO_IGNORE_FILE_NAME_CASE_OPTION: + ignore_file_name_case = 0; + break; + + case NORMAL_OPTION: + specify_style (OUTPUT_NORMAL); + break; + + case SDIFF_MERGE_ASSIST_OPTION: + specify_style (OUTPUT_SDIFF); + sdiff_merge_assist = 1; + break; + + case STRIP_TRAILING_CR_OPTION: + strip_trailing_cr = 1; + break; + + case SUPPRESS_COMMON_LINES_OPTION: + suppress_common_lines = 1; + 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; + + default: + try_help (0, 0); + } + prev = c; + } + + 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)) + time_format = "%Y-%m-%d %H:%M:%S.%N %z"; + else + { + /* See POSIX 1003.1-2001 for this format. */ + time_format = "%a %b %e %T %Y"; + } + + if (0 <= ocontext) + { + bool modern_usage = 200112 <= posix2_version (); + + if ((output_style == OUTPUT_CONTEXT + || output_style == OUTPUT_UNIFIED) + && (context < ocontext + || (ocontext < context && ! explicit_context))) + { + if (modern_usage) + { + error (0, 0, + _("`-%ld' option is obsolete; use `-%c %ld'"), + (long) ocontext, + output_style == OUTPUT_CONTEXT ? 'C' : 'U', + (long) ocontext); + try_help (0, 0); + } + context = ocontext; + } + else + { + if (modern_usage) + { + error (0, 0, _("`-%ld' option is obsolete; omit it"), + (long) ocontext); + try_help (0, 0); + } + } + } + + { + /* + * We maximize first the half line width, and then the gutter width, + * according to the following constraints: + * 1. Two half lines plus a gutter must fit in a line. + * 2. If the half line width is nonzero: + * a. The gutter width is at least GUTTER_WIDTH_MINIMUM. + * b. If tabs are not expanded to spaces, + * a half line plus a gutter is an integral number of tabs, + * so that tabs in the right column line up. + */ + unsigned int t = expand_tabs ? 1 : TAB_WIDTH; + int w = width ? width : 130; + int off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t) * t; + sdiff_half_width = MAX (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; 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] + || (strcmp (group_format[UNCHANGED], "%=") == 0 + && !*line_format[UNCHANGED])) + : (output_style != OUTPUT_SDIFF) | suppress_common_lines); + + files_can_be_treated_as_binary = + (brief + & ~ (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 ((struct comparison *) 0, + from_file, argv[optind]); + if (exit_status < status) + exit_status = status; + } + } + else + { + if (to_file) + for (; optind < argc; optind++) + { + int status = compare_files ((struct comparison *) 0, + 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 ((struct comparison *) 0, + 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 != 0) + 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_("Compare files line by line."), + "", + N_("-i --ignore-case Ignore case differences in file contents."), + N_("--ignore-file-name-case Ignore case when comparing file names."), + N_("--no-ignore-file-name-case Consider case when comparing file names."), + N_("-E --ignore-tab-expansion Ignore changes due to tab expansion."), + 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 RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."), + N_("--strip-trailing-cr Strip trailing carriage return on input."), +#if HAVE_SETMODE_DOS + N_("--binary Read and write data in binary mode."), +#endif + N_("-a --text Treat all files as text."), + "", + 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\ + --label LABEL Use LABEL instead of file name.\n\ + -p --show-c-function Show which C function each change is in.\n\ + -F RE --show-function-line=RE Show the most recent line matching RE."), + N_("-q --brief Output only whether files differ."), + N_("-e --ed Output an ed script."), + N_("--normal Output a normal diff."), + N_("-n --rcs Output an RCS format diff."), + N_("-y --side-by-side Output in two columns.\n\ + -W NUM --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_("-D NAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs."), + N_("--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT."), + N_("--line-format=LFMT Similar, but format all input lines with LFMT."), + N_("--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT."), + N_(" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'."), + N_(" GFMT 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_(" LFMT 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_(" Either GFMT or LFMT may contain:\n\ + %% %\n\ + %c'C' the single character C\n\ + %c'\\OOO' the character with octal code OOO"), + "", + N_("-l --paginate Pass the output through `pr' to paginate it."), + N_("-t --expand-tabs Expand tabs to spaces in output."), + N_("-T --initial-tab Make tabs line up by prepending a tab."), + "", + N_("-r --recursive Recursively compare any subdirectories found."), + N_("-N --new-file Treat absent files as empty."), + N_("--unidirectional-new-file Treat absent first files as empty."), + N_("-s --report-identical-files Report when two files are the same."), + N_("-x PAT --exclude=PAT Exclude files that match PAT."), + N_("-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE."), + N_("-S FILE --starting-file=FILE Start with FILE when comparing directories."), + N_("--from-file=FILE1 Compare FILE1 to all operands. FILE1 can be a directory."), + N_("--to-file=FILE2 Compare all operands to FILE2. FILE2 can be a directory."), + "", + N_("--horizon-lines=NUM Keep NUM lines of the common prefix and suffix."), + N_("-d --minimal Try hard to find a smaller set of changes."), + N_("--speed-large-files Assume large files and many scattered small changes."), + "", + N_("-v --version Output version info."), + N_("--help Output this help."), + "", + 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 FILES."), + N_("If a FILE is `-', read standard input."), + "", + N_("Report bugs to <bug-gnu-utils@gnu.org>."), + 0 +}; + +static void +usage (void) +{ + char const * const *p; + + printf (_("Usage: %s [OPTION]... FILES\n"), program_name); + + 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); + } + } +} + +/* 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 && strcmp (*var, value) != 0) + { + error (0, 0, _("conflicting %s option value `%s'"), option, value); + try_help (0, 0); + } + *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", 0); + output_style = style; + } +} + +static char const * +filetype (struct stat const *st) +{ + /* See POSIX 1003.1-2001 for these formats. + + To keep diagnostics grammatical in English, the returned string + must start with a consonant. */ + + if (S_ISREG (st->st_mode)) + return st->st_size == 0 ? _("regular empty file") : _("regular file"); + + if (S_ISDIR (st->st_mode)) return _("directory"); + +#ifdef S_ISBLK + if (S_ISBLK (st->st_mode)) return _("block special file"); +#endif +#ifdef S_ISCHR + if (S_ISCHR (st->st_mode)) return _("character special file"); +#endif +#ifdef S_ISFIFO + if (S_ISFIFO (st->st_mode)) return _("fifo"); +#endif + /* S_ISLNK is impossible with `fstat' and `stat'. */ +#ifdef S_ISSOCK + if (S_ISSOCK (st->st_mode)) return _("socket"); +#endif +#ifdef S_TYPEISMQ + if (S_TYPEISMQ (st)) return _("message queue"); +#endif +#ifdef S_TYPEISSEM + if (S_TYPEISSEM (st)) return _("semaphore"); +#endif +#ifdef S_TYPEISSHM + if (S_TYPEISSHM (st)) return _("shared memory object"); +#endif +#ifdef S_TYPEISTMO + if (S_TYPEISTMO (st)) return _("typed memory object"); +#endif + + return _("weird file"); +} + +/* Set the last-modified time of *ST to be the current time. */ + +static void +set_mtime_to_now (struct stat *st) +{ +#ifdef ST_MTIM_NSEC + +# if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME + if (clock_gettime (CLOCK_REALTIME, &st->st_mtim) == 0) + return; +# endif + +# if HAVE_GETTIMEOFDAY + { + struct timeval timeval; + if (gettimeofday (&timeval, NULL) == 0) + { + st->st_mtime = timeval.tv_sec; + st->st_mtim.ST_MTIM_NSEC = timeval.tv_usec * 1000; + return; + } + } +# endif + +#endif /* ST_MTIM_NSEC */ + + time (&st->st_mtime); +} + +/* Compare two files (or dirs) with parent comparison PARENT + and names NAME0 and NAME1. + (If PARENT is 0, 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, *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 == 0 ? name1 : name0; + char const *dir = parent->file[name0 == 0].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 == 0 ? NONEXISTENT : UNOPENED; + cmp.file[1].desc = name1 == 0 ? NONEXISTENT : UNOPENED; + + /* Now record the full name of each file, including nonexistent ones. */ + + if (name0 == 0) + name0 = name1; + if (name1 == 0) + name1 = name0; + + if (!parent) + { + free0 = 0; + free1 = 0; + cmp.file[0].name = name0; + cmp.file[1].name = name1; + } + else + { + cmp.file[0].name = free0 + = dir_file_pathname (parent->file[0].name, name0); + cmp.file[1].name = free1 + = dir_file_pathname (parent->file[1].name, name1); + } + + /* 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 (strcmp (cmp.file[f].name, "-") == 0) + { + cmp.file[f].desc = STDIN_FILENO; + 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, (off_t) 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 (stat (cmp.file[f].name, &cmp.file[f].stat) != 0) + cmp.file[f].desc = ERRNO_ENCODE (errno); + } + } + + /* Mark files as nonexistent at the top level as needed for -N and + --unidirectional-new-file. */ + if (! parent) + { + if ((new_file | unidirectional_new_file) + && cmp.file[0].desc == ERRNO_ENCODE (ENOENT) + && cmp.file[1].desc == UNOPENED) + cmp.file[0].desc = NONEXISTENT; + + if (new_file + && cmp.file[0].desc == UNOPENED + && cmp.file[1].desc == ERRNO_ENCODE (ENOENT)) + cmp.file[1].desc = NONEXISTENT; + } + + for (f = 0; f < 2; f++) + if (cmp.file[f].desc == NONEXISTENT) + 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 + = dir_file_pathname (dir, base_name (fnm)); + + if (strcmp (fnm, "-") == 0) + fatal ("cannot compare `-' to a directory"); + + if (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 ((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_ISREG (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->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, + filetype (&cmp.file[0].stat), + file_label[1] ? file_label[1] : cmp.file[1].name, + filetype (&cmp.file[1].stat)); + + /* This is a difference. */ + status = EXIT_FAILURE; + } + } + else if (files_can_be_treated_as_binary + && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size + && (cmp.file[0].desc == NONEXISTENT + || S_ISREG (cmp.file[0].stat.st_mode)) + && (cmp.file[1].desc == NONEXISTENT + || S_ISREG (cmp.file[1].stat.st_mode))) + { + 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. */ + + if (cmp.file[0].desc == UNOPENED) + if ((cmp.file[0].desc = open (cmp.file[0].name, O_RDONLY, 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, O_RDONLY, 0)) + < 0) + { + perror_with_name (cmp.file[1].name); + status = EXIT_TROUBLE; + } + } + +#if HAVE_SETMODE_DOS + if (binary) + for (f = 0; f < 2; f++) + if (0 <= cmp.file[f].desc) + set_binary_mode (cmp.file[f].desc, 1); +#endif + + /* 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")); + } + + if (free0) + free (free0); + if (free1) + free (free1); + + return status; +} diff --git a/gnu/dist/diffutils/src/diff.h b/gnu/dist/diffutils/src/diff.h new file mode 100644 index 00000000000..deb561a6931 --- /dev/null +++ b/gnu/dist/diffutils/src/diff.h @@ -0,0 +1,377 @@ +/* $NetBSD: diff.h,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */ + +/* Shared definitions for GNU DIFF + + Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1998, 2001, + 2002 Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" +#include <stdio.h> + +#define TAB_WIDTH 8 + +/* 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 +}; + +/* 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; + +/* 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. */ +XTERN enum +{ + /* All white space is significant (the default). */ + IGNORE_NO_WHITE_SPACE, + + /* Ignore changes due to tab expansion (-E). */ + IGNORE_TAB_EXPANSION, + + /* Ignore changes in horizontal white space (-b). */ + IGNORE_SPACE_CHANGE, + + /* Ignore all horizontal white space (-w). */ + IGNORE_ALL_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; + +/* 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; + +/* 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; + +/* 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 unsigned int sdiff_half_width; +XTERN unsigned int 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; + +/* Name of program the user invoked (for error messages). */ +XTERN char *program_name; + +/* 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 TRUE for a line that is an insertion or a deletion. + The results of comparison are stored here. */ + bool *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 */ +int diff_2_files (struct comparison *); + +/* context.c */ +void print_context_header (struct file_data[], bool); +void print_context_script (struct change *, bool); + +/* dir.c */ +int diff_dirs (struct comparison const *, int (*) (struct comparison const *, char const *, char const *)); + +/* ed.c */ +void print_ed_script (struct change *); +void pr_forward_ed_script (struct change *); + +/* ifdef.c */ +void print_ifdef_script (struct change *); + +/* io.c */ +void file_block_read (struct file_data *, size_t); +bool read_files (struct file_data[], bool); + +/* normal.c */ +void print_normal_script (struct change *); + +/* rcs.c */ +void print_rcs_script (struct change *); + +/* side.c */ +void print_sdiff_script (struct change *); + +/* util.c */ +extern char const change_letter[4]; +extern char const pr_program[]; +char *concat (char const *, char const *, char const *); +char *dir_file_pathname (char const *, char const *); +bool lines_differ (char const *, char const *); +lin translate_line_number (struct file_data const *, lin); +struct change *find_change (struct change *); +struct change *find_reverse_change (struct change *); +void *zalloc (size_t); +enum changes analyze_hunk (struct change *, lin *, lin *, lin *, lin *); +void begin_output (void); +void debug_script (struct change *); +void fatal (char const *) __attribute__((noreturn)); +void finish_output (void); +void message (char const *, char const *, char const *); +void message5 (char const *, char const *, char const *, char const *, char const *); +void output_1_line (char const *, char const *, char const *, char const *); +void perror_with_name (char const *); +void pfatal_with_name (char const *) __attribute__((noreturn)); +void print_1_line (char const *, char const * const *); +void print_message_queue (void); +void print_number_range (char, struct file_data *, lin, lin); +void print_script (struct change *, struct change * (*) (struct change *), void (*) (struct change *)); +void setup_output (char const *, char const *, bool); +void translate_range (struct file_data const *, lin, lin, long *, long *); + +/* version.c */ +extern char const version_string[]; diff --git a/gnu/dist/diffutils/src/diff3.c b/gnu/dist/diffutils/src/diff3.c new file mode 100644 index 00000000000..33a680b3d9d --- /dev/null +++ b/gnu/dist/diffutils/src/diff3.c @@ -0,0 +1,1747 @@ +/* $NetBSD: diff3.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* diff3 - compare three files line by line + + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998, 2001, + 2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" + +static char const copyright_string[] = + "Copyright (C) 2002 Free Software Foundation, Inc."; + +static char const authorship_msgid[] = N_("Written by Randy Smith."); + +#include <c-stack.h> +#include <cmpbuf.h> +#include <error.h> +#include <exitfail.h> +#include <freesoft.h> +#include <getopt.h> +#include <inttostr.h> +#include <quotesys.h> +#include <stdio.h> +#include <xalloc.h> + +extern char const version_string[]; + +/* + * Internal data structures and macros for the diff3 program; includes + * data structures for both diff3 diffs and normal diffs. + */ + +/* Different files within a three way diff. */ +#define FILE0 0 +#define FILE1 1 +#define FILE2 2 + +/* + * A three way diff is built from two two-way diffs; the file which + * the two two-way diffs share is: + */ +#define FILEC FILE2 + +/* + * Different files within a two way diff. + * FC is the common file, FO the other file. + */ +#define FO 0 +#define FC 1 + +/* The ranges are indexed by */ +#define 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; +}; + +/* 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; + +/* 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; + +char *program_name; + +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 **); +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 +}; + +static struct option const longopts[] = +{ + {"text", 0, 0, 'a'}, + {"show-all", 0, 0, 'A'}, + {"ed", 0, 0, 'e'}, + {"diff-program", 1, 0, DIFF_PROGRAM_OPTION}, + {"show-overlap", 0, 0, 'E'}, + {"label", 1, 0, 'L'}, + {"merge", 0, 0, 'm'}, + {"initial-tab", 0, 0, 'T'}, + {"overlap-only", 0, 0, 'x'}, + {"easy-only", 0, 0, '3'}, + {"version", 0, 0, 'v'}, + {"help", 0, 0, HELP_OPTION}, + {0, 0, 0, 0} +}; + +/* + * Main program. Calls diff twice on two pairs of input files, + * combines the two diffs, and outputs them. + */ +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 = 2; + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + c_stack_action (c_stack_die); + + while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1) + { + switch (c) + { + case 'a': + text = 1; + break; + case 'A': + show_2nd = 1; + flagging = 1; + incompat++; + break; + case 'x': + overlap_only = 1; + incompat++; + break; + case '3': + simple_only = 1; + incompat++; + break; + case 'i': + finalwrite = 1; + break; + case 'm': + merge = 1; + break; + case 'X': + overlap_only = 1; + /* Fall through. */ + case 'E': + flagging = 1; + /* Fall through. */ + case 'e': + incompat++; + break; + case 'T': + initial_tab = 1; + break; + case 'v': + printf ("diff3 %s\n%s\n\n%s\n\n%s\n", + version_string, copyright_string, + _(free_software_msgid), _(authorship_msgid)); + 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 (strcmp (file[common], "-") == 0) + { + /* 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 (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0) + 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 (strcmp (file[i], "-") != 0) + { + 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 + + commonname = file[rev_mapping[FILEC]]; + thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block); + thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block); + diff3 = make_3way_diff (thread0, thread1); + if (edscript) + conflicts_found + = output_diff3_edscript (stdout, diff3, mapping, rev_mapping, + tag_strings[0], tag_strings[1], tag_strings[2]); + else if (merge) + { + if (! freopen (file[rev_mapping[FILE0]], "r", stdin)) + perror_with_exit (file[rev_mapping[FILE0]]); + conflicts_found + = output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping, + tag_strings[0], tag_strings[1], tag_strings[2]); + if (ferror (stdin)) + fatal ("read failed"); + } + else + { + output_diff3 (stdout, diff3, mapping, rev_mapping); + conflicts_found = 0; + } + + check_stdout (); + exit (conflicts_found); + return 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_("-e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE."), + N_("-E --show-overlap Output unmerged changes, bracketing conflicts."), + N_("-A --show-all Output all changes, bracketing conflicts."), + N_("-x --overlap-only Output overlapping changes."), + N_("-X Output overlapping changes, bracketing them."), + N_("-3 --easy-only Output unmerged nonoverlapping changes."), + "", + N_("-m --merge Output merged file instead of ed script (default -A)."), + N_("-L LABEL --label=LABEL Use LABEL instead of file name."), + N_("-i Append `w' and `q' commands to ed scripts."), + N_("-a --text Treat all files as text."), + N_("-T --initial-tab Make tabs line up by prepending a tab."), + N_("--diff-program=PROGRAM Use PROGRAM to compare files."), + "", + N_("-v --version Output version info."), + N_("--help Output this help."), + 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.")); + for (p = option_help_msgid; *p; p++) + if (**p) + printf (" %s\n", _(*p)); + else + putchar ('\n'); + printf ("\n%s\n\n%s\n", + _("If a FILE is `-', read standard input."), + _("Report bugs to <bug-gnu-utils@gnu.org>.")); +} + +/* + * Routines that combine the two diffs together into one. The + * algorithm used follows: + * + * File2 is shared in common between the two diffs. + * Diff02 is the diff between 0 and 2. + * Diff12 is the diff between 1 and 2. + * + * 1) Find the range for the first block in File2. + * a) Take the lowest of the two ranges (in File2) in the two + * current blocks (one from each diff) as being the low + * water mark. Assign the upper end of this block as + * being the high water mark and move the current block up + * one. Mark the block just moved over as to be used. + * b) Check the next block in the diff that the high water + * mark is *not* from. + * + * *If* the high water mark is above + * the low end of the range in that block, + * + * mark that block as to be used and move the current + * block up. Set the high water mark to the max of + * the high end of this block and the current. Repeat b. + * + * 2) Find the corresponding ranges in File0 (from the blocks + * in diff02; line per line outside of diffs) and in File1. + * Create a diff3_block, reserving space as indicated by the ranges. + * + * 3) Copy all of the pointers for file2 in. At least for now, + * do memcmp's between corresponding strings in the two diffs. + * + * 4) Copy all of the pointers for file0 and 1 in. Get what you + * need from file2 (when there isn't a diff block, it's + * identical to file2 within the range between diff blocks). + * + * 5) If the diff blocks you used came from only one of the two + * strings of diffs, then that file (i.e. the one other than + * the common file in that diff) is the odd person out. If you used + * diff blocks from both sets, check to see if files 0 and 1 match: + * + * Same number of lines? If so, do a set of memcmp's (if a + * memcmp matches; copy the pointer over; it'll be easier later + * if you have to do any compares). If they match, 0 & 1 are + * the same. If not, all three different. + * + * Then you do it again, until you run out of blocks. + * + */ + +/* + * This routine makes a three way diff (chain of diff3_block's) from two + * two way diffs (chains of diff_block's). It is assumed that each of + * the two diffs passed are onto the same file (i.e. that each of the + * diffs were made "to" the same file). The three way diff pointer + * returned will have numbering FILE0--the other file in diff02, + * FILE1--the other file in diff12, and FILEC--the common file. + */ +static struct diff3_block * +make_3way_diff (struct diff_block *thread0, struct diff_block *thread1) +{ +/* + * This routine works on the two diffs passed to it as threads. + * Thread number 0 is diff02, thread number 1 is diff12. The USING + * array is set to the base of the list of blocks to be used to + * construct each block of the three way diff; if no blocks from a + * particular thread are to be used, that element of the using array + * is set to 0. The elements LAST_USING array are set to the last + * elements on each of the using lists. + * + * The HIGH_WATER_MARK is set to the highest line number in the common file + * described in any of the diffs in either of the USING lists. The + * HIGH_WATER_THREAD names the thread. Similarly the BASE_WATER_MARK + * and BASE_WATER_THREAD describe the lowest line number in the common file + * described in any of the diffs in either of the USING lists. The + * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was + * taken. + * + * The HIGH_WATER_DIFF should always be equal to LAST_USING + * [HIGH_WATER_THREAD]. The OTHER_DIFF is the next diff to check for + * higher water, and should always be equal to + * CURRENT[HIGH_WATER_THREAD ^ 0x1]. The OTHER_THREAD is the thread + * in which the OTHER_DIFF is, and hence should always be equal to + * HIGH_WATER_THREAD ^ 0x1. + * + * The variable LAST_DIFF is kept set to the last diff block produced + * by this routine, for line correspondence purposes between that diff + * and the one currently being worked on. It is initialized to + * ZERO_DIFF before any blocks have been created. + */ + + struct diff_block *using[2]; + 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_diff = other_diff; + high_water_mark = D_HIGHLINE (other_diff, FC); + } + + /* Set the other diff */ + other_thread = high_water_thread ^ 0x1; + other_diff = current[other_thread]; + } + + /* The using lists contain a list of all of the blocks to be + included in this diff3_block. Create it. */ + + tmpblock = using_to_diff3_block (using, last_using, + base_water_thread, high_water_thread, + last_diff3); + + if (!tmpblock) + fatal ("internal error: screwup in format of diff blocks"); + + /* Put it on the list. */ + *result_end = tmpblock; + result_end = &tmpblock->next; + + /* Set up corresponding lines correctly. */ + last_diff3 = tmpblock; + } + return result; +} + +/* + * using_to_diff3_block: + * This routine takes two lists of blocks (from two separate diff + * threads) and puts them together into one diff3 block. + * It then returns a pointer to this diff3 block or 0 for failure. + * + * All arguments besides using are for the convenience of the routine; + * they could be derived from the using array. + * LAST_USING is a pair of pointers to the last blocks in the using + * structure. + * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest + * and highest line numbers for File0. + * last_diff3 contains the last diff produced in the calling routine. + * This is used for lines mappings which would still be identical to + * the state that diff ended in. + * + * A distinction should be made in this routine between the two diffs + * that are part of a normal two diff block, and the three diffs that + * are part of a diff3_block. + */ +static struct diff3_block * +using_to_diff3_block (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; +} + +/* + * This routine copies pointers from a list of strings to a different list + * of strings. If a spot in the second list is already filled, it + * makes sure that it is filled with the same string; if not it + * returns 0, the copy incomplete. + * Upon successful completion of the copy, it returns 1. + */ +static 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)) return 0; } + else + { *t = *f ; *tl = *fl; } + + t++; f++; tl++; fl++; + } + return 1; +} + +/* + * Create a diff3_block, with ranges as specified in the arguments. + * Allocate the arrays for the various pointers (and zero them) based + * on the arguments passed. Return the block as a result. + */ +static struct diff3_block * +create_diff3_block (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, + * const *l2 = list2; + size_t const + *lgths1 = lengths1, + *lgths2 = lengths2; + + while (nl--) + if (!*l1 || !*l2 || *lgths1 != *lgths2++ + || memcmp (*l1++, *l2++, *lgths1++)) + return 0; + return 1; +} + +/* + * Routines to input and parse two way diffs. + */ + +static struct diff_block * +process_diff (char const *filea, + char const *fileb, + struct diff_block **last_block) +{ + char *diff_contents; + char *diff_limit; + char *scan_diff; + enum diff_type dt; + lin i; + struct diff_block *block_list, **block_list_end, *bptr; + size_t too_many_lines = (PTRDIFF_MAX + / MIN (sizeof *bptr->lines[1], + sizeof *bptr->lengths[1])); + + diff_limit = read_diff (filea, fileb, &diff_contents); + scan_diff = diff_contents; + block_list_end = &block_list; + bptr = 0; /* Pacify `gcc -W'. */ + + while (scan_diff < diff_limit) + { + bptr = 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 = 0; + *last_block = bptr; + return block_list; +} + +/* + * This routine will parse a normal format diff control string. It + * returns the type of the diff (ERROR if the format is bad). All of + * the other important information is filled into to the structure + * pointed to by db, and the string pointer (whose location is passed + * to this routine) is updated to point beyond the end of the string + * parsed. Note that only the ranges in the diff_block will be set by + * this routine. + * + * If some specific pair of numbers has been reduced to a single + * number, then both corresponding numbers in the diff block are set + * to that number. In general these numbers are 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; + lin holdnum; + enum diff_type type; + +/* These macros are defined here because they can use variables + defined in this function. Don't try this at home kids, we're + trained professionals! + + Also note that SKIPWHITE only recognizes tabs and spaces, and + that READNUM can only read positive, integral numbers */ + +#define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; } +#define READNUM(s, num) \ + { unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \ + do { holdnum = (c - '0' + holdnum * 10); } \ + while (ISDIGIT (c = *++s)); (num) = holdnum; } + + /* Read first set of digits */ + SKIPWHITE (s); + READNUM (s, db->ranges[0][RANGE_START]); + + /* Was that the only digit? */ + SKIPWHITE (s); + if (*s == ',') + { + /* Get the next digit */ + s++; + READNUM (s, db->ranges[0][RANGE_END]); + } + else + db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START]; + + /* Get the letter */ + SKIPWHITE (s); + switch (*s) + { + case 'a': + type = ADD; + break; + case 'c': + type = CHANGE; + break; + case 'd': + type = DELETE; + break; + default: + return ERROR; /* Bad format */ + } + s++; /* Past letter */ + + /* Read second set of digits */ + SKIPWHITE (s); + READNUM (s, db->ranges[1][RANGE_START]); + + /* Was that the only digit? */ + SKIPWHITE (s); + if (*s == ',') + { + /* Get the next digit */ + s++; + READNUM (s, db->ranges[1][RANGE_END]); + 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; + int werrno = 0; + struct stat pipestat; + +#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK + + char const *argv[8]; + char const **ap; + int fds[2]; + pid_t pid; + + ap = argv; + *ap++ = diff_program; + if (text) + *ap++ = "-a"; + *ap++ = "--horizon-lines=100"; + *ap++ = "--"; + *ap++ = filea; + *ap++ = fileb; + *ap = 0; + + if (pipe (fds) != 0) + perror_with_exit ("pipe"); + + pid = vfork (); + if (pid == 0) + { + /* Child */ + close (fds[0]); + if (fds[1] != STDOUT_FILENO) + { + dup2 (fds[1], STDOUT_FILENO); + close (fds[1]); + } + + /* The cast to (char **) is needed for portability to older + hosts with a nonstandard prototype for execvp. */ + execvp (diff_program, (char **) argv); + + _exit (errno == ENOEXEC ? 126 : 127); + } + + if (pid == -1) + perror_with_exit ("fork"); + + close (fds[1]); /* Prevent erroneous lack of EOF */ + fd = fds[0]; + +#else + + FILE *fpipe; + char const args[] = " -a --horizon-lines=100 -- "; + char *command = xmalloc (quote_system_arg (0, diff_program) + + sizeof args - 1 + + quote_system_arg (0, filea) + 1 + + quote_system_arg (0, fileb) + 1); + char *p = command; + p += quote_system_arg (p, diff_program); + strcpy (p, args + (text ? 0 : 3)); + p += strlen (p); + p += quote_system_arg (p, filea); + *p++ = ' '; + p += quote_system_arg (p, fileb); + *p = 0; + 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 || HAVE_WORKING_VFORK) + + 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 + + if (! werrno && WIFEXITED (wstatus)) + switch (WEXITSTATUS (wstatus)) + { + case 126: + error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not executable"), + diff_program); + case 127: + error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not found"), + diff_program); + } + if (werrno || ! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2)) + error (EXIT_TROUBLE, werrno, _("subsidiary program `%s' failed"), + diff_program); + + 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; +} + +/* + * This routine outputs a three way diff passed as a list of + * diff3_block's. + * The argument MAPPING is indexed by external file number (in the + * argument list) and contains the internal file number (from the + * diff passed). This is important because the user expects his + * outputs in terms of the argument list number, and the diff passed + * may have been done slightly differently (if the last argument + * was "-", for example). + * REV_MAPPING is the inverse of MAPPING. + */ +static void +output_diff3 (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 llowt = lowt; + long 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 + { + fprintf (outputfile, line_prefix); + cp = D_RELNUM (ptr, realfile, line); + length = D_RELLEN (ptr, realfile, line); + fwrite (cp, sizeof (char), length, outputfile); + } + while (++line < hight - lowt + 1); + if (cp[length - 1] != '\n') + fprintf (outputfile, "\n\\ %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 = 0; + + for (i = 0; + i < D_NUMLINES (b, filenum); + i++) + { + char *line = D_RELNUM (b, filenum, i); + if (line[0] == '.') + { + leading_dot = 1; + fprintf (outputfile, "."); + } + fwrite (line, sizeof (char), + D_RELLEN (b, filenum, i), outputfile); + } + + return leading_dot; +} + +/* + * Output to OUTPUTFILE a '.' line. If LEADING_DOT is nonzero, + * also output a command that removes initial '.'s + * starting with line START and continuing for NUM lines. + * (START is long, not lin, for convenience with printf %ld formats.) + */ +static void +undotlines (FILE *outputfile, bool leading_dot, long start, lin num) +{ + fprintf (outputfile, ".\n"); + if (leading_dot) + { + if (num == 1) + fprintf (outputfile, "%lds/^\\.//\n", start); + else + fprintf (outputfile, "%ld,%lds/^\\.//\n", start, start + num - 1); + } +} + +/* + * This routine outputs a diff3 set of blocks as an ed script. This + * script applies the changes between file's 2 & 3 to file 1. It + * takes the precise format of the ed script to be output from global + * variables set during options processing. Note that it does + * destructive things to the set of diff3 blocks it is passed; it + * reverses their order (this gets around the problems involved with + * changing line numbers in an ed script). + * + * Note that this routine has the same problem of mapping as the last + * one did; the variable MAPPING maps from file number according to + * the argument list to file number according to the diff passed. All + * files listed below are in terms of the argument list. + * REV_MAPPING is the inverse of MAPPING. + * + * The arguments FILE0, FILE1 and FILE2 are the strings to print + * as the names of the three files. These may be the actual names, + * or may be the arguments specified with -L. + * + * Returns 1 if conflicts were found. + */ + +static 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 = 0, conflict; + struct diff3_block *b; + + for (b = reverse_diff3_blocklist (diff); b; b = b->next) + { + /* Must do mapping correctly. */ + enum diff_type type + = (b->correspond == DIFF_ALL + ? DIFF_ALL + : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]); + + long 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 = 1; break; + case DIFF_3RD: if (overlap_only) continue; conflict = 0; 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 = 1; + + + /* Mark end of conflict. */ + + fprintf (outputfile, "%lda\n", high0); + leading_dot = 0; + if (type == DIFF_ALL) + { + if (show_2nd) + { + /* Append lines from FILE1. */ + fprintf (outputfile, "||||||| %s\n", file1); + leading_dot = dotlines (outputfile, b, mapping[FILE1]); + } + /* Append lines from FILE2. */ + fprintf (outputfile, "=======\n"); + leading_dot |= dotlines (outputfile, b, mapping[FILE2]); + } + fprintf (outputfile, ">>>>>>> %s\n", file2); + undotlines (outputfile, leading_dot, 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 = 0; + if (type == DIFF_2ND) + { + /* Prepend lines from FILE1. */ + leading_dot = dotlines (outputfile, b, mapping[FILE1]); + fprintf (outputfile, "=======\n"); + } + 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) fprintf (outputfile, "w\nq\n"); + return conflicts_found; +} + +/* + * Read from INFILE and output to OUTPUTFILE a set of diff3_ blocks DIFF + * as a merged file. This acts like 'ed file0 <[output_diff3_edscript]', + * except that it works even for binary data or incomplete lines. + * + * As before, MAPPING maps from arg list file number to diff file number, + * REV_MAPPING is its inverse, + * and FILE0, FILE1, and FILE2 are the names of the files. + * + * Returns 1 if conflicts were found. + */ + +static 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 = 0, 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 = 1; break; + case DIFF_3RD: if (overlap_only) continue; conflict = 0; break; + case DIFF_ALL: if (simple_only) continue; conflict = flagging; + format_2nd = "||||||| %s\n"; + break; + } + + /* Copy I lines from file 0. */ + i = D_LOWLINE (b, FILE0) - linesread - 1; + linesread += i; + while (0 <= --i) + do + { + c = getc (infile); + if (c == EOF) + { + if (ferror (infile)) + perror_with_exit (_("read failed")); + else if (feof (infile)) + fatal ("input file shrank"); + } + putc (c, outputfile); + } + while (c != '\n'); + + if (conflict) + { + conflicts_found = 1; + + if (type == DIFF_ALL) + { + /* Put in lines from FILE0 with bracket. */ + fprintf (outputfile, "<<<<<<< %s\n", file0); + for (i = 0; + i < D_NUMLINES (b, mapping[FILE0]); + i++) + fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char), + D_RELLEN (b, mapping[FILE0], i), outputfile); + } + + if (show_2nd) + { + /* Put in lines from FILE1 with bracket. */ + fprintf (outputfile, format_2nd, file1); + for (i = 0; + i < D_NUMLINES (b, mapping[FILE1]); + i++) + fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char), + D_RELLEN (b, mapping[FILE1], i), outputfile); + } + + fprintf (outputfile, "=======\n"); + } + + /* Put in lines from FILE2. */ + for (i = 0; + i < D_NUMLINES (b, mapping[FILE2]); + i++) + fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char), + D_RELLEN (b, mapping[FILE2], i), outputfile); + + if (conflict) + fprintf (outputfile, ">>>>>>> %s\n", file2); + + /* Skip I lines in file 0. */ + i = D_NUMLINES (b, FILE0); + linesread += i; + while (0 <= --i) + while ((c = getc (infile)) != '\n') + if (c == EOF) + { + if (ferror (infile)) + perror_with_exit (_("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/gnu/dist/diffutils/src/dir.c b/gnu/dist/diffutils/src/dir.c new file mode 100644 index 00000000000..61410d07323 --- /dev/null +++ b/gnu/dist/diffutils/src/dir.c @@ -0,0 +1,291 @@ +/* $NetBSD: dir.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* Read, sort and compare two directories. Used for GNU DIFF. + + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "diff.h" +#include <error.h> +#include <exclude.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 strcoll. */ +static bool locale_specific_sorting; + +/* Where to go if strcoll fails. */ +static jmp_buf failed_strcoll; + +static bool dir_loop (struct comparison const *, int); +static int compare_names_for_qsort (void const *, void const *); + + +/* 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 0; + + /* 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 = NAMLEN (next) + 1; + + /* Ignore "." and "..". */ + if (d_name[0] == '.' + && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0))) + continue; + + if (excluded_filename (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 0; + } +#if CLOSEDIR_VOID + closedir (reading); +#else + if (closedir (reading) != 0) + return 0; +#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 1; +} + +/* Compare file names, returning a value compatible with strcmp. */ + +static int +compare_names (char const *name1, char const *name2) +{ + if (ignore_file_name_case) + { + int r = strcasecmp (name1, name2); + if (r) + return r; + } + + if (locale_specific_sorting) + { + int r; + errno = 0; + r = strcoll (name1, name2); + if (errno) + { + error (0, errno, _("cannot compare file names `%s' and `%s'"), + name1, name2); + longjmp (failed_strcoll, 1); + } + if (r) + return r; + } + + return file_name_cmp (name1, name2); +} + +/* A wrapper for compare_names suitable as an argument for qsort. */ + +static int +compare_names_for_qsort (void const *file1, void const *file2) +{ + char const *const *f1 = file1; + char const *const *f2 = file2; + return compare_names (*f1, *f2); +} + +/* 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 = 1; + if (setjmp (failed_strcoll)) + locale_specific_sorting = 0; + + /* 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])); + 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++) + { + if (dirdata[i].names) + free (dirdata[i].names); + if (dirdata[i].data) + free (dirdata[i].data); + } + + return val; +} + +/* Return nonzero if CMP is looping recursively in argument I. */ + +static bool +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 1; + return 0; +} diff --git a/gnu/dist/diffutils/src/ed.c b/gnu/dist/diffutils/src/ed.c new file mode 100644 index 00000000000..42e47edbbb4 --- /dev/null +++ b/gnu/dist/diffutils/src/ed.c @@ -0,0 +1,171 @@ +/* $NetBSD: ed.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* Output routines for ed-script format. + + Copyright (C) 1988, 1989, 1991, 1992, 1993, 1995, 1998, 2001 Free + Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#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); + fprintf (outfile, "%c\n", change_letter[changes]); + + /* Print new/changed lines from second file, if needed */ + if (changes != OLD) + { + lin i; + for (i = f1; i <= l1; i++) + { + 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, remove the extra dot, and then resume + insert mode. */ + fprintf (outfile, "..\n.\ns/.//\na\n"); + } + else + print_1_line ("", &files[1].linbuf[i]); + } + + fprintf (outfile, ".\n"); + } +} + +/* Print change script in the style of ed commands, + but print the changes in the order they appear in the input files, + which means that the commands are not truly useful with ed. */ + +void +pr_forward_ed_script (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 (); + + fprintf (outfile, "%c", change_letter[changes]); + print_number_range (' ', files, f0, l0); + fprintf (outfile, "\n"); + + /* 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]); + + fprintf (outfile, ".\n"); +} + +/* Print in a format somewhat like ed commands + except that each insert command states the number of lines it inserts. + This format is used for RCS. */ + +void +print_rcs_script (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 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) + { + fprintf (outfile, "d"); + /* For deletion, print just the starting line number from file 0 + and the number of lines deleted. */ + fprintf (outfile, "%ld %ld\n", tf0, tf0 <= tl0 ? tl0 - tf0 + 1 : 1); + } + + if (changes & NEW) + { + fprintf (outfile, "a"); + + /* Take last-line-number from file 0 and # lines from file 1. */ + translate_range (&files[1], f1, l1, &tf1, &tl1); + fprintf (outfile, "%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/gnu/dist/diffutils/src/ifdef.c b/gnu/dist/diffutils/src/ifdef.c new file mode 100644 index 00000000000..f8e3f4e4044 --- /dev/null +++ b/gnu/dist/diffutils/src/ifdef.c @@ -0,0 +1,426 @@ +/* $NetBSD: ifdef.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* #ifdef-format output routines for GNU DIFF. + + Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002 Free + Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. No author or distributor + accepts responsibility to anyone for the consequences of using it + or for whether it serves any particular purpose or works at all, + unless he says so in writing. Refer to the GNU DIFF General Public + License for full details. + + Everyone is granted permission to copy, modify and redistribute + GNU DIFF, but only under the conditions described in the + GNU DIFF General Public License. A copy of this license is + supposed to have been given to you along with GNU DIFF so you + can know your rights and responsibilities. It should be in a + file named COPYING. Among other things, the copyright notice + and this notice must be preserved on all copies. */ + +#include "diff.h" + +#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_line; + +/* Print the edit-script SCRIPT as a merged #ifdef file. */ + +void +print_ifdef_script (struct change *script) +{ + next_line = - files[0].prefix_lines; + print_script (script, find_change, print_ifdef_hunk); + if (next_line < files[0].valid_lines) + { + begin_output (); + format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines, + next_line - files[0].valid_lines + files[1].valid_lines, + files[1].valid_lines); + } +} + +/* Print a hunk of an ifdef diff. + This is a contiguous portion of a complete edit script, + describing changes in consecutive lines. */ + +static void +print_ifdef_hunk (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_line < first0) + format_ifdef (group_format[UNCHANGED], next_line, first0, + next_line - first0 + first1, first1); + + /* Print this change. */ + next_line = last0 + 1; + format_ifdef (group_format[changes], first0, next_line, first1, last1 + 1); +} + +/* Print a set of lines according to FORMAT. + Lines BEG0 up to END0 are from the first file; + lines BEG1 up to END1 are from the second file. */ + +static void +format_ifdef (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; + 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 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 zero 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 0; + + case '\\': + value = 0; + while ((c = *p++) != '\'') + { + unsigned int digit = c - '0'; + if (8 <= digit) + return 0; + value = 8 * value + digit; + } + digits = p - lit - 2; + if (! (1 <= digits && digits <= 3)) + return 0; + break; + + default: + value = c; + if (*p++ != '\'') + return 0; + break; + } + + *valptr = value; + return p; +} diff --git a/gnu/dist/diffutils/src/io.c b/gnu/dist/diffutils/src/io.c new file mode 100644 index 00000000000..47d75a535b6 --- /dev/null +++ b/gnu/dist/diffutils/src/io.c @@ -0,0 +1,857 @@ +/* $NetBSD: io.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* File I/O for GNU DIFF. + + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "diff.h" +#include <cmpbuf.h> +#include <regex.h> +#include <setmode.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 (hash_value_is_unsigned, ! 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); + + if (! skip_test) + { + /* Check first part of file to see if it's a binary file. */ + + bool was_binary = set_binary_mode (current->desc, 1); + off_t buffered; + file_block_read (current, current->bufsize); + buffered = current->buffered; + + if (! was_binary) + { + /* Revert to text mode and seek back to the beginning 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) == -1) + pfatal_with_name (current->name); + set_binary_mode (current->desc, 0); + current->buffered = 0; + current->eof = 0; + } + + return binary_file_p (current->buffer, buffered); + } + } + + current->buffered = 0; + current->eof = 0; + return 0; +} + +/* 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) +{ + hash_value h; + unsigned char const *p = (unsigned char const *) current->prefix_end; + unsigned char c; + 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 diff_length_compare_anyway = + ignore_white_space != IGNORE_NO_WHITE_SPACE; + bool same_length_diff_contents_compare_anyway = + diff_length_compare_anyway | ignore_case; + + while ((char const *) p < suffix_begin) + { + char const *ip = (char const *) p; + + h = 0; + + /* Hash this line until we find a newline. */ + if (ignore_case) + switch (ignore_white_space) + { + case IGNORE_ALL_SPACE: + while ((c = *p++) != '\n') + if (! ISSPACE (c)) + h = HASH (h, TOLOWER (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, TOLOWER (c)); + } + break; + + case IGNORE_TAB_EXPANSION: + { + size_t column = 0; + while ((c = *p++) != '\n') + { + int repetitions = 1; + + switch (c) + { + case '\b': + column -= 0 < column; + break; + + case '\t': + c = ' '; + repetitions = TAB_WIDTH - column % TAB_WIDTH; + column += repetitions; + break; + + case '\r': + column = 0; + break; + + default: + c = TOLOWER (c); + column++; + break; + } + + do + h = HASH (h, c); + while (--repetitions != 0); + } + } + break; + + default: + while ((c = *p++) != '\n') + h = HASH (h, TOLOWER (c)); + break; + } + else + switch (ignore_white_space) + { + case IGNORE_ALL_SPACE: + while ((c = *p++) != '\n') + if (! ISSPACE (c)) + h = HASH (h, 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, c); + } + break; + + case IGNORE_TAB_EXPANSION: + { + size_t column = 0; + while ((c = *p++) != '\n') + { + int repetitions = 1; + + switch (c) + { + case '\b': + column -= 0 < column; + break; + + case '\t': + c = ' '; + repetitions = TAB_WIDTH - column % TAB_WIDTH; + column += repetitions; + break; + + case '\r': + column = 0; + break; + + default: + column++; + break; + } + + do + h = HASH (h, c); + while (--repetitions != 0); + } + } + break; + + default: + while ((c = *p++) != '\n') + h = HASH (h, c); + break; + } + + hashing_done:; + + bucket = &buckets[h % nbuckets]; + length = (char const *) p - ip - 1; + + if ((char const *) p == bufend + && current->missing_newline + && ROBUST_OUTPUT_STYLE (output_style)) + { + /* This line is incomplete. If this is significant, + put the line into buckets[-1]. */ + if (ignore_white_space < IGNORE_SPACE_CHANGE) + bucket = &buckets[-1]; + + /* Omit the inserted newline when computing linbuf later. */ + p--; + bufend = suffix_begin = (char const *) p; + } + + for (i = *bucket; ; i = eqs[i].next) + if (!i) + { + /* Create a new equivalence class in this bucket. */ + i = eqs_index++; + if (i == eqs_alloc) + { + 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] = (char const *) p; + + if ((char const *) p == bufend) + 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); + char *dst; + + if (buffered == 0 || p[buffered - 1] == '\n') + current->missing_newline = 0; + else + { + p[buffered++] = '\n'; + current->missing_newline = 1; + } + + if (!p) + return; + + /* Don't use uninitialized storage when planting or using sentinels. */ + memset (p + buffered, 0, sizeof (word)); + + if (strip_trailing_cr && (dst = memchr (p, '\r', buffered))) + { + char const *src = dst; + char const *srclim = p + buffered; + + do + dst += ! ((*dst = *src++) == '\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; + 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. */ + for (; p0 != beg0; p0--, p1--) + if (*p0 != *p1) + { + /* Point at the first char of the matching suffix. */ + 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); + p0 = buffer0; + + /* If the prefix is needed, find the prefix lines. */ + if (! (no_diff_means_no_output + && filevec[0].prefix_end == p0 + && filevec[1].prefix_end == p1)) + { + 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 (enough_prime_offsets, + 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, 1); + set_binary_mode (filevec[1].desc, 1); + return 1; + } + + 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 0; +} diff --git a/gnu/dist/diffutils/src/normal.c b/gnu/dist/diffutils/src/normal.c new file mode 100644 index 00000000000..be0a216115d --- /dev/null +++ b/gnu/dist/diffutils/src/normal.c @@ -0,0 +1,73 @@ +/* $NetBSD: normal.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* Normal-format output routines for GNU DIFF. + + Copyright (C) 1988, 1989, 1993, 1995, 1998, 2001 Free Software + Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#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 */ + print_number_range (',', &files[0], first0, last0); + fprintf (outfile, "%c", change_letter[changes]); + print_number_range (',', &files[1], first1, last1); + fprintf (outfile, "\n"); + + /* Print the lines that the first file has. */ + if (changes & OLD) + for (i = first0; i <= last0; i++) + print_1_line ("<", &files[0].linbuf[i]); + + if (changes == CHANGED) + fprintf (outfile, "---\n"); + + /* Print the lines that the second file has. */ + if (changes & NEW) + for (i = first1; i <= last1; i++) + print_1_line (">", &files[1].linbuf[i]); +} diff --git a/gnu/dist/diffutils/src/sdiff.c b/gnu/dist/diffutils/src/sdiff.c new file mode 100644 index 00000000000..4c65992d9cd --- /dev/null +++ b/gnu/dist/diffutils/src/sdiff.c @@ -0,0 +1,1222 @@ +/* $NetBSD: sdiff.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* sdiff - side-by-side merge of file differences + + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002 Free + Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" + +#include <c-stack.h> +#include <dirname.h> +#include <error.h> +#include <exitfail.h> +#include <freesoft.h> +#include <getopt.h> +#include <quotesys.h> +#include <stdio.h> +#include <xalloc.h> + +static char const authorship_msgid[] = N_("Written by Thomas Lord."); + +static char const copyright_string[] = + "Copyright (C) 2002 Free Software Foundation, Inc."; + +extern char const version_string[]; + +/* Size of chunks read from files which must be parsed into lines. */ +#define SDIFF_BUFSIZE ((size_t) 65536) + +char *program_name; + +static char const *editor_program = DEFAULT_EDITOR_PROGRAM; +static char const **diffargv; + +static char * volatile tmpname; +static FILE *tmp; + +#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK +static pid_t volatile diffpid; +#endif + +struct line_filter; + +static RETSIGTYPE 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); + +#define NUM_SIGS (sizeof sigs / sizeof *sigs) +static int const sigs[] = { +#ifdef SIGHUP + SIGHUP, +#endif +#ifdef SIGQUIT + SIGQUIT, +#endif +#ifdef SIGTERM + SIGTERM, +#endif +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif + SIGINT, + SIGPIPE +}; +#define handler_index_of_SIGINT (NUM_SIGS - 2) +#define handler_index_of_SIGPIPE (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, RETSIGTYPE (*) (int)); +#else + static RETSIGTYPE (*initial_action[NUM_SIGS]) (); +# define initial_handler(i) (initial_action[i]) +# define signal_handler(sig, handler) signal (sig, handler) +#endif + +#if ! HAVE_SIGPROCMASK +# define sigset_t int +# define sigemptyset(s) (*(s) = 0) +# ifndef sigmask +# define sigmask(sig) (1 << ((sig) - 1)) +# endif +# define sigaddset(s, sig) (*(s) |= sigmask (sig)) +# ifndef SIG_BLOCK +# define SIG_BLOCK 0 +# endif +# ifndef SIG_SETMASK +# define SIG_SETMASK (! SIG_BLOCK) +# endif +# define sigprocmask(how, n, o) \ + ((how) == SIG_BLOCK ? *(o) = sigblock (*(n)) : sigsetmask (*(n))) +#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 +}; + +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'}, + {"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'}, + {"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 FILE --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_("-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 RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."), + N_("--strip-trailing-cr Strip trailing carriage return on input."), + N_("-a --text Treat all files as text."), + "", + N_("-w NUM --width=NUM Output at most NUM (default 130) columns per line."), + N_("-l --left-column Output only the left column of common lines."), + N_("-s --suppress-common-lines Do not output common lines."), + "", + N_("-t --expand-tabs Expand tabs to spaces in output."), + "", + N_("-d --minimal Try hard to find a smaller set of changes."), + N_("-H --speed-large-files Assume large files and many scattered small changes."), + N_("--diff-program=PROGRAM Use PROGRAM to compare files."), + "", + N_("-v --version Output version info."), + N_("--help Output this help."), + 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 file differences.")); + for (p = option_help_msgid; *p; p++) + if (**p) + printf (" %s\n", _(*p)); + else + putchar ('\n'); + printf ("\n%s\n\n%s\n", + _("If a FILE is `-', read standard input."), + _("Report bugs to <bug-gnu-utils@gnu.org>.")); +} + +static void +cleanup (void) +{ +#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK + if (0 < diffpid) + kill (diffpid, SIGPIPE); +#endif + if (tmpname) + unlink (tmpname); +} + +static void exiterr (void) __attribute__((noreturn)); +static void +exiterr (void) +{ + cleanup (); + 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 +ck_editor_status (int errnum, int status) +{ + if (errnum | status) + { + char const *failure_msgid = N_("subsidiary program `%s' failed"); + if (! errnum && WIFEXITED (status)) + switch (WEXITSTATUS (status)) + { + case 126: + failure_msgid = N_("subsidiary program `%s' not executable"); + break; + case 127: + failure_msgid = N_("subsidiary program `%s' not found"); + break; + } + error (0, errnum, _(failure_msgid), editor_program); + 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 (strcmp (name, "-") == 0) + fatal ("cannot interactively merge standard input"); + if (! is_dir) + return name; + else + { + /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */ + char const *base = base_name (other_name); + size_t namelen = strlen (name), baselen = strlen (base); + bool insert_slash = *base_name (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 + 1); + 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 = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); + if (! lf->bufpos) + { + ck_fwrite (start, lf->buflim - start, outfile); + if (! lf_refill (lf)) + return; + start = lf->bufpos; + } + else + { + --lines; + ++lf->bufpos; + } + } + + ck_fwrite (start, lf->bufpos - start, outfile); +} + +/* Advance LINES on LF's infile without doing output */ +static void +lf_skip (struct line_filter *lf, lin lines) +{ + while (lines) + { + lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); + if (! lf->bufpos) + { + if (! lf_refill (lf)) + break; + } + else + { + --lines; + ++lf->bufpos; + } + } +} + +/* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */ +static int +lf_snarf (struct line_filter *lf, char *buffer, size_t bufsize) +{ + for (;;) + { + char *start = lf->bufpos; + char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start); + size_t s = next - start; + if (bufsize <= s) + return 0; + memcpy (buffer, start, s); + if (next < lf->buflim) + { + buffer[s] = 0; + lf->bufpos = next + 1; + return 1; + } + if (! lf_refill (lf)) + return s ? 0 : EOF; + buffer += s; + bufsize -= s; + } +} + + + +int +main (int argc, char *argv[]) +{ + int opt; + char const *prog; + + exit_failure = EXIT_TROUBLE; + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + c_stack_action (c_stack_die); + + prog = getenv ("EDITOR"); + if (prog) + editor_program = prog; + + diffarg (DEFAULT_DIFF_PROGRAM); + + /* parse command line args */ + while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", 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 = 1; + break; + + case 't': + diffarg ("-t"); + break; + + case 'v': + printf ("sdiff %s\n%s\n\n%s\n\n%s\n", + version_string, copyright_string, + _(free_software_msgid), _(authorship_msgid)); + check_stdout (); + return EXIT_SUCCESS; + + case 'w': + diffarg ("-W"); + diffarg (optarg); + break; + + case 'W': + diffarg ("-w"); + 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; + + 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 || HAVE_WORKING_VFORK) + { + size_t cmdsize = 1; + char *p, *command; + int i; + + for (i = 0; diffargv[i]; i++) + cmdsize += quote_system_arg (0, diffargv[i]) + 1; + command = p = xmalloc (cmdsize); + for (i = 0; diffargv[i]; i++) + { + p += quote_system_arg (p, diffargv[i]); + *p++ = ' '; + } + p[-1] = 0; + errno = 0; + diffout = popen (command, "r"); + if (! diffout) + perror_fatal (command); + free (command); + } +#else + { + int diff_fds[2]; +# if HAVE_WORKING_VFORK + sigset_t procmask; + sigset_t blocked; +# endif + + if (pipe (diff_fds) != 0) + perror_fatal ("pipe"); + +# if HAVE_WORKING_VFORK + /* Block SIGINT and SIGPIPE. */ + sigemptyset (&blocked); + sigaddset (&blocked, SIGINT); + sigaddset (&blocked, SIGPIPE); + sigprocmask (SIG_BLOCK, &blocked, &procmask); +# endif + diffpid = vfork (); + 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); +# if HAVE_WORKING_VFORK + /* Stop blocking SIGINT and SIGPIPE in the child. */ + sigprocmask (SIG_SETMASK, &procmask, 0); +# endif + 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 == ENOEXEC ? 126 : 127); + } + +# if HAVE_WORKING_VFORK + /* Restore the parent's SIGINT and SIGPIPE behavior. */ + if (initial_handler (handler_index_of_SIGINT) != SIG_IGN) + signal_handler (SIGINT, catchsig); + if (initial_handler (handler_index_of_SIGPIPE) != SIG_IGN) + signal_handler (SIGPIPE, catchsig); + else + signal_handler (SIGPIPE, SIG_IGN); + + /* Stop blocking SIGINT and SIGPIPE in the parent. */ + sigprocmask (SIG_SETMASK, &procmask, 0); +# endif + + 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 || HAVE_WORKING_VFORK) + 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 (); + + ck_editor_status (werrno, wstatus); + 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 RETSIGTYPE +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, RETSIGTYPE (*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 = 1; +} + +/* 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 (); + + /* 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:\tEdit then use the left version.\n\ +er:\tEdit then use the right version.\n\ +e:\tEdit a new version.\n\ +l:\tUse the left version.\n\ +r:\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, cmd1; + bool gotcmd = 0; + + cmd1 = 0; /* Pacify `gcc -W'. */ + + while (! gotcmd) + { + if (putchar ('%') != '%') + perror_fatal (_("write failed")); + ck_fflush (stdout); + + cmd0 = skip_white (); + switch (cmd0) + { + case 'l': case 'r': case 's': case 'v': case 'q': + if (skip_white () != '\n') + { + give_help (); + flush_line (); + continue; + } + gotcmd = 1; + break; + + case 'e': + cmd1 = skip_white (); + switch (cmd1) + { + case 'b': case 'd': case 'l': case 'r': + if (skip_white () != '\n') + { + give_help (); + flush_line (); + continue; + } + gotcmd = 1; + break; + case '\n': + gotcmd = 1; + break; + default: + give_help (); + flush_line (); + continue; + } + break; + + case EOF: + if (feof (stdin)) + { + gotcmd = 1; + cmd0 = 'q'; + break; + } + /* Fall through. */ + default: + flush_line (); + /* Fall through. */ + case '\n': + give_help (); + continue; + } + } + + switch (cmd0) + { + case 'l': + lf_copy (left, llen, outfile); + lf_skip (right, rlen); + return 1; + case 'r': + lf_copy (right, rlen, outfile); + lf_skip (left, llen); + return 1; + case 's': + suppress_common_lines = 1; + break; + case 'v': + suppress_common_lines = 0; + break; + case 'q': + return 0; + 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) lline); + else + fprintf (tmp, "--- %s %ld,%ld\n", lname, + (long) lline, (long) (lline + llen - 1)); + } + /* Fall through. */ + 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) rline); + else + fprintf (tmp, "+++ %s %ld,%ld\n", rname, + (long) rline, (long) (rline + rlen - 1)); + } + /* Fall through. */ + case 'b': case 'r': + lf_copy (right, rlen, tmp); + break; + + default: + lf_skip (right, rlen); + break; + } + + ck_fclose (tmp); + + { + int wstatus; + int werrno = 0; + ignore_SIGINT = 1; + checksigs (); + + { +#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) + char *command = + xmalloc (quote_system_arg (0, editor_program) + + 1 + strlen (tmpname) + 1); + sprintf (command + quote_system_arg (command, editor_program), + " %s", tmpname); + wstatus = system (command); + if (wstatus == -1) + werrno = errno; + free (command); +#else + pid_t pid; + + pid = vfork (); + if (pid == 0) + { + char const *argv[3]; + int i = 0; + + argv[i++] = editor_program; + argv[i++] = tmpname; + argv[i] = 0; + + execvp (editor_program, (char **) argv); + _exit (errno == ENOEXEC ? 126 : 127); + } + + if (pid < 0) + perror_fatal ("fork"); + + while (waitpid (pid, &wstatus, 0) < 0) + if (errno == EINTR) + checksigs (); + else + perror_fatal ("waitpid"); +#endif + } + + ignore_SIGINT = 0; + ck_editor_status (werrno, wstatus); + } + + { + 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 1; + } + 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; + llen = val = strtoumax (diff_help + 1, &numend, 10); + if (llen < 0 || llen != val || errno || *numend != ',') + fatal (diff_help); + rlen = val = strtoumax (numend + 1, &numend, 10); + if (rlen < 0 || rlen != val || errno || *numend) + fatal (diff_help); + + 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 0; + break; + + default: + fatal (diff_help); + } + + lline += llen; + rline += rlen; + } + } +} + +/* Return nonzero 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; + int e; + sigset_t procmask; + sigset_t blocked; + sprintf (buf, "%s/sdiffXXXXXX", dir); + sigemptyset (&blocked); + sigaddset (&blocked, SIGINT); + sigprocmask (SIG_BLOCK, &blocked, &procmask); + fd = mkstemp (buf); + e = errno; + if (0 <= fd) + tmpname = buf; + sigprocmask (SIG_SETMASK, &procmask, 0); + errno = e; + return fd; +} diff --git a/gnu/dist/diffutils/src/side.c b/gnu/dist/diffutils/src/side.c new file mode 100644 index 00000000000..1ad8c4ec86e --- /dev/null +++ b/gnu/dist/diffutils/src/side.c @@ -0,0 +1,285 @@ +/* $NetBSD: side.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* sdiff-format output routines for GNU DIFF. + + Copyright (C) 1991, 1992, 1993, 1998, 2001, 2002 Free Software + Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. No author or distributor + accepts responsibility to anyone for the consequences of using it + or for whether it serves any particular purpose or works at all, + unless he says so in writing. Refer to the GNU DIFF General Public + License for full details. + + Everyone is granted permission to copy, modify and redistribute + GNU DIFF, but only under the conditions described in the + GNU DIFF General Public License. A copy of this license is + supposed to have been given to you along with GNU DIFF so you + can know your rights and responsibilities. It should be in a + file named COPYING. Among other things, the copyright notice + and this notice must be preserved on all copies. */ + +#include "diff.h" + +static 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 unsigned int +tab_from_to (unsigned int from, unsigned int to) +{ + FILE *out = outfile; + unsigned int tab; + + if (!expand_tabs) + for (tab = from + TAB_WIDTH - from % TAB_WIDTH; tab <= to; tab += TAB_WIDTH) + { + putc ('\t', out); + from = tab; + } + while (from++ < to) + putc (' ', out); + return to; +} + +/* + * Print the text for half an sdiff line. This means truncate to width + * observing tabs, and trim a trailing newline. Returns the last column + * written (not the number of chars). + */ +static unsigned int +print_half_line (char const *const *line, unsigned int indent, + unsigned int out_bound) +{ + FILE *out = outfile; + register unsigned int in_position = 0; + register unsigned int out_position = 0; + register char const *text_pointer = line[0]; + register char const *text_limit = line[1]; + + while (text_pointer < text_limit) + { + register unsigned char c = *text_pointer++; + + switch (c) + { + case '\t': + { + unsigned int spaces = TAB_WIDTH - in_position % TAB_WIDTH; + if (in_position == out_position) + { + unsigned int 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; + + case '\f': + case '\v': + control_char: + if (in_position < out_bound) + putc (c, out); + break; + + default: + if (! ISPRINT (c)) + goto control_char; + /* falls through */ + case ' ': + if (in_position++ < out_bound) + { + out_position = in_position; + putc (c, out); + } + break; + + case '\n': + return out_position; + } + } + + return out_position; +} + +/* + * Print side by side lines with a separator in the middle. + * 0 parameters are taken to indicate white space text. + * Blank lines that can easily be caught are reduced to a single newline. + */ + +static void +print_1sdiff_line (char const *const *left, char sep, + char const *const *right) +{ + FILE *out = outfile; + unsigned int hw = sdiff_half_width, c2o = sdiff_column2_offset; + unsigned int col = 0; + bool put_newline = 0; + + 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); +} + +/* 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 len0 = limit0 - i0; + long 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 len0 = last0 - first0 + 1; + long 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/gnu/dist/diffutils/src/system.h b/gnu/dist/diffutils/src/system.h new file mode 100644 index 00000000000..0fa5e58b4fd --- /dev/null +++ b/gnu/dist/diffutils/src/system.h @@ -0,0 +1,403 @@ +/* $NetBSD: system.h,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */ + +/* System dependent declarations. + + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> + +/* Don't bother to support K&R C compilers any more; it's not worth + the trouble. These macros prevent some library modules from being + compiled in K&R C mode. */ +#define PARAMS(Args) Args +#define PROTOTYPES 1 + +/* 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 +#if defined const && !defined volatile +# define volatile +#endif + +/* Verify a requirement at compile-time (unlike assert, which is runtime). */ +#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } + + +/* Determine whether an integer type is signed, and its bounds. + This code assumes two's (or one's!) complement with no holes. */ + +/* The extra casts work around common compiler bugs, + e.g. Cray C 5.0.3.0 when t == time_t. */ +#ifndef TYPE_SIGNED +# define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) +#endif +#ifndef TYPE_MINIMUM +# define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \ + ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) \ + : (t) 0)) +#endif +#ifndef TYPE_MAXIMUM +# define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t))) +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#if STAT_MACROS_BROKEN +# undef S_ISBLK +# undef S_ISCHR +# undef S_ISDIR +# undef S_ISFIFO +# undef S_ISREG +# undef S_ISSOCK +#endif +#ifndef S_ISDIR +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#ifndef S_ISREG +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined S_ISBLK && defined S_IFBLK +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif +#if !defined S_ISCHR && defined S_IFCHR +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined S_ISFIFO && defined S_IFFIFO +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) +#endif +#if !defined S_ISSOCK && defined S_IFSOCK +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#ifndef S_IXUSR +# define S_IXUSR 0100 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0010 +#endif +#ifndef S_IXOTH +# define S_IXOTH 0001 +#endif + +#if HAVE_UNISTD_H +# include <unistd.h> +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +# define SEEK_CUR 1 +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +#if HAVE_TIME_H +# include <time.h> +#else +# include <sys/time.h> +#endif + +#if HAVE_FCNTL_H +# include <fcntl.h> +#else +# if HAVE_SYS_FILE_H +# include <sys/file.h> +# endif +#endif + +#if !HAVE_DUP2 +# define dup2(f, t) (close (t), fcntl (f, F_DUPFD, t)) +#endif + +#ifndef O_RDONLY +# define O_RDONLY 0 +#endif +#ifndef O_RDWR +# define O_RDWR 2 +#endif +#ifndef S_IRUSR +# define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 0200 +#endif + +#if HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#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 + +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen ((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) ((dirent)->d_namlen) +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#if HAVE_STDLIB_H +# include <stdlib.h> +#else +# ifndef getenv + char *getenv (); +# endif +#endif +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif +#if !EXIT_FAILURE +# undef EXIT_FAILURE /* Sony NEWS-OS 4.0C defines EXIT_FAILURE to 0. */ +# define EXIT_FAILURE 1 +#endif +#define EXIT_TROUBLE 2 + +#include <limits.h> +#ifndef SSIZE_MAX +# define SSIZE_MAX TYPE_MAXIMUM (ssize_t) +#endif + +#if HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifndef PTRDIFF_MAX +# define PTRDIFF_MAX TYPE_MAXIMUM (ptrdiff_t) +#endif +#ifndef SIZE_MAX +# define SIZE_MAX TYPE_MAXIMUM (size_t) +#endif +#ifndef UINTMAX_MAX +# define UINTMAX_MAX TYPE_MAXIMUM (uintmax_t) +#endif +#if ! HAVE_STRTOUMAX && ! defined strtoumax +uintmax_t strtoumax (char const *, char **, int); +#endif + +#include <stddef.h> + +#if STDC_HEADERS || HAVE_STRING_H +# include <string.h> +#else +# if !HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# if !HAVE_MEMCHR +# define memcmp(s1, s2, n) bcmp (s1, s2, n) +# define memcpy(d, s, n) bcopy (s, d, n) +void *memchr (); +# endif +#endif + +#if HAVE_LOCALE_H +# include <locale.h> +#else +# define setlocale(category, locale) +#endif + +#include <gettext.h> + +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +#include <ctype.h> + +/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given + as an argument to <ctype.h> macros like `isspace'. */ +#if STDC_HEADERS +# define CTYPE_DOMAIN(c) 1 +#else +# define CTYPE_DOMAIN(c) ((unsigned int) (c) <= 0177) +#endif +#define ISPRINT(c) (CTYPE_DOMAIN (c) && isprint (c)) +#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c)) + +#if STDC_HEADERS +# define TOLOWER(c) tolower (c) +#else +# ifndef _tolower +# define _tolower(c) tolower (c) +# endif +# define TOLOWER(c) (CTYPE_DOMAIN (c) && isupper (c) ? _tolower (c) : (c)) +#endif + +/* 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> +#if !STDC_HEADERS + extern int errno; +#endif + +#include <signal.h> +#ifndef SA_RESTART +# ifdef SA_INTERRUPT /* e.g. SunOS 4.1.x */ +# define SA_RESTART SA_INTERRUPT +# else +# define SA_RESTART 0 +# endif +#endif +#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)) + +#if HAVE_STDBOOL_H +# include <stdbool.h> +#else +# define bool unsigned char +#endif + +#if HAVE_VFORK_H +# include <vfork.h> +#endif + +#if ! HAVE_WORKING_VFORK +# define vfork fork +#endif + +/* Type used for fast comparison of several bytes at a time. */ + +#ifndef word +# define word uintmax_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 (lin_is_signed, TYPE_SIGNED (lin)); +verify (lin_is_wide_enough, sizeof (ptrdiff_t) <= sizeof (lin)); +verify (lin_is_printable_as_long, sizeof (lin) <= sizeof (long)); + +/* 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_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 filesystems incorrectly assign the same (device, + inode) pair to two distinct files, including: + + - GNU/Linux NFS servers that export all local filesystems as a + single NFS filesystem, 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 filesystem 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 diff --git a/gnu/dist/diffutils/src/util.c b/gnu/dist/diffutils/src/util.c new file mode 100644 index 00000000000..0208c7efb18 --- /dev/null +++ b/gnu/dist/diffutils/src/util.c @@ -0,0 +1,758 @@ +/* $NetBSD: util.c,v 1.1.1.1 2003/01/26 00:43:17 wiz Exp $ */ + +/* Support routines for GNU DIFF. + + Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002 + Free Software Foundation, Inc. + + This file is part of GNU DIFF. + + GNU DIFF is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU DIFF is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "diff.h" +#include <dirname.h> +#include <error.h> +#include <quotesys.h> +#include <regex.h> +#include <xalloc.h> + +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; + } +} + +/* Call before outputting the results of comparing files NAME0 and NAME1 + to set up OUTFILE, the stdio stream for the output to go to. + + Usually, OUTFILE is just stdout. But when -l was specified + we fork off a `pr' and make OUTFILE a pipe to it. + `pr' then outputs to our stdout. */ + +static char const *current_name0; +static char const *current_name1; +static bool currently_recursive; + +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 || HAVE_WORKING_VFORK +static pid_t pr_pid; +#endif + +void +begin_output (void) +{ + char *name; + + if (outfile != 0) + return; + + /* Construct the header of this piece of diff. */ + name = xmalloc (strlen (current_name0) + strlen (current_name1) + + strlen (switch_string) + 7); + + /* POSIX 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. */ + sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1); + + if (paginate) + { + if (fflush (stdout) != 0) + pfatal_with_name (_("write failed")); + + /* Make OUTFILE a pipe to a subsidiary `pr'. */ + { +#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK + int pipes[2]; + + if (pipe (pipes) != 0) + pfatal_with_name ("pipe"); + + pr_pid = vfork (); + 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]); + } + + execl (pr_program, pr_program, "-h", name, 0); + _exit (errno == ENOEXEC ? 126 : 127); + } + else + { + close (pipes[0]); + outfile = fdopen (pipes[1], "w"); + if (!outfile) + pfatal_with_name ("fdopen"); + } +#else + char *command = xmalloc (sizeof pr_program - 1 + 7 + + quote_system_arg ((char *) 0, name) + 1); + char *p; + sprintf (command, "%s -f -h ", pr_program); + p = command + sizeof pr_program - 1 + 7; + p += quote_system_arg (p, name); + *p = 0; + errno = 0; + outfile = popen (command, "w"); + if (!outfile) + pfatal_with_name (command); + free (command); +#endif + } + } + else + { + + /* If -l was not specified, output the diff straight to `stdout'. */ + + outfile = stdout; + + /* If handling multiple files (because scanning a directory), + print which files the following output is about. */ + if (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, 0); + break; + + case OUTPUT_UNIFIED: + print_context_header (files, 1); + break; + + default: + break; + } +} + +/* Call after the end of output of diffs for one file. + Close OUTFILE and get rid of the `pr' subfork. */ + +void +finish_output (void) +{ + if (outfile != 0 && outfile != stdout) + { + int wstatus; + int werrno = 0; + if (ferror (outfile)) + fatal ("write failed"); +#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) + 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 + if (! werrno && WIFEXITED (wstatus) && WEXITSTATUS (wstatus) == 127) + error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not found"), + pr_program); + if (wstatus != 0) + error (EXIT_TROUBLE, werrno, _("subsidiary program `%s' failed"), + pr_program); + } + + 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 unsigned char const *t1 = (unsigned char const *) s1; + register unsigned char const *t2 = (unsigned char const *) 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' + && (unsigned char const *) s1 + 1 < t1 + && ISSPACE (t1[-2])) + { + --t1; + continue; + } + if (c1 == ' ' && c2 != '\n' + && (unsigned char const *) s2 + 1 < t2 + && ISSPACE (t2[-2])) + { + --t2; + continue; + } + } + + break; + + 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 += TAB_WIDTH - column % TAB_WIDTH; + else + break; + } + for (;; c2 = *t2++) + { + if (c2 == ' ') + column2++; + else if (c2 == '\t') + column2 += TAB_WIDTH - column2 % TAB_WIDTH; + else + break; + } + if (column != column2) + return 1; + } + 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 0; + + column += c1 == '\t' ? TAB_WIDTH - column % TAB_WIDTH : 1; + } + + return 1; +} + +/* Find the consecutive changes at the start of the script START. + Return the last link before the first gap. */ + +struct change * +find_change (struct change *start) +{ + return start; +} + +struct change * +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.). */ + +void +print_1_line (char const *line_flag, char const *const *line) +{ + 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. */ + + if (line_flag && *line_flag) + { + flag_format = initial_tab ? "%s\t" : "%s "; + fprintf (out, flag_format, line_flag); + } + + output_1_line (base, limit, flag_format, line_flag); + + if ((!line_flag || line_flag[0]) && limit[-1] != '\n') + 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) +{ + if (!expand_tabs) + fwrite (base, limit - base, 1, outfile); + else + { + register FILE *out = outfile; + register unsigned char c; + register char const *t = base; + register unsigned int column = 0; + + while (t < limit) + switch ((c = *t++)) + { + case '\t': + { + unsigned int spaces = TAB_WIDTH - column % TAB_WIDTH; + column += spaces; + do + putc (' ', out); + while (--spaces); + } + break; + + case '\r': + putc (c, out); + if (flag_format && t < limit && *t != '\n') + fprintf (out, flag_format, line_flag); + column = 0; + break; + + case '\b': + if (column == 0) + continue; + column--; + putc (c, out); + break; + + default: + if (ISPRINT (c)) + column++; + putc (c, out); + break; + } + } +} + +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 +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 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 *aptr, long *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 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 = (int) ignore_blank_lines - 1; + /* If 0, ignore zero-length lines; + if SIZE_MAX, do not ignore lines just because of their length. */ + + 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]; + size_t len = linbuf0[i + 1] - line - 1; + if (len != 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]; + size_t len = linbuf1[i + 1] - line - 1; + if (len != 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; +} + +/* Yield the newly malloc'd pathname + of the file in DIR whose filename is FILE. */ + +char * +dir_file_pathname (char const *dir, char const *file) +{ + char const *base = base_name (dir); + bool omit_slash = !*base || base[strlen (base) - 1] == '/'; + return concat (dir, "/" + omit_slash, file); +} + +void +debug_script (struct change *sp) +{ + fflush (stdout); + + for (; sp; sp = sp->link) + { + long line0 = sp->line0; + long line1 = sp->line1; + long deleted = sp->deleted; + long inserted = sp->inserted; + fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n", + line0, line1, deleted, inserted); + } + + fflush (stderr); +} diff --git a/gnu/dist/diffutils/src/version.c b/gnu/dist/diffutils/src/version.c new file mode 100644 index 00000000000..b3cc515c7cd --- /dev/null +++ b/gnu/dist/diffutils/src/version.c @@ -0,0 +1,4 @@ +/* $NetBSD: version.c,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */ + +#include <config.h> +char const version_string[] = "(GNU diffutils) 2.8.1"; |
