Blame diffstat.c

Packit bef1e6
/******************************************************************************
Packit bef1e6
 * Copyright 1994-2015,2016 by Thomas E. Dickey                               *
Packit bef1e6
 * All Rights Reserved.                                                       *
Packit bef1e6
 *                                                                            *
Packit bef1e6
 * Permission to use, copy, modify, and distribute this software and its      *
Packit bef1e6
 * documentation for any purpose and without fee is hereby granted, provided  *
Packit bef1e6
 * that the above copyright notice appear in all copies and that both that    *
Packit bef1e6
 * copyright notice and this permission notice appear in supporting           *
Packit bef1e6
 * documentation, and that the name of the above listed copyright holder(s)   *
Packit bef1e6
 * not be used in advertising or publicity pertaining to distribution of the  *
Packit bef1e6
 * software without specific, written prior permission.                       *
Packit bef1e6
 *                                                                            *
Packit bef1e6
 * THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD   *
Packit bef1e6
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND  *
Packit bef1e6
 * FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE  *
Packit bef1e6
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES          *
Packit bef1e6
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN      *
Packit bef1e6
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR *
Packit bef1e6
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.                *
Packit bef1e6
 ******************************************************************************/
Packit bef1e6
Packit bef1e6
#ifndef	NO_IDENT
Packit bef1e6
static const char *Id = "$Id: diffstat.c,v 1.61 2016/01/14 00:52:29 tom Exp $";
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Title:	diffstat.c
Packit bef1e6
 * Author:	T.E.Dickey
Packit bef1e6
 * Created:	02 Feb 1992
Packit bef1e6
 * Modified:
Packit bef1e6
 *		14 Jan 2016, extend -S option to count unmodified files.
Packit bef1e6
 *			     add -T option to show values with histogram
Packit bef1e6
 *		06 Jul 2015, handle double-quotes, e.g., from diffutils 3.3
Packit bef1e6
 *			     when filenames have embedded spaces.
Packit bef1e6
 *		05 Jun 2014, add -E option to filter colordiff output.
Packit bef1e6
 *		28 Oct 2013, portability improvements for MinGW.
Packit bef1e6
 *		15 Apr 2013, modify to accommodate output of "diff -q", which
Packit bef1e6
 *			     tells only if the files are different.  Work
Packit bef1e6
 *			     around the equivalent ambiguous message introduced
Packit bef1e6
 *			     in diffutils 2.8.4 and finally removed for 3.0
Packit bef1e6
 *		11 Feb 2013, add -K option.  Use strtol() to provide error
Packit bef1e6
 *			     checking of optarg values.
Packit bef1e6
 *		10 Feb 2013, document -b, -C, -s option in usage (patch by
Packit bef1e6
 *			     Tim Waugh, Red Hat #852770).  Improve pathname
Packit bef1e6
 *			     merging.
Packit bef1e6
 *		02 Jun 2012, fix for svn diff with spaces in path (patch by
Packit bef1e6
 *			     Stuart Prescott, Debian #675465).
Packit bef1e6
 *		03 Jan 2012, Correct case for "xz" suffix in is_compressed()
Packit bef1e6
 *			     (patch from Frederic Culot in FreeBSD ports).  Add
Packit bef1e6
 *			     "-R" option.  Improve dequoting of filenames in
Packit bef1e6
 *			     headers.
Packit bef1e6
 *		10 Oct 2010, correct display of new files when -S/-D options
Packit bef1e6
 *			     are used.  Remove the temporary directory on
Packit bef1e6
 *			     error, introduced in 1.48+ (patch by Solar
Packit bef1e6
 *			     Designer).
Packit bef1e6
 *		19 Jul 2010, add missing "break" statement which left "-c"
Packit bef1e6
 *			     option falling-through into "-C".
Packit bef1e6
 *		16 Jul 2010, configure "xz" path explicitly, in case lzcat
Packit bef1e6
 *			     does not support xz format.  Add "-s" (summary)
Packit bef1e6
 *			     and "-C" (color) options.
Packit bef1e6
 *		15 Jul 2010, fix strict gcc warnings, e.g., using const.
Packit bef1e6
 *		10 Jan 2010, improve a case where filenames have embedded blanks
Packit bef1e6
 *			     (patch by Reinier Post).
Packit bef1e6
 *		07 Nov 2009, correct suffix-check for ".xz" files as
Packit bef1e6
 *			     command-line parameters rather than as piped
Packit bef1e6
 *			     input (report by Moritz Barsnick).
Packit bef1e6
 *		06 Oct 2009, fixes to build/run with MSYS or MinGW.  use
Packit bef1e6
 *			     $TMPDIR for path of temporary file used in
Packit bef1e6
 *			     decompression.  correct else-condition for
Packit bef1e6
 *			     detecting compression type (patch by Zach Hirsch).
Packit bef1e6
 *		31 Aug 2009, improve lzma support, add support for xz (patch by
Packit bef1e6
 *			     Eric Blake).  Add special case for no-newline
Packit bef1e6
 *			     message from some diff's (Ubuntu #269895).
Packit bef1e6
 *			     Improve configure check for getopt().
Packit bef1e6
 *		11 Aug 2009, Add logic to check standard input, decompress if
Packit bef1e6
 *			     possible.  Add -N option, to truncate long names.
Packit bef1e6
 *			     Add pack/pcat as a compression type.
Packit bef1e6
 *			     Add lzma/lzcat as a compression type.
Packit bef1e6
 *			     Allow overriding program paths with environment.
Packit bef1e6
 *		10 Aug 2009, modify to work with Perforce-style diffs (patch
Packit bef1e6
 *			     by Ed Schouten).
Packit bef1e6
 *		29 Mar 2009, modify to work with patch ".rej" files, which have
Packit bef1e6
 *			     no filename header (use the name of the ".rej"
Packit bef1e6
 *			     file if it is available).
Packit bef1e6
 *		29 Sep 2008, fix typo in usage message.
Packit bef1e6
 *		06 Aug 2008, add "-m", "-S" and "-D" options.
Packit bef1e6
 *		05 Aug 2008, add "-q" option to suppress 0-files-changed
Packit bef1e6
 *			     message (patch by Greg Norris).
Packit bef1e6
 *		04 Sep 2007, add "-b" option to suppress binary-files (patch
Packit bef1e6
 *			     by Greg Norris).
Packit bef1e6
 *		26 Aug 2007, add "-d" option to show debugging traces, rather
Packit bef1e6
 *			     than by defining DEBUG.  Add check after
Packit bef1e6
 *			     unified-diff chunk to avoid adding non-diff text
Packit bef1e6
 *			     (report by Adrian Bunk).  Quote pathname passed
Packit bef1e6
 *			     in command to gzip/uncompress.  Add a check for
Packit bef1e6
 *			     default-diff output without the "diff" command
Packit bef1e6
 *			     supplied to provide filename, mark as "unknown".
Packit bef1e6
 *		16 Jul 2006, fix to avoid modifying which is being used by
Packit bef1e6
 *			     tsearch() for ordering the binary tree (report by
Packit bef1e6
 *			     Adrian Bunk).
Packit bef1e6
 *		02 Jul 2006, do not ignore pathnames in /tmp/, since some tools
Packit bef1e6
 *			     create usable pathnames for both old/new files
Packit bef1e6
 *			     there (Debian #376086).  Correct ifdef for
Packit bef1e6
 *			     fgetc_unlocked().  Add configure check for
Packit bef1e6
 *			     compress, gzip and bzip2 programs that may be used
Packit bef1e6
 *			     to decompress files.
Packit bef1e6
 *		24 Aug 2005, update usage message for -l, -r changes.
Packit bef1e6
 *		15 Aug 2005, apply PLURAL() to num_files (Jean Delvare).
Packit bef1e6
 *			     add -l option (request by Michael Burian).
Packit bef1e6
 *			     Use fgetc_locked() if available.
Packit bef1e6
 *		14 Aug 2005, add -r2 option (rounding with adjustment to ensure
Packit bef1e6
 *			     that nonzero values always display a histogram
Packit bef1e6
 *			     bar), adapted from patch by Jean Delvare.  Extend
Packit bef1e6
 *			     the -f option (2=filled, 4=verbose).
Packit bef1e6
 *		12 Aug 2005, modify to use tsearch() for sorted lists.
Packit bef1e6
 *		11 Aug 2005, minor fixes to scaling of modified lines.  Add
Packit bef1e6
 *			     -r (round) option.
Packit bef1e6
 *		05 Aug 2005, add -t (table) option.
Packit bef1e6
 *		10 Apr 2005, change order of merging and prefix-stripping so
Packit bef1e6
 *			     stripping all prefixes, e.g., with -p9, will be
Packit bef1e6
 *			     sorted as expected (Patch by Jean Delvare
Packit bef1e6
 *			     <khali@linux-fr.org>).
Packit bef1e6
 *		10 Jan 2005, add support for '--help' and '--version' (Patch
Packit bef1e6
 *			     by Eric Blake <ebb9@byu.net>.)
Packit bef1e6
 *		16 Dec 2004, fix a different case for data beginning with "--"
Packit bef1e6
 *			     which was treated as a header line.
Packit bef1e6
 *		14 Dec 2004, Fix allocation problems.  Open files in binary
Packit bef1e6
 *			     mode for reading.  Getopt returns -1, not
Packit bef1e6
 *			     necessarily EOF.  Add const where useful.  Use
Packit bef1e6
 *			     NO_IDENT where necessary.  malloc() comes from
Packit bef1e6
 *			     <stdlib.h> in standard systems (Patch by Eric
Packit bef1e6
 *			     Blake <ebb9@byu.net>.)
Packit bef1e6
 *		08 Nov 2004, minor fix for resync of unified diffs checks for
Packit bef1e6
 *			     range (line beginning with '@' without header
Packit bef1e6
 *			     lines (successive lines beginning with "---" and
Packit bef1e6
 *			     "+++").  Fix a few problems reported by valgrind.
Packit bef1e6
 *		09 Nov 2003, modify check for lines beginning with '-' or '+'
Packit bef1e6
 *			     to treat only "---" in old-style diffs as a
Packit bef1e6
 *			     special case.
Packit bef1e6
 *		14 Feb 2003, modify check for filenames to allow for some cases
Packit bef1e6
 *			     of incomplete dates (the reported example omitted
Packit bef1e6
 *			     the day of the month).  Correct a typo in usage().
Packit bef1e6
 *			     Add -e, -h, -o options.
Packit bef1e6
 *		04 Jan 2003, improve tracking of chunks in unified diff, in
Packit bef1e6
 *			     case the original files contained a '+' or '-' in
Packit bef1e6
 *			     the first column (Debian #155000).  Add -v option
Packit bef1e6
 *			     (Debian #170947).  Modify to allocate buffers big
Packit bef1e6
 *			     enough for long input lines.  Do additional
Packit bef1e6
 *			     merging to handle unusual Index/diff constructs in
Packit bef1e6
 *			     recent makepatch script.
Packit bef1e6
 *		20 Aug 2002, add -u option to tell diffstat to preserve the
Packit bef1e6
 *			     order of filenames as given rather than sort them
Packit bef1e6
 *			     (request by H Peter Anvin <hpa@zytor.com>).  Add
Packit bef1e6
 *			     -k option for completeness.
Packit bef1e6
 *		09 Aug 2002, allow either '/' or '-' as delimiters in dates,
Packit bef1e6
 *			     to accommodate diffutils 2.8 (report by Rik van
Packit bef1e6
 *			     Riel <riel@conectiva.com.br>).
Packit bef1e6
 *		10 Oct 2001, add bzip2 (.bz2) suffix as suggested by
Packit bef1e6
 *			     Gregory T Norris <haphazard@socket.net> in Debian
Packit bef1e6
 *			     bug report #82969).
Packit bef1e6
 *			     add check for diff from RCS archive where the
Packit bef1e6
 *			     "diff" lines do not reference a filename.
Packit bef1e6
 *		29 Mar 2000, add -c option.  Check for compressed input, read
Packit bef1e6
 *			     via pipe.  Change to ANSI C.  Adapted change from
Packit bef1e6
 *			     Troy Engel to add option that displays a number
Packit bef1e6
 *			     only, rather than a histogram.
Packit bef1e6
 *		17 May 1998, handle Debian diff files, which do not contain
Packit bef1e6
 *			     dates on the header lines.
Packit bef1e6
 *		16 Jan 1998, accommodate patches w/o tabs in header lines (e.g.,
Packit bef1e6
 *			     from cut/paste).  Strip suffixes such as ".orig".
Packit bef1e6
 *		24 Mar 1996, corrected -p0 logic, more fixes in do_merging.
Packit bef1e6
 *		16 Mar 1996, corrected state-change for "Binary".  Added -p
Packit bef1e6
 *			     option.
Packit bef1e6
 *		17 Dec 1995, corrected matching algorithm in 'do_merging()'
Packit bef1e6
 *		11 Dec 1995, mods to accommodate diffs against /dev/null or
Packit bef1e6
 *			     /tmp/XXX (tempfiles).
Packit bef1e6
 *		06 May 1995, limit scaling -- only shrink-to-fit.
Packit bef1e6
 *		29 Apr 1995, recognize 'rcsdiff -u' format.
Packit bef1e6
 *		26 Dec 1994, strip common pathname-prefix.
Packit bef1e6
 *		13 Nov 1994, added '-n' option.  Corrected logic of 'match'.
Packit bef1e6
 *		17 Jun 1994, ifdef-<string.h>
Packit bef1e6
 *		12 Jun 1994, recognize unified diff, and output of makepatch.
Packit bef1e6
 *		04 Oct 1993, merge multiple diff-files, busy message when the
Packit bef1e6
 *			     output is piped to a file.
Packit bef1e6
 *
Packit bef1e6
 * Function:	this program reads the output of 'diff' and displays a histogram
Packit bef1e6
 *		of the insertions/deletions/modifications per-file.
Packit bef1e6
 */
Packit bef1e6
Packit bef1e6
#if defined(HAVE_CONFIG_H)
Packit bef1e6
#include <config.h>
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#if defined(WIN32) && !defined(HAVE_CONFIG_H)
Packit bef1e6
#define HAVE_STDLIB_H
Packit bef1e6
#define HAVE_STRING_H
Packit bef1e6
#define HAVE_MALLOC_H
Packit bef1e6
#define HAVE_GETOPT_H
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#include <stdio.h>
Packit bef1e6
#include <ctype.h>
Packit bef1e6
Packit bef1e6
#ifdef HAVE_STRING_H
Packit bef1e6
#include <string.h>
Packit bef1e6
#else
Packit bef1e6
#include <strings.h>
Packit bef1e6
#define strchr index
Packit bef1e6
#define strrchr rindex
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifdef HAVE_STDLIB_H
Packit bef1e6
#include <stdlib.h>
Packit bef1e6
#else
Packit bef1e6
extern int atoi(const char *);
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifdef HAVE_UNISTD_H
Packit bef1e6
#include <unistd.h>
Packit bef1e6
#else
Packit bef1e6
extern int isatty(int);
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifdef HAVE_OPENDIR
Packit bef1e6
#include <dirent.h>
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifdef HAVE_MALLOC_H
Packit bef1e6
#include <malloc.h>
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#if defined(HAVE_SEARCH_H) && defined(HAVE_TSEARCH)
Packit bef1e6
#include <search.h>
Packit bef1e6
#else
Packit bef1e6
#undef HAVE_TSEARCH
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifdef HAVE_GETC_UNLOCKED
Packit bef1e6
#define MY_GETC getc_unlocked
Packit bef1e6
#else
Packit bef1e6
#define MY_GETC getc
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifdef HAVE_GETOPT_H
Packit bef1e6
#include <getopt.h>
Packit bef1e6
#elif !defined(HAVE_GETOPT_HEADER)
Packit bef1e6
extern int getopt(int, char *const *, const char *);
Packit bef1e6
extern char *optarg;
Packit bef1e6
extern int optind;
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#include <sys/types.h>
Packit bef1e6
#include <sys/stat.h>
Packit bef1e6
Packit bef1e6
#if defined(HAVE_POPEN) && !defined(HAVE_POPEN_PROTOTYPE)
Packit bef1e6
extern FILE *popen(const char *, const char *);
Packit bef1e6
extern int pclose(FILE *);
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#if !defined(EXIT_SUCCESS)
Packit bef1e6
#define EXIT_SUCCESS 0
Packit bef1e6
#define EXIT_FAILURE 1
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef BZCAT_PATH
Packit bef1e6
#define BZCAT_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef BZIP2_PATH
Packit bef1e6
#define BZIP2_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef COMPRESS_PATH
Packit bef1e6
#define COMPRESS_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef GZIP_PATH
Packit bef1e6
#define GZIP_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef LZCAT_PATH
Packit bef1e6
#define LZCAT_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef PCAT_PATH
Packit bef1e6
#define PCAT_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef UNCOMPRESS_PATH
Packit bef1e6
#define UNCOMPRESS_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef XZ_PATH
Packit bef1e6
#define XZ_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#ifndef ZCAT_PATH
Packit bef1e6
#define ZCAT_PATH ""
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
/******************************************************************************/
Packit bef1e6
Packit bef1e6
#if defined(__MINGW32__) || defined(WIN32)
Packit bef1e6
#define MKDIR(name,mode) mkdir(name)
Packit bef1e6
#else
Packit bef1e6
#define MKDIR(name,mode) mkdir(name,mode)
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#if defined(WIN32) && !defined(__MINGW32__)
Packit bef1e6
#define PATHSEP '\\'
Packit bef1e6
#else
Packit bef1e6
#define PATHSEP '/'
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#define DQUOTE  '"'
Packit bef1e6
#define SQUOTE  '\''
Packit bef1e6
#define EOS     '\0'
Packit bef1e6
#define BLANK   ' '
Packit bef1e6
Packit bef1e6
#define UC(c)   ((unsigned char)(c))
Packit bef1e6
Packit bef1e6
#ifndef OPT_TRACE
Packit bef1e6
#define OPT_TRACE 1
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#if OPT_TRACE
Packit bef1e6
#define TRACE(p) if (trace_opt) printf p
Packit bef1e6
#else
Packit bef1e6
#define TRACE(p)		/*nothing */
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
#define contain_any(s,reject) (strcspn(s,reject) != strlen(s))
Packit bef1e6
#define maximum(a,b) ((a) < (b) ? (b) : (a))
Packit bef1e6
Packit bef1e6
#define HAVE_NOTHING 0
Packit bef1e6
#define HAVE_GENERIC 1		/* e.g., "Index: foo" w/o pathname */
Packit bef1e6
#define HAVE_PATH    2		/* reference-file from "diff dirname/foo" */
Packit bef1e6
#define HAVE_PATH2   4		/* comparison-file from "diff dirname/foo" */
Packit bef1e6
Packit bef1e6
#define FMT_CONCISE  0
Packit bef1e6
#define FMT_NORMAL   1
Packit bef1e6
#define FMT_FILLED   2
Packit bef1e6
#define FMT_VERBOSE  4
Packit bef1e6
Packit bef1e6
typedef enum comment {
Packit bef1e6
    Normal, Only, OnlyLeft, OnlyRight, Binary, Differs, Either
Packit bef1e6
} Comment;
Packit bef1e6
Packit bef1e6
#define MARKS 4			/* each of +, - and ! */
Packit bef1e6
Packit bef1e6
typedef enum {
Packit bef1e6
    cInsert = 0,
Packit bef1e6
    cDelete,
Packit bef1e6
    cModify,
Packit bef1e6
    cEquals
Packit bef1e6
} Change;
Packit bef1e6
Packit bef1e6
#define InsOf(p) (p)->count[cInsert]	/* "+" count inserted lines */
Packit bef1e6
#define DelOf(p) (p)->count[cDelete]	/* "-" count deleted lines */
Packit bef1e6
#define ModOf(p) (p)->count[cModify]	/* "!" count modified lines */
Packit bef1e6
#define EqlOf(p) (p)->count[cEquals]	/* "=" count unmodified lines */
Packit bef1e6
Packit bef1e6
#define TotalOf(p) (InsOf(p) + DelOf(p) + ModOf(p) + EqlOf(p))
Packit bef1e6
#define for_each_mark(n) for (n = 0; n < num_marks; ++n)
Packit bef1e6
Packit bef1e6
typedef struct _data {
Packit bef1e6
    struct _data *link;
Packit bef1e6
    char *name;			/* the filename */
Packit bef1e6
    int copy;			/* true if filename is const-literal */
Packit bef1e6
    int base;			/* beginning of name if -p option used */
Packit bef1e6
    Comment cmt;
Packit bef1e6
    int pending;
Packit bef1e6
    long chunks;		/* total number of chunks */
Packit bef1e6
    long chunk[MARKS];		/* counts for the current chunk */
Packit bef1e6
    long count[MARKS];		/* counts for the file */
Packit bef1e6
} DATA;
Packit bef1e6
Packit bef1e6
typedef enum {
Packit bef1e6
    dcNone = 0,
Packit bef1e6
    dcBzip,
Packit bef1e6
    dcCompress,
Packit bef1e6
    dcGzip,
Packit bef1e6
    dcLzma,
Packit bef1e6
    dcPack,
Packit bef1e6
    dcXz,
Packit bef1e6
    dcEmpty
Packit bef1e6
} Decompress;
Packit bef1e6
Packit bef1e6
static const char marks[MARKS + 1] = "+-!=";
Packit bef1e6
static const int colors[MARKS + 1] =
Packit bef1e6
{2, 1, 6, 4};
Packit bef1e6
Packit bef1e6
static DATA *all_data;
Packit bef1e6
static const char *comment_opt = "";
Packit bef1e6
static char *path_opt = 0;
Packit bef1e6
static int count_files;		/* true if we count added/deleted files */
Packit bef1e6
static int format_opt = FMT_NORMAL;
Packit bef1e6
static int max_name_wide;	/* maximum amount reserved for filenames */
Packit bef1e6
static int max_width;		/* the specified width-limit */
Packit bef1e6
static int merge_names = 1;	/* true if we merge similar filenames */
Packit bef1e6
static int merge_opt = 0;	/* true if we merge ins/del as modified */
Packit bef1e6
static int min_name_wide;	/* minimum amount reserved for filenames */
Packit bef1e6
static int names_only;		/* true if we list filenames only */
Packit bef1e6
static int num_marks = 3;	/* 3 or 4, according to "-P" option */
Packit bef1e6
static int path_dest;		/* true if path_opt is destination (patched) */
Packit bef1e6
static int plot_width;		/* the amount left over for histogram */
Packit bef1e6
static int prefix_opt = -1;	/* if positive, controls stripping of PATHSEP */
Packit bef1e6
static int quiet = 0;		/* -q option */
Packit bef1e6
static int reverse_opt;		/* true if results are reversed */
Packit bef1e6
static int round_opt = 0;	/* if nonzero, round data for histogram */
Packit bef1e6
static int show_colors;		/* true if showing SGR colors */
Packit bef1e6
static int show_progress;	/* if not writing to tty, show progress */
Packit bef1e6
static int sort_names = 1;	/* true if we sort filenames */
Packit bef1e6
static int summary_only = 0;	/* true if only summary line is shown */
Packit bef1e6
static int suppress_binary = 0;	/* -b option */
Packit bef1e6
static int trim_escapes = 0;	/* -E option */
Packit bef1e6
static int table_opt = 0;	/* if 1/2, write table instead/also plot */
Packit bef1e6
static int trace_opt = 0;	/* if nonzero, write debugging information */
Packit bef1e6
static int unchanged = 0;	/* special-case for -S vs modified-files */
Packit bef1e6
static int verbose = 0;		/* -v option */
Packit bef1e6
static long plot_scale;		/* the effective scale (1:maximum) */
Packit bef1e6
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
static int use_tsearch;
Packit bef1e6
static void *sorted_data;
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
static int number_len = 5;
Packit bef1e6
static int prefix_len = -1;
Packit bef1e6
Packit bef1e6
/******************************************************************************/
Packit bef1e6
Packit bef1e6
#ifdef GCC_NORETURN
Packit bef1e6
static void failed(const char *) GCC_NORETURN;
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
failed(const char *s)
Packit bef1e6
{
Packit bef1e6
    perror(s);
Packit bef1e6
    exit(EXIT_FAILURE);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/* malloc wrapper that never returns NULL */
Packit bef1e6
static void *
Packit bef1e6
xmalloc(size_t s)
Packit bef1e6
{
Packit bef1e6
    void *p;
Packit bef1e6
    if ((p = malloc(s)) == NULL)
Packit bef1e6
	failed("malloc");
Packit bef1e6
    return p;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static mode_t
Packit bef1e6
get_stat(const char *name)
Packit bef1e6
{
Packit bef1e6
    struct stat sb;
Packit bef1e6
    int rc;
Packit bef1e6
#ifdef HAVE_LSTAT
Packit bef1e6
    rc = lstat(name, &sb);
Packit bef1e6
#else
Packit bef1e6
    rc = stat(name, &sb);
Packit bef1e6
#endif
Packit bef1e6
    return ((rc == 0) ? (sb.st_mode & S_IFMT) : 0);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
is_dir(const char *name)
Packit bef1e6
{
Packit bef1e6
    return get_stat(name) == S_IFDIR;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
is_file(const char *name)
Packit bef1e6
{
Packit bef1e6
    return get_stat(name) == S_IFREG;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
blip(int c)
Packit bef1e6
{
Packit bef1e6
    if (show_progress) {
Packit bef1e6
	(void) fputc(c, stderr);
Packit bef1e6
	(void) fflush(stderr);
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
new_string(const char *s)
Packit bef1e6
{
Packit bef1e6
    return strcpy((char *) xmalloc((size_t) (strlen(s) + 1)), s);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
compare_data(const void *a, const void *b)
Packit bef1e6
{
Packit bef1e6
    const DATA *p = (const DATA *) a;
Packit bef1e6
    const DATA *q = (const DATA *) b;
Packit bef1e6
    return strcmp(p->name + p->base, q->name + q->base);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
init_data(DATA * data, const char *name, int copy, int base)
Packit bef1e6
{
Packit bef1e6
    memset(data, 0, sizeof(*data));
Packit bef1e6
    data->name = (char *) name;
Packit bef1e6
    data->copy = copy;
Packit bef1e6
    data->base = base;
Packit bef1e6
    data->cmt = Normal;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static DATA *
Packit bef1e6
new_data(const char *name, int base)
Packit bef1e6
{
Packit bef1e6
    DATA *r = (DATA *) xmalloc(sizeof(DATA));
Packit bef1e6
Packit bef1e6
    init_data(r, new_string(name), 0, base);
Packit bef1e6
Packit bef1e6
    return r;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
static DATA *
Packit bef1e6
add_tsearch_data(const char *name, int base)
Packit bef1e6
{
Packit bef1e6
    DATA find;
Packit bef1e6
    DATA *result;
Packit bef1e6
    void *pp;
Packit bef1e6
Packit bef1e6
    init_data(&find, name, 1, base);
Packit bef1e6
    if ((pp = tfind(&find, &sorted_data, compare_data)) != 0) {
Packit bef1e6
	result = *(DATA **) pp;
Packit bef1e6
	return result;
Packit bef1e6
    }
Packit bef1e6
    result = new_data(name, base);
Packit bef1e6
    (void) tsearch(result, &sorted_data, compare_data);
Packit bef1e6
    result->link = all_data;
Packit bef1e6
    all_data = result;
Packit bef1e6
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
count_prefix(const char *name)
Packit bef1e6
{
Packit bef1e6
    int count = 0;
Packit bef1e6
    const char *s;
Packit bef1e6
    while ((s = strchr(name, PATHSEP)) != 0) {
Packit bef1e6
	name = s + 1;
Packit bef1e6
	++count;
Packit bef1e6
    }
Packit bef1e6
    return count;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static const char *
Packit bef1e6
skip_prefix(const char *name, int prefix, int *base)
Packit bef1e6
{
Packit bef1e6
    if (prefix >= 0) {
Packit bef1e6
	int n;
Packit bef1e6
	*base = 0;
Packit bef1e6
Packit bef1e6
	for (n = prefix; n > 0; n--) {
Packit bef1e6
	    const char *s = strchr(name + *base, PATHSEP);
Packit bef1e6
	    if (s == 0 || *++s == EOS) {
Packit bef1e6
		name = s;
Packit bef1e6
		break;
Packit bef1e6
	    }
Packit bef1e6
	    *base = (int) (s - name);
Packit bef1e6
	}
Packit bef1e6
	TRACE(("** base set to %d\n", *base));
Packit bef1e6
    }
Packit bef1e6
    return name;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static DATA *
Packit bef1e6
find_data(const char *name)
Packit bef1e6
{
Packit bef1e6
    DATA *p, *q, *r;
Packit bef1e6
    DATA find;
Packit bef1e6
    int base = 0;
Packit bef1e6
Packit bef1e6
    TRACE(("** find_data(%s)\n", name));
Packit bef1e6
Packit bef1e6
    /* Compute the base offset if the prefix option is used */
Packit bef1e6
    if (prefix_opt >= 0) {
Packit bef1e6
	(void) skip_prefix(name, prefix_opt, &base);
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    /* Insert into sorted list (usually sorted).  If we are not sorting or
Packit bef1e6
     * merging names, we fall off the end and link the new entry to the end of
Packit bef1e6
     * the list.  If the prefix option is used, the prefix is ignored by the
Packit bef1e6
     * merge and sort operations.
Packit bef1e6
     *
Packit bef1e6
     * If we have tsearch(), we will maintain the sorted list using it and
Packit bef1e6
     * tfind().
Packit bef1e6
     */
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
    if (use_tsearch) {
Packit bef1e6
	r = add_tsearch_data(name, base);
Packit bef1e6
    } else
Packit bef1e6
#endif
Packit bef1e6
    {
Packit bef1e6
	init_data(&find, name, 1, base);
Packit bef1e6
	for (p = all_data, q = 0; p != 0; q = p, p = p->link) {
Packit bef1e6
	    int cmp = compare_data(p, &find);
Packit bef1e6
	    if (merge_names && (cmp == 0))
Packit bef1e6
		return p;
Packit bef1e6
	    if (sort_names && (cmp > 0))
Packit bef1e6
		break;
Packit bef1e6
	}
Packit bef1e6
	r = new_data(name, base);
Packit bef1e6
	if (q != 0)
Packit bef1e6
	    q->link = r;
Packit bef1e6
	else
Packit bef1e6
	    all_data = r;
Packit bef1e6
Packit bef1e6
	r->link = p;
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    return r;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Remove a unneeded data item from the linked list.  Free the name as well.
Packit bef1e6
 */
Packit bef1e6
static int
Packit bef1e6
delink(DATA * data)
Packit bef1e6
{
Packit bef1e6
    DATA *p, *q;
Packit bef1e6
Packit bef1e6
    TRACE(("** delink '%s'\n", data->name));
Packit bef1e6
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
    if (use_tsearch) {
Packit bef1e6
	if (tdelete(data, &sorted_data, compare_data) == 0)
Packit bef1e6
	    return 0;
Packit bef1e6
    }
Packit bef1e6
#endif
Packit bef1e6
    for (p = all_data, q = 0; p != 0; q = p, p = p->link) {
Packit bef1e6
	if (p == data) {
Packit bef1e6
	    if (q != 0)
Packit bef1e6
		q->link = p->link;
Packit bef1e6
	    else
Packit bef1e6
		all_data = p->link;
Packit bef1e6
	    if (!p->copy)
Packit bef1e6
		free(p->name);
Packit bef1e6
	    free(p);
Packit bef1e6
	    return 1;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return 0;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Compare string 's' against a constant, returning either a pointer just
Packit bef1e6
 * past the matched part of 's' if it matches exactly, or null if a mismatch
Packit bef1e6
 * was found.
Packit bef1e6
 */
Packit bef1e6
static char *
Packit bef1e6
match(char *s, const char *p)
Packit bef1e6
{
Packit bef1e6
    int ok = 0;
Packit bef1e6
Packit bef1e6
    while (*s != EOS) {
Packit bef1e6
	if (*p == EOS) {
Packit bef1e6
	    ok = 1;
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
	if (*s++ != *p++)
Packit bef1e6
	    break;
Packit bef1e6
	if (*s == EOS && *p == EOS) {
Packit bef1e6
	    ok = 1;
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return ok ? s : 0;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
version_num(const char *s)
Packit bef1e6
{
Packit bef1e6
    int main_ver, sub_ver;
Packit bef1e6
    char temp[2];
Packit bef1e6
    return (sscanf(s, "%d.%d%c", &main_ver, &sub_ver, temp) == 2);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Check for a range of line-numbers, used in editing scripts.
Packit bef1e6
 */
Packit bef1e6
static int
Packit bef1e6
edit_range(const char *s)
Packit bef1e6
{
Packit bef1e6
    int first, last;
Packit bef1e6
    char temp[2];
Packit bef1e6
    return (sscanf(s, "%d,%d%c", &first, &last, temp) == 2)
Packit bef1e6
	|| (sscanf(s, "%d%c", &first, temp) == 1);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Decode a range for default diff.
Packit bef1e6
 */
Packit bef1e6
static int
Packit bef1e6
decode_default(char *s,
Packit bef1e6
	       long *first, long *first_size,
Packit bef1e6
	       long *second, long *second_size)
Packit bef1e6
{
Packit bef1e6
    int rc = 0;
Packit bef1e6
    char *next;
Packit bef1e6
Packit bef1e6
    if (isdigit(UC(*s))) {
Packit bef1e6
	*first_size = 1;
Packit bef1e6
	*second_size = 1;
Packit bef1e6
Packit bef1e6
	*first = strtol(s, &next, 10);
Packit bef1e6
	if (next != 0 && next != s) {
Packit bef1e6
	    if (*next == ',') {
Packit bef1e6
		s = ++next;
Packit bef1e6
		*first_size = strtol(s, &next, 10) + 1 - *first;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
	if (next != 0 && next != s) {
Packit bef1e6
	    switch (*next++) {
Packit bef1e6
	    case 'a':
Packit bef1e6
	    case 'c':
Packit bef1e6
	    case 'd':
Packit bef1e6
		s = next;
Packit bef1e6
		*second = strtol(s, &next, 10);
Packit bef1e6
		if (next != 0 && next != s) {
Packit bef1e6
		    if (*next == ',') {
Packit bef1e6
			s = ++next;
Packit bef1e6
			*second_size = strtol(s, &next, 10) + 1 - *second;
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
		if (next != 0 && next != s && *next == EOS)
Packit bef1e6
		    rc = 1;
Packit bef1e6
		break;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return rc;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Decode a range for unified diff.  Oddly, the comments in diffutils code
Packit bef1e6
 * claim that both numbers are line-numbers.  However, inspection of the output
Packit bef1e6
 * shows that the numbers are a line-number followed by a count.
Packit bef1e6
 */
Packit bef1e6
static int
Packit bef1e6
decode_range(const char *s, int *first, int *second)
Packit bef1e6
{
Packit bef1e6
    int rc = 0;
Packit bef1e6
    char check;
Packit bef1e6
Packit bef1e6
    if (isdigit(UC(*s))) {
Packit bef1e6
	if (sscanf(s, "%d,%d%c", first, second, &check) == 2) {
Packit bef1e6
	    TRACE(("** decode_range #1 first=%d, second=%d\n", *first, *second));
Packit bef1e6
	    rc = 1;
Packit bef1e6
	} else if (sscanf(s, "%d%c", first, &check) == 1) {
Packit bef1e6
	    *second = *first;	/* diffutils 2.7 does this */
Packit bef1e6
	    TRACE(("** decode_range #2 first=%d, second=%d\n", *first, *second));
Packit bef1e6
	    rc = 1;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return rc;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
HadDiffs(const DATA * data)
Packit bef1e6
{
Packit bef1e6
    return InsOf(data) != 0
Packit bef1e6
	|| DelOf(data) != 0
Packit bef1e6
	|| ModOf(data) != 0
Packit bef1e6
	|| data->cmt != Normal;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * If the given path is not one of the "ignore" paths, then return true.
Packit bef1e6
 */
Packit bef1e6
static int
Packit bef1e6
can_be_merged(const char *path)
Packit bef1e6
{
Packit bef1e6
    int result = 0;
Packit bef1e6
    if (strcmp(path, "")
Packit bef1e6
	&& strcmp(path, "/dev/null"))
Packit bef1e6
	result = 1;
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
is_leaf(const char *theLeaf, const char *path)
Packit bef1e6
{
Packit bef1e6
    char *s;
Packit bef1e6
Packit bef1e6
    if (strchr(theLeaf, PATHSEP) == 0
Packit bef1e6
	&& (s = strrchr(path, PATHSEP)) != 0
Packit bef1e6
	&& !strcmp(++s, theLeaf))
Packit bef1e6
	return 1;
Packit bef1e6
    return 0;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
trim_datapath(DATA ** datap, size_t length, int *localp)
Packit bef1e6
{
Packit bef1e6
    char *target = (*datap)->name;
Packit bef1e6
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
    /*
Packit bef1e6
     * If we are using tsearch(), make a local copy of the data
Packit bef1e6
     * so we can trim it without interfering with tsearch's
Packit bef1e6
     * notion of the ordering of data.  That will create some
Packit bef1e6
     * spurious empty data, so we add the changed() macro in a
Packit bef1e6
     * few places to skip over those.
Packit bef1e6
     */
Packit bef1e6
    if (use_tsearch) {
Packit bef1e6
	char *trim = new_string(target);
Packit bef1e6
	trim[length] = EOS;
Packit bef1e6
	*datap = add_tsearch_data(trim, (*datap)->base);
Packit bef1e6
	target = (*datap)->name;
Packit bef1e6
	free(trim);
Packit bef1e6
	*localp = 1;
Packit bef1e6
    } else
Packit bef1e6
#endif
Packit bef1e6
	target[length] = EOS;
Packit bef1e6
Packit bef1e6
    return target;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static size_t
Packit bef1e6
compare_tails(const char *target, const char *source, int *diff)
Packit bef1e6
{
Packit bef1e6
    size_t len1 = strlen(target);
Packit bef1e6
    size_t len2 = strlen(source);
Packit bef1e6
    size_t n;
Packit bef1e6
    size_t matched = 0;
Packit bef1e6
Packit bef1e6
    *diff = 0;
Packit bef1e6
    for (n = 1; n <= len1 && n <= len2; n++) {
Packit bef1e6
	if (target[len1 - n] != source[len2 - n]) {
Packit bef1e6
	    *diff = (int) n;
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
	if (source[len2 - n] == PATHSEP) {
Packit bef1e6
	    matched = n;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return matched;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * The 'data' parameter points to the first of two markers, while
Packit bef1e6
 * 'path' is the pathname from the second marker.
Packit bef1e6
 *
Packit bef1e6
 * On the first call for
Packit bef1e6
 * a given file, the 'data' parameter stores no differences.
Packit bef1e6
 */
Packit bef1e6
static char *
Packit bef1e6
do_merging(DATA * data, char *path, int *freed)
Packit bef1e6
{
Packit bef1e6
    char *target = reverse_opt ? path : data->name;
Packit bef1e6
    char *source = reverse_opt ? data->name : path;
Packit bef1e6
    char *result = source;
Packit bef1e6
    int diff;
Packit bef1e6
Packit bef1e6
    TRACE(("** do_merging(\"%s\",\"%s\") diffs:%d\n",
Packit bef1e6
	   data->name, path, HadDiffs(data)));
Packit bef1e6
Packit bef1e6
    *freed = 0;
Packit bef1e6
    if (!HadDiffs(data)) {
Packit bef1e6
Packit bef1e6
	if (is_leaf(target, source)) {
Packit bef1e6
	    TRACE(("** is_leaf: \"%s\" vs \"%s\"\n", target, source));
Packit bef1e6
	    if (reverse_opt) {
Packit bef1e6
		TRACE((".. no action @%d\n", __LINE__));
Packit bef1e6
	    } else {
Packit bef1e6
		*freed = delink(data);
Packit bef1e6
	    }
Packit bef1e6
	} else if (can_be_merged(target)
Packit bef1e6
		   && can_be_merged(source)) {
Packit bef1e6
	    size_t len1 = strlen(target);
Packit bef1e6
	    size_t len2 = strlen(source);
Packit bef1e6
	    int matched = 0;
Packit bef1e6
	    int local = 0;
Packit bef1e6
Packit bef1e6
	    /*
Packit bef1e6
	     * If the source/target differ only by some suffix, e.g., ".orig"
Packit bef1e6
	     * or ".bak", strip that off.  The target may may also be a
Packit bef1e6
	     * temporary filename (which would not be merged since it has no
Packit bef1e6
	     * apparent relationship to the current).
Packit bef1e6
	     */
Packit bef1e6
	    if (len1 > len2) {
Packit bef1e6
		if (!strncmp(target, source, len2)) {
Packit bef1e6
		    TRACE(("** trimming data \"%s\" to \"%.*s\"\n",
Packit bef1e6
			   target, (int) len2, target));
Packit bef1e6
		    if (reverse_opt) {
Packit bef1e6
			TRACE((".. no action @%d\n", __LINE__));
Packit bef1e6
		    } else {
Packit bef1e6
			target = trim_datapath(&data, len1 = len2, &local);
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
	    } else if (len1 < len2) {
Packit bef1e6
		if (!strncmp(target, source, len1)) {
Packit bef1e6
		    TRACE(("** trimming source \"%s\" to \"%.*s\"\n",
Packit bef1e6
			   source, (int) len1, source));
Packit bef1e6
		    if (reverse_opt) {
Packit bef1e6
			TRACE((".. no action @%d\n", __LINE__));
Packit bef1e6
		    } else {
Packit bef1e6
			source[len2 = len1] = EOS;
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
Packit bef1e6
	    /*
Packit bef1e6
	     * If there was no "-p" option, look for the best match by
Packit bef1e6
	     * stripping prefixes from both source/target strings.
Packit bef1e6
	     */
Packit bef1e6
	    if (prefix_opt < 0) {
Packit bef1e6
		/*
Packit bef1e6
		 * Now (whether or not we trimmed a suffix), scan back from the
Packit bef1e6
		 * end of source/target strings to find if they happen to share
Packit bef1e6
		 * a common ending, e.g., a/b/c versus d/b/c.  If the strings
Packit bef1e6
		 * are not identical, then 'diff' will be set, but if they have
Packit bef1e6
		 * a common ending then 'matched' will be set.
Packit bef1e6
		 */
Packit bef1e6
		diff = 0;
Packit bef1e6
		matched = (int) compare_tails(target, source, &diff);
Packit bef1e6
Packit bef1e6
		TRACE(("** merge @%d, prefix_opt=%d matched=%d diff=%d\n",
Packit bef1e6
		       __LINE__, prefix_opt, matched, diff));
Packit bef1e6
		if (matched != 0 && diff) {
Packit bef1e6
		    if (reverse_opt) {
Packit bef1e6
			TRACE((".. no action @%d\n", __LINE__));
Packit bef1e6
		    } else {
Packit bef1e6
			result = source + ((int) len2 - matched + 1);
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
Packit bef1e6
	    if (!local) {
Packit bef1e6
		if (reverse_opt) {
Packit bef1e6
		    TRACE((".. no action @%d\n", __LINE__));
Packit bef1e6
		} else {
Packit bef1e6
		    *freed = delink(data);
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	} else if (reverse_opt) {
Packit bef1e6
	    TRACE((".. no action @%d\n", __LINE__));
Packit bef1e6
	    if (can_be_merged(source)) {
Packit bef1e6
		TRACE(("** merge @%d\n", __LINE__));
Packit bef1e6
	    } else {
Packit bef1e6
		TRACE(("** do not merge, retain @%d\n", __LINE__));
Packit bef1e6
		/* must not merge, retain existing name */
Packit bef1e6
		result = target;
Packit bef1e6
	    }
Packit bef1e6
	} else {
Packit bef1e6
	    if (can_be_merged(source)) {
Packit bef1e6
		TRACE(("** merge @%d\n", __LINE__));
Packit bef1e6
		*freed = delink(data);
Packit bef1e6
	    } else {
Packit bef1e6
		TRACE(("** do not merge, retain @%d\n", __LINE__));
Packit bef1e6
		/* must not merge, retain existing name */
Packit bef1e6
		result = target;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
    } else if (reverse_opt) {
Packit bef1e6
	TRACE((".. no action @%d\n", __LINE__));
Packit bef1e6
	if (can_be_merged(source)) {
Packit bef1e6
	    TRACE(("** merge @%d\n", __LINE__));
Packit bef1e6
	    result = target;
Packit bef1e6
	} else {
Packit bef1e6
	    TRACE(("** do not merge, retain @%d\n", __LINE__));
Packit bef1e6
	}
Packit bef1e6
    } else {
Packit bef1e6
	if (can_be_merged(source)) {
Packit bef1e6
	    TRACE(("** merge @%d\n", __LINE__));
Packit bef1e6
	    if (merge_names
Packit bef1e6
		&& *target != '\0'
Packit bef1e6
		&& prefix_opt < 0) {
Packit bef1e6
		size_t matched = compare_tails(target, source, &diff);
Packit bef1e6
		if (matched)
Packit bef1e6
		    result = target + (int) (strlen(target) - matched);
Packit bef1e6
	    }
Packit bef1e6
	} else {
Packit bef1e6
	    TRACE(("** do not merge, retain @%d\n", __LINE__));
Packit bef1e6
	    result = target;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    TRACE(("** finish do_merging ->\"%s\"\n", result));
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
begin_data(const DATA * p)
Packit bef1e6
{
Packit bef1e6
    TRACE(("...begin_data(%s)\n", p->name));
Packit bef1e6
    if (!can_be_merged(p->name)
Packit bef1e6
	&& strchr(p->name, PATHSEP) != 0) {
Packit bef1e6
	TRACE(("** begin_data:HAVE_PATH\n"));
Packit bef1e6
	return HAVE_PATH;
Packit bef1e6
    }
Packit bef1e6
    TRACE(("** begin_data:HAVE_GENERIC\n"));
Packit bef1e6
    return HAVE_GENERIC;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
skip_blanks(char *s)
Packit bef1e6
{
Packit bef1e6
    while (isspace(UC(*s)))
Packit bef1e6
	++s;
Packit bef1e6
    return s;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Skip a filename, which may be in quotes, to allow embedded blanks in the
Packit bef1e6
 * name.
Packit bef1e6
 */
Packit bef1e6
static char *
Packit bef1e6
skip_filename(char *s)
Packit bef1e6
{
Packit bef1e6
    int delim = (*s == SQUOTE) ? SQUOTE : DQUOTE;
Packit bef1e6
Packit bef1e6
    if ((*s == delim) && (s[1] != EOS) && (strchr) (s + 1, delim) != 0) {
Packit bef1e6
	++s;
Packit bef1e6
	while (*s != EOS && (*s != delim) && isprint(UC(*s))) {
Packit bef1e6
	    ++s;
Packit bef1e6
	}
Packit bef1e6
	++s;
Packit bef1e6
    } else {
Packit bef1e6
	while (*s != EOS && isgraph(UC(*s))) {
Packit bef1e6
	    ++s;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return s;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
skip_options(char *params)
Packit bef1e6
{
Packit bef1e6
    while (*params != EOS) {
Packit bef1e6
	params = skip_blanks(params);
Packit bef1e6
	if (*params == '-') {
Packit bef1e6
	    while (isgraph(UC(*params)))
Packit bef1e6
		params++;
Packit bef1e6
	} else {
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return skip_blanks(params);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Strip single-quotes from a name (needed for recent makepatch versions).
Packit bef1e6
 */
Packit bef1e6
static void
Packit bef1e6
dequote(char *s)
Packit bef1e6
{
Packit bef1e6
    size_t len = strlen(s);
Packit bef1e6
    int n;
Packit bef1e6
    int delim = (*s == SQUOTE) ? SQUOTE : DQUOTE;
Packit bef1e6
Packit bef1e6
    if (*s == delim && len > 2 && s[len - 1] == delim) {
Packit bef1e6
	for (n = 0; (s[n] = s[n + 1]) != EOS; ++n) {
Packit bef1e6
	    ;
Packit bef1e6
	}
Packit bef1e6
	s[len - 2] = EOS;
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Allocate a fixed-buffer
Packit bef1e6
 */
Packit bef1e6
static void
Packit bef1e6
fixed_buffer(char **buffer, size_t want)
Packit bef1e6
{
Packit bef1e6
    *buffer = (char *) xmalloc(want);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Reallocate a fixed-buffer
Packit bef1e6
 */
Packit bef1e6
static void
Packit bef1e6
adjust_buffer(char **buffer, size_t want)
Packit bef1e6
{
Packit bef1e6
    if ((*buffer = (char *) realloc(*buffer, want)) == 0)
Packit bef1e6
	failed("realloc");
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Read until newline or end-of-file, allocating the line-buffer so it is long
Packit bef1e6
 * enough for the input.
Packit bef1e6
 */
Packit bef1e6
static int
Packit bef1e6
get_line(char **buffer, size_t *have, FILE *fp)
Packit bef1e6
{
Packit bef1e6
    int ch;
Packit bef1e6
    size_t used = 0;
Packit bef1e6
Packit bef1e6
    while ((ch = MY_GETC(fp)) != EOF) {
Packit bef1e6
	if (used + 2 > *have) {
Packit bef1e6
	    adjust_buffer(buffer, *have *= 2);
Packit bef1e6
	}
Packit bef1e6
	(*buffer)[used++] = (char) ch;
Packit bef1e6
	if (ch == '\n')
Packit bef1e6
	    break;
Packit bef1e6
    }
Packit bef1e6
    (*buffer)[used] = EOS;
Packit bef1e6
    return (used != 0);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
data_filename(const DATA * p)
Packit bef1e6
{
Packit bef1e6
    return p ? (p->name + (prefix_opt >= 0 ? p->base : prefix_len)) : "";
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
count_lines2(const char *filename)
Packit bef1e6
{
Packit bef1e6
    int result = 0;
Packit bef1e6
    int ch;
Packit bef1e6
    FILE *fp;
Packit bef1e6
Packit bef1e6
    TRACE(("count_lines %s\n", filename));
Packit bef1e6
    if ((fp = fopen(filename, "r")) != 0) {
Packit bef1e6
	result = 0;
Packit bef1e6
	while ((ch = MY_GETC(fp)) != EOF) {
Packit bef1e6
	    if (ch == '\n')
Packit bef1e6
		++result;
Packit bef1e6
	}
Packit bef1e6
	fclose(fp);
Packit bef1e6
	TRACE(("->%d lines\n", result));
Packit bef1e6
    } else {
Packit bef1e6
	fprintf(stderr, "Cannot open \"%s\"\n", filename);
Packit bef1e6
    }
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Count the (new)lines in a file, return -1 if the file is not found.
Packit bef1e6
 */
Packit bef1e6
static int
Packit bef1e6
count_lines(DATA * p)
Packit bef1e6
{
Packit bef1e6
    int result = -1;
Packit bef1e6
    char *filename = 0;
Packit bef1e6
    char *filetail = data_filename(p);
Packit bef1e6
    size_t want = strlen(path_opt) + 2 + strlen(filetail) + strlen(p->name);
Packit bef1e6
Packit bef1e6
    if ((filename = malloc(want)) != 0) {
Packit bef1e6
	int merge = 0;
Packit bef1e6
Packit bef1e6
	if (path_dest) {
Packit bef1e6
	    size_t path_len = strlen(path_opt);
Packit bef1e6
	    size_t tail_len;
Packit bef1e6
	    char *tail_sep = strchr(filetail, PATHSEP);
Packit bef1e6
Packit bef1e6
	    if (tail_sep != 0) {
Packit bef1e6
		tail_len = (size_t) (tail_sep - filetail);
Packit bef1e6
		if (tail_len != 0 && tail_len <= path_len) {
Packit bef1e6
		    if (tail_len < path_len
Packit bef1e6
			&& path_opt[path_len - tail_len - 1] != PATHSEP) {
Packit bef1e6
			merge = 0;
Packit bef1e6
		    } else if (!strncmp(path_opt + path_len - tail_len,
Packit bef1e6
					filetail,
Packit bef1e6
					tail_len - 1)) {
Packit bef1e6
			merge = 1;
Packit bef1e6
			if (path_len > tail_len) {
Packit bef1e6
			    sprintf(filename, "%.*s%c%s",
Packit bef1e6
				    (int) (path_len - tail_len),
Packit bef1e6
				    path_opt,
Packit bef1e6
				    PATHSEP,
Packit bef1e6
				    filetail);
Packit bef1e6
			} else {
Packit bef1e6
			    strcpy(filename, filetail);
Packit bef1e6
			}
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
	if (!merge) {
Packit bef1e6
	    if (path_opt) {
Packit bef1e6
		strcpy(filename, p->name);
Packit bef1e6
	    } else {
Packit bef1e6
		sprintf(filename, "%s%c%s", path_opt, PATHSEP, filetail);
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	result = count_lines2(filename);
Packit bef1e6
	free(filename);
Packit bef1e6
    } else {
Packit bef1e6
	failed("count_lines");
Packit bef1e6
    }
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
update_chunk(DATA * p, Change change)
Packit bef1e6
{
Packit bef1e6
    if (merge_opt) {
Packit bef1e6
	p->pending += 1;
Packit bef1e6
	p->chunk[change] += 1;
Packit bef1e6
    } else {
Packit bef1e6
	p->count[change] += 1;
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
finish_chunk(DATA * p)
Packit bef1e6
{
Packit bef1e6
    int i;
Packit bef1e6
Packit bef1e6
    if (p->pending) {
Packit bef1e6
	p->pending = 0;
Packit bef1e6
	p->chunks += 1;
Packit bef1e6
	if (merge_opt) {
Packit bef1e6
	    /*
Packit bef1e6
	     * This is crude, but to make it really precise we would have
Packit bef1e6
	     * to keep an array of line-numbers to which which in a chunk
Packit bef1e6
	     * are marked as insert/delete.
Packit bef1e6
	     */
Packit bef1e6
	    if (p->chunk[cInsert] && p->chunk[cDelete]) {
Packit bef1e6
		long change;
Packit bef1e6
		if (p->chunk[cInsert] > p->chunk[cDelete]) {
Packit bef1e6
		    change = p->chunk[cDelete];
Packit bef1e6
		} else {
Packit bef1e6
		    change = p->chunk[cInsert];
Packit bef1e6
		}
Packit bef1e6
		p->chunk[cInsert] -= change;
Packit bef1e6
		p->chunk[cDelete] -= change;
Packit bef1e6
		p->chunk[cModify] += change;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
	for_each_mark(i) {
Packit bef1e6
	    p->count[i] += p->chunk[i];
Packit bef1e6
	    p->chunk[i] = 0;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
#define date_delims(a,b) (((a)=='/' && (b)=='/') || ((a) == '-' && (b) == '-'))
Packit bef1e6
#define CASE_TRACE() TRACE(("** handle case for '%c' %d:%s\n", *buffer, ok, that ? that->name : ""))
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
do_file(FILE *fp, const char *default_name)
Packit bef1e6
{
Packit bef1e6
    static const char *only_stars = "***************";
Packit bef1e6
Packit bef1e6
    DATA dummy;
Packit bef1e6
    DATA *that = &dummy;
Packit bef1e6
    DATA *prev = 0;
Packit bef1e6
    char *buffer = 0;
Packit bef1e6
    char *b_fname = 0;
Packit bef1e6
    char *b_temp1 = 0;
Packit bef1e6
    char *b_temp2 = 0;
Packit bef1e6
    char *b_temp3 = 0;
Packit bef1e6
    size_t length = 0;
Packit bef1e6
    size_t fixed = 0;
Packit bef1e6
    int ok = HAVE_NOTHING;
Packit bef1e6
    int marker;
Packit bef1e6
    int freed = 0;
Packit bef1e6
Packit bef1e6
    int unified = 0;
Packit bef1e6
    int old_unify = 0;
Packit bef1e6
    int new_unify = 0;
Packit bef1e6
    int expect_unify = 0;
Packit bef1e6
Packit bef1e6
    long old_dft = 0;
Packit bef1e6
    long new_dft = 0;
Packit bef1e6
Packit bef1e6
    int context = 1;
Packit bef1e6
    int either = 0;
Packit bef1e6
Packit bef1e6
    char *s;
Packit bef1e6
#if OPT_TRACE
Packit bef1e6
    int line_no = 0;
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
    init_data(&dummy, "", 1, 0);
Packit bef1e6
Packit bef1e6
    fixed_buffer(&buffer, fixed = length = BUFSIZ);
Packit bef1e6
    fixed_buffer(&b_fname, length);
Packit bef1e6
    fixed_buffer(&b_temp1, length);
Packit bef1e6
    fixed_buffer(&b_temp2, length);
Packit bef1e6
    fixed_buffer(&b_temp3, length);
Packit bef1e6
Packit bef1e6
    while (get_line(&buffer, &length, fp)) {
Packit bef1e6
	/*
Packit bef1e6
	 * Adjust size of fixed-buffers so that a sscanf cannot overflow.
Packit bef1e6
	 */
Packit bef1e6
	if (length > fixed) {
Packit bef1e6
	    fixed = length;
Packit bef1e6
	    adjust_buffer(&b_fname, length);
Packit bef1e6
	    adjust_buffer(&b_temp1, length);
Packit bef1e6
	    adjust_buffer(&b_temp2, length);
Packit bef1e6
	    adjust_buffer(&b_temp3, length);
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * Trim trailing newline.
Packit bef1e6
	 */
Packit bef1e6
	for (s = buffer + strlen(buffer); s > buffer; s--) {
Packit bef1e6
	    if ((UC(s[-1]) == '\n') || (UC(s[-1]) == '\r'))
Packit bef1e6
		s[-1] = EOS;
Packit bef1e6
	    else
Packit bef1e6
		break;
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * Trim escapes from colordiff.
Packit bef1e6
	 */
Packit bef1e6
#define isFINAL(c) (UC(*s) >= '\140' && UC(*s) <= '\176')
Packit bef1e6
	if (trim_escapes && (strchr(buffer, '\033') != 0)) {
Packit bef1e6
	    char *d = buffer;
Packit bef1e6
	    s = d;
Packit bef1e6
	    while (*s != '\0') {
Packit bef1e6
		if (*s == '\033') {
Packit bef1e6
		    while (*s != '\0' && !isFINAL(*s)) {
Packit bef1e6
			++s;
Packit bef1e6
		    }
Packit bef1e6
		    if (*s != '\0') {
Packit bef1e6
			++s;
Packit bef1e6
			continue;
Packit bef1e6
		    } else {
Packit bef1e6
			break;
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
		*d++ = *s++;
Packit bef1e6
	    }
Packit bef1e6
	    *d = '\0';
Packit bef1e6
	}
Packit bef1e6
	++line_no;
Packit bef1e6
	TRACE(("[%05d] %s\n", line_no, buffer));
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * "patch -U" can create ".rej" files lacking a filename header,
Packit bef1e6
	 * in unified format.  Check for those.
Packit bef1e6
	 */
Packit bef1e6
	if (line_no == 1 && !strncmp(buffer, "@@", (size_t) 2)) {
Packit bef1e6
	    unified = 2;
Packit bef1e6
	    that = find_data(default_name);
Packit bef1e6
	    ok = begin_data(that);
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * The lines identifying files in a context diff depend on how it was
Packit bef1e6
	 * invoked.  But after the header, each chunk begins with a line
Packit bef1e6
	 * containing 15 *'s.  Each chunk may contain a line-range with '***'
Packit bef1e6
	 * for the "before", and a line-range with '---' for the "after".  The
Packit bef1e6
	 * part of the chunk depicting the deletion may be absent, though the
Packit bef1e6
	 * edit line is present.
Packit bef1e6
	 *
Packit bef1e6
	 * The markers for unified diff are a little different from the normal
Packit bef1e6
	 * context-diff.  Also, the edit-lines in a unified diff won't have a
Packit bef1e6
	 * space in column 2.  Because of the missing space, we have to count
Packit bef1e6
	 * lines to ensure we do not confuse the marker lines.
Packit bef1e6
	 */
Packit bef1e6
	marker = 0;
Packit bef1e6
	if (that != &dummy && !strcmp(buffer, only_stars)) {
Packit bef1e6
	    finish_chunk(that);
Packit bef1e6
	    TRACE(("** begin context chunk\n"));
Packit bef1e6
	    context = 2;
Packit bef1e6
	} else if (line_no == 1 && !strcmp(buffer, only_stars)) {
Packit bef1e6
	    TRACE(("** begin context chunk\n"));
Packit bef1e6
	    context = 2;
Packit bef1e6
	    that = find_data(default_name);
Packit bef1e6
	    ok = begin_data(that);
Packit bef1e6
	} else if (context == 2 && match(buffer, "*** ")) {
Packit bef1e6
	    context = 1;
Packit bef1e6
	} else if (context == 1 && match(buffer, "--- ")) {
Packit bef1e6
	    marker = 1;
Packit bef1e6
	    context = 0;
Packit bef1e6
	} else if (match(buffer, "*** ")) {
Packit bef1e6
	} else if ((old_unify + new_unify) == 0 && match(buffer, "==== ")) {
Packit bef1e6
	    finish_chunk(that);
Packit bef1e6
	    unified = 2;
Packit bef1e6
	} else if ((old_unify + new_unify) == 0 && match(buffer, "--- ")) {
Packit bef1e6
	    finish_chunk(that);
Packit bef1e6
	    marker = unified = 1;
Packit bef1e6
	} else if ((old_unify + new_unify) == 0 && match(buffer, "+++ ")) {
Packit bef1e6
	    marker = unified = 2;
Packit bef1e6
	} else if (unified == 2
Packit bef1e6
		   || ((old_unify + new_unify) == 0 && (*buffer == '@'))) {
Packit bef1e6
	    finish_chunk(that);
Packit bef1e6
	    unified = 0;
Packit bef1e6
	    if (*buffer == '@') {
Packit bef1e6
		int old_base, new_base, old_size, new_size;
Packit bef1e6
		char test_at;
Packit bef1e6
Packit bef1e6
		old_unify = new_unify = 0;
Packit bef1e6
		if (sscanf(buffer, "@@ -%[0-9,] +%[0-9,] @%c",
Packit bef1e6
			   b_temp1,
Packit bef1e6
			   b_temp2,
Packit bef1e6
			   &test_at) == 3
Packit bef1e6
		    && test_at == '@'
Packit bef1e6
		    && decode_range(b_temp1, &old_base, &old_size)
Packit bef1e6
		    && decode_range(b_temp2, &new_base, &new_size)) {
Packit bef1e6
		    old_unify = old_size;
Packit bef1e6
		    new_unify = new_size;
Packit bef1e6
		    unified = -1;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	} else if (unified == 1 && !context) {
Packit bef1e6
	    /*
Packit bef1e6
	     * If unified==1, we guessed we would find a "+++" line, but since
Packit bef1e6
	     * we are here, we did not find that.  The context check ensures
Packit bef1e6
	     * we do not mistake the "---" for a unified diff with that for
Packit bef1e6
	     * a context diff's "after" line-range.
Packit bef1e6
	     *
Packit bef1e6
	     * If we guessed wrong, then we probably found a data line with
Packit bef1e6
	     * "--" in the first two columns of the diff'd file.
Packit bef1e6
	     */
Packit bef1e6
	    unified = 0;
Packit bef1e6
	    TRACE(("?? Expected \"+++\" for unified diff\n"));
Packit bef1e6
	    if (prev != 0
Packit bef1e6
		&& prev != that
Packit bef1e6
		&& InsOf(that) == 0
Packit bef1e6
		&& DelOf(that) == 0
Packit bef1e6
		&& strcmp(prev->name, that->name)) {
Packit bef1e6
		TRACE(("?? giveup on %ld/%ld %s\n", InsOf(that),
Packit bef1e6
		       DelOf(that), that->name));
Packit bef1e6
		TRACE(("?? revert to %ld/%ld %s\n", InsOf(prev),
Packit bef1e6
		       DelOf(prev), prev->name));
Packit bef1e6
		(void) delink(that);
Packit bef1e6
		that = prev;
Packit bef1e6
		update_chunk(that, cDelete);
Packit bef1e6
	    }
Packit bef1e6
	} else if (old_unify + new_unify) {
Packit bef1e6
	    switch (*buffer) {
Packit bef1e6
	    case '-':
Packit bef1e6
		if (old_unify)
Packit bef1e6
		    --old_unify;
Packit bef1e6
		break;
Packit bef1e6
	    case '+':
Packit bef1e6
		if (new_unify)
Packit bef1e6
		    --new_unify;
Packit bef1e6
		break;
Packit bef1e6
	    case EOS:
Packit bef1e6
	    case ' ':
Packit bef1e6
		if (old_unify)
Packit bef1e6
		    --old_unify;
Packit bef1e6
		if (new_unify)
Packit bef1e6
		    --new_unify;
Packit bef1e6
		break;
Packit bef1e6
	    case '\\':
Packit bef1e6
		if (strstr(buffer, "newline") != 0) {
Packit bef1e6
		    break;
Packit bef1e6
		}
Packit bef1e6
		/* FALLTHRU */
Packit bef1e6
	    default:
Packit bef1e6
		TRACE(("?? expected more in chunk\n"));
Packit bef1e6
		old_unify = new_unify = 0;
Packit bef1e6
		break;
Packit bef1e6
	    }
Packit bef1e6
	    if (!(old_unify + new_unify)) {
Packit bef1e6
		expect_unify = 2;
Packit bef1e6
	    }
Packit bef1e6
	} else {
Packit bef1e6
	    long old_base, new_base;
Packit bef1e6
Packit bef1e6
	    unified = 0;
Packit bef1e6
Packit bef1e6
	    if (line_no == 1
Packit bef1e6
		&& decode_default(buffer,
Packit bef1e6
				  &old_base, &old_dft,
Packit bef1e6
				  &new_base, &new_dft)) {
Packit bef1e6
		TRACE(("DFT %ld,%ld -> %ld,%ld\n",
Packit bef1e6
		       old_base, old_base + old_dft - 1,
Packit bef1e6
		       new_base, new_base + new_dft - 1));
Packit bef1e6
		finish_chunk(that);
Packit bef1e6
		that = find_data("unknown");
Packit bef1e6
		ok = begin_data(that);
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * If the previous line ended a chunk of a unified diff, we may begin
Packit bef1e6
	 * another chunk, or begin another type of diff.  If neither, do not
Packit bef1e6
	 * continue to accumulate counts for the unified diff which has ended.
Packit bef1e6
	 */
Packit bef1e6
	if (expect_unify != 0) {
Packit bef1e6
	    if (expect_unify-- == 1) {
Packit bef1e6
		if (unified == 0) {
Packit bef1e6
		    TRACE(("?? did not get chunk\n"));
Packit bef1e6
		    finish_chunk(that);
Packit bef1e6
		    that = &dummy;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * Override the beginning of the line to simplify the case statement
Packit bef1e6
	 * below.
Packit bef1e6
	 */
Packit bef1e6
	if (marker > 0) {
Packit bef1e6
	    TRACE(("** have marker=%d, override %s\n", marker, buffer));
Packit bef1e6
	    (void) strncpy(buffer, "***", (size_t) 3);
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * Use the first character of the input line to determine its
Packit bef1e6
	 * type:
Packit bef1e6
	 */
Packit bef1e6
	switch (*buffer) {
Packit bef1e6
	case 'O':		/* Only */
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if (match(buffer, "Only in ")) {
Packit bef1e6
		char *path = buffer + 8;
Packit bef1e6
		int found = 0;
Packit bef1e6
		for (s = path; *s != EOS; s++) {
Packit bef1e6
		    if (match(s, ": ")) {
Packit bef1e6
			found = 1;
Packit bef1e6
			*s++ = PATHSEP;
Packit bef1e6
			while ((s[0] = s[1]) != EOS)
Packit bef1e6
			    s++;
Packit bef1e6
			break;
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
		if (found) {
Packit bef1e6
		    blip('.');
Packit bef1e6
		    finish_chunk(that);
Packit bef1e6
		    that = find_data(path);
Packit bef1e6
		    that->cmt = Only;
Packit bef1e6
		    ok = HAVE_NOTHING;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	    /*
Packit bef1e6
	     * Several different scripts produce "Index:" lines
Packit bef1e6
	     * (e.g., "makepatch").  Not all bother to put the
Packit bef1e6
	     * pathname of the files; some put only the leaf names.
Packit bef1e6
	     */
Packit bef1e6
	case 'I':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if ((s = match(buffer, "Index: ")) != 0) {
Packit bef1e6
		s = skip_blanks(s);
Packit bef1e6
		dequote(s);
Packit bef1e6
		blip('.');
Packit bef1e6
		finish_chunk(that);
Packit bef1e6
		s = do_merging(that, s, &freed);
Packit bef1e6
		that = find_data(s);
Packit bef1e6
		ok = begin_data(that);
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	case 'd':		/* diff command trace */
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if ((s = match(buffer, "diff ")) != 0
Packit bef1e6
		&& *(s = skip_options(s)) != EOS) {
Packit bef1e6
		if (reverse_opt) {
Packit bef1e6
		    *skip_filename(s) = EOS;
Packit bef1e6
		} else {
Packit bef1e6
		    s = skip_filename(s);
Packit bef1e6
		    s = skip_blanks(s);
Packit bef1e6
		}
Packit bef1e6
		dequote(s);
Packit bef1e6
		blip('.');
Packit bef1e6
		finish_chunk(that);
Packit bef1e6
		s = do_merging(that, s, &freed);
Packit bef1e6
		that = find_data(s);
Packit bef1e6
		ok = begin_data(that);
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	case '*':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if (!(ok & HAVE_PATH)) {
Packit bef1e6
		int ddd, hour, minute, second;
Packit bef1e6
		int day, month, year;
Packit bef1e6
		char yrmon, monday;
Packit bef1e6
Packit bef1e6
		/* check for tab-delimited first, so we can
Packit bef1e6
		 * accept filenames containing spaces.
Packit bef1e6
		 */
Packit bef1e6
		if (sscanf(buffer,
Packit bef1e6
			   "*** %[^\t]\t%[^ ] %[^ ] %d %d:%d:%d %d",
Packit bef1e6
			   b_fname,
Packit bef1e6
			   b_temp2, b_temp3, &ddd,
Packit bef1e6
			   &hour, &minute, &second, &year) == 8
Packit bef1e6
		    || (sscanf(buffer,
Packit bef1e6
			       "*** %[^\t]\t%d%c%d%c%d %d:%d:%d",
Packit bef1e6
			       b_fname,
Packit bef1e6
			       &year, &yrmon, &month, &monday, &day,
Packit bef1e6
			       &hour, &minute, &second) == 9
Packit bef1e6
			&& date_delims(yrmon, monday)
Packit bef1e6
			&& !version_num(b_fname))
Packit bef1e6
		    || (sscanf(buffer,
Packit bef1e6
			       "*** %[^\t]\t(%[^)])\t(%[^)])",
Packit bef1e6
			       b_fname, b_temp1, b_temp2) == 3
Packit bef1e6
			&& !version_num(b_fname))
Packit bef1e6
		    || sscanf(buffer,
Packit bef1e6
			      "*** %[^\t ]%[\t ]%[^ ] %[^ ] %d %d:%d:%d %d",
Packit bef1e6
			      b_fname,
Packit bef1e6
			      b_temp1,
Packit bef1e6
			      b_temp2, b_temp3, &ddd,
Packit bef1e6
			      &hour, &minute, &second, &year) == 9
Packit bef1e6
		    || (sscanf(buffer,
Packit bef1e6
			       "*** %[^\t ]%[\t ]%d%c%d%c%d %d:%d:%d",
Packit bef1e6
			       b_fname,
Packit bef1e6
			       b_temp1,
Packit bef1e6
			       &year, &yrmon, &month, &monday, &day,
Packit bef1e6
			       &hour, &minute, &second) == 10
Packit bef1e6
			&& date_delims(yrmon, monday)
Packit bef1e6
			&& !version_num(b_fname))
Packit bef1e6
		    || (sscanf(buffer,
Packit bef1e6
			       "*** %[^\t ]%[\t ]",
Packit bef1e6
			       b_fname,
Packit bef1e6
			       b_temp1) >= 1
Packit bef1e6
			&& !version_num(b_fname)
Packit bef1e6
			&& !contain_any(b_fname, "*")
Packit bef1e6
			&& !edit_range(b_fname))
Packit bef1e6
		    ) {
Packit bef1e6
		    prev = that;
Packit bef1e6
		    finish_chunk(that);
Packit bef1e6
		    dequote(b_fname);
Packit bef1e6
		    s = do_merging(that, b_fname, &freed);
Packit bef1e6
		    if (freed)
Packit bef1e6
			prev = 0;
Packit bef1e6
		    that = find_data(s);
Packit bef1e6
		    ok = begin_data(that);
Packit bef1e6
		    TRACE(("** after merge:%d:%s\n", ok, s));
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	case '=':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if (!(ok & HAVE_PATH)) {
Packit bef1e6
		int rev;
Packit bef1e6
Packit bef1e6
		if (((sscanf(buffer,
Packit bef1e6
			     "==== %[^\t #]#%d - %[^\t ]",
Packit bef1e6
			     b_fname,
Packit bef1e6
			     &rev,
Packit bef1e6
			     b_temp1) == 3)
Packit bef1e6
		     || ((sscanf(buffer,
Packit bef1e6
				 "==== %[^\t #]#%d (%[^)]) - %[^\t ]",
Packit bef1e6
				 b_fname,
Packit bef1e6
				 &rev,
Packit bef1e6
				 b_temp1,
Packit bef1e6
				 b_temp2) == 4)))
Packit bef1e6
		    && !version_num(b_fname)
Packit bef1e6
		    && !contain_any(b_fname, "*")
Packit bef1e6
		    && !edit_range(b_fname)) {
Packit bef1e6
		    TRACE(("** found p4-diff\n"));
Packit bef1e6
		    prev = that;
Packit bef1e6
		    finish_chunk(that);
Packit bef1e6
		    dequote(b_fname);
Packit bef1e6
		    s = do_merging(that, b_fname, &freed);
Packit bef1e6
		    if (freed)
Packit bef1e6
			prev = 0;
Packit bef1e6
		    that = find_data(s);
Packit bef1e6
		    ok = begin_data(that);
Packit bef1e6
		    TRACE(("** after merge:%d:%s\n", ok, s));
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	case '+':
Packit bef1e6
	    /* FALL-THRU */
Packit bef1e6
	case '>':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if (ok) {
Packit bef1e6
		update_chunk(that, cInsert);
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	case '-':
Packit bef1e6
	    if (!ok) {
Packit bef1e6
		CASE_TRACE();
Packit bef1e6
		break;
Packit bef1e6
	    }
Packit bef1e6
	    if (!unified && !strcmp(buffer, "---")) {
Packit bef1e6
		CASE_TRACE();
Packit bef1e6
		break;
Packit bef1e6
	    }
Packit bef1e6
	    /* fall-thru */
Packit bef1e6
	case '<':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if (ok) {
Packit bef1e6
		update_chunk(that, cDelete);
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	case '!':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if (ok) {
Packit bef1e6
		update_chunk(that, cModify);
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	    /* Expecting "Files XXX and YYY differ" */
Packit bef1e6
	case 'F':		/* FALL-THRU */
Packit bef1e6
	case 'f':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if ((s = match(buffer + 1, "iles ")) != 0) {
Packit bef1e6
		char *first = skip_blanks(s);
Packit bef1e6
		/* blindly assume the first filename does not contain " and " */
Packit bef1e6
		char *at_and = strstr(s, " and ");
Packit bef1e6
		s = strrchr(buffer, BLANK);
Packit bef1e6
		if ((at_and != NULL) && !strcmp(s, " differ")) {
Packit bef1e6
		    char *second = skip_blanks(at_and + 5);
Packit bef1e6
Packit bef1e6
		    if (reverse_opt) {
Packit bef1e6
			*at_and = EOS;
Packit bef1e6
			s = first;
Packit bef1e6
		    } else {
Packit bef1e6
			*s = EOS;
Packit bef1e6
			s = second;
Packit bef1e6
		    }
Packit bef1e6
		    blip('.');
Packit bef1e6
		    finish_chunk(that);
Packit bef1e6
		    that = find_data(s);
Packit bef1e6
		    that->cmt = Either;
Packit bef1e6
		    ok = HAVE_NOTHING;
Packit bef1e6
		    either = 1;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
	    /* Expecting "Binary files XXX and YYY differ" */
Packit bef1e6
	case 'B':		/* FALL-THRU */
Packit bef1e6
	case 'b':
Packit bef1e6
	    CASE_TRACE();
Packit bef1e6
	    if ((s = match(buffer + 1, "inary files ")) != 0) {
Packit bef1e6
		char *first = skip_blanks(s);
Packit bef1e6
		/* blindly assume the first filename does not contain " and " */
Packit bef1e6
		char *at_and = strstr(s, " and ");
Packit bef1e6
		s = strrchr(buffer, BLANK);
Packit bef1e6
		if ((at_and != NULL) && !strcmp(s, " differ")) {
Packit bef1e6
		    char *second = skip_blanks(at_and + 5);
Packit bef1e6
Packit bef1e6
		    if (reverse_opt) {
Packit bef1e6
			*at_and = EOS;
Packit bef1e6
			s = first;
Packit bef1e6
		    } else {
Packit bef1e6
			*s = EOS;
Packit bef1e6
			s = second;
Packit bef1e6
		    }
Packit bef1e6
		    blip('.');
Packit bef1e6
		    finish_chunk(that);
Packit bef1e6
		    that = find_data(s);
Packit bef1e6
		    that->cmt = Binary;
Packit bef1e6
		    ok = HAVE_NOTHING;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    blip('\n');
Packit bef1e6
Packit bef1e6
    finish_chunk(that);
Packit bef1e6
    finish_chunk(&dummy);
Packit bef1e6
Packit bef1e6
    if (either) {
Packit bef1e6
	int pass;
Packit bef1e6
	int fixup_diffs = 0;
Packit bef1e6
Packit bef1e6
	for (pass = 0; pass < 2; ++pass) {
Packit bef1e6
	    DATA *p;
Packit bef1e6
	    for (p = all_data; p; p = p->link) {
Packit bef1e6
		switch (p->cmt) {
Packit bef1e6
		default:
Packit bef1e6
		    break;
Packit bef1e6
		case Normal:
Packit bef1e6
		    fixup_diffs = 1;
Packit bef1e6
		    break;
Packit bef1e6
		case Either:
Packit bef1e6
		    if (pass) {
Packit bef1e6
			if (fixup_diffs) {
Packit bef1e6
			    p->cmt = Binary;
Packit bef1e6
			} else {
Packit bef1e6
			    p->cmt = Differs;
Packit bef1e6
			}
Packit bef1e6
		    }
Packit bef1e6
		    break;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    free(buffer);
Packit bef1e6
    free(b_fname);
Packit bef1e6
    free(b_temp1);
Packit bef1e6
    free(b_temp2);
Packit bef1e6
    free(b_temp3);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
show_color(int color)
Packit bef1e6
{
Packit bef1e6
    if (color >= 0)
Packit bef1e6
	printf("\033[%dm", color + 30);
Packit bef1e6
    else
Packit bef1e6
	printf("\033[0;39m");
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static long
Packit bef1e6
plot_bar(long count, int c, int color)
Packit bef1e6
{
Packit bef1e6
    long result = count;
Packit bef1e6
Packit bef1e6
    if (show_colors && result != 0)
Packit bef1e6
	show_color(color);
Packit bef1e6
Packit bef1e6
    while (--count >= 0)
Packit bef1e6
	(void) putchar(c);
Packit bef1e6
Packit bef1e6
    if (show_colors && result != 0)
Packit bef1e6
	show_color(-1);
Packit bef1e6
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Each call to 'plot_num()' prints a scaled bar of 'c' characters.  The
Packit bef1e6
 * 'extra' parameter is used to keep the accumulated error in the bar's total
Packit bef1e6
 * length from getting large.
Packit bef1e6
 */
Packit bef1e6
static long
Packit bef1e6
plot_num(long num_value, int c, int color, long *extra)
Packit bef1e6
{
Packit bef1e6
    long product;
Packit bef1e6
    long result = 0;
Packit bef1e6
Packit bef1e6
    /* the value to plot */
Packit bef1e6
    /* character to display in the bar */
Packit bef1e6
    /* accumulated error in the bar */
Packit bef1e6
    if (num_value) {
Packit bef1e6
	product = (plot_width * num_value);
Packit bef1e6
	result = ((product + *extra) / plot_scale);
Packit bef1e6
	*extra = product - (result * plot_scale) - *extra;
Packit bef1e6
	plot_bar(result, c, color);
Packit bef1e6
    }
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static long
Packit bef1e6
plot_round1(const long num[MARKS])
Packit bef1e6
{
Packit bef1e6
    long result = 0;
Packit bef1e6
    long scaled[MARKS];
Packit bef1e6
    long remain[MARKS];
Packit bef1e6
    long want = 0;
Packit bef1e6
    long have = 0;
Packit bef1e6
    long half = (plot_scale / 2);
Packit bef1e6
    int i, j;
Packit bef1e6
Packit bef1e6
    for_each_mark(i) {
Packit bef1e6
	long product = (plot_width * num[i]);
Packit bef1e6
	scaled[i] = (product / plot_scale);
Packit bef1e6
	remain[i] = (product % plot_scale);
Packit bef1e6
	want += product;
Packit bef1e6
	have += product - remain[i];
Packit bef1e6
    }
Packit bef1e6
    while (want > have) {
Packit bef1e6
	j = -1;
Packit bef1e6
	for_each_mark(i) {
Packit bef1e6
	    if (remain[i] != 0
Packit bef1e6
		&& (remain[i] > (j >= 0 ? remain[j] : half))) {
Packit bef1e6
		j = i;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
	if (j >= 0) {
Packit bef1e6
	    have += remain[j];
Packit bef1e6
	    remain[j] = 0;
Packit bef1e6
	    scaled[j] += 1;
Packit bef1e6
	} else {
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    for_each_mark(i) {
Packit bef1e6
	plot_bar(scaled[i], marks[i], colors[i]);
Packit bef1e6
	result += scaled[i];
Packit bef1e6
    }
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Print a scaled bar of characters, where c[0] is for insertions, c[1]
Packit bef1e6
 * for deletions and c[2] for modifications. The num array contains the
Packit bef1e6
 * count for each type of change, in the same order.
Packit bef1e6
 */
Packit bef1e6
static long
Packit bef1e6
plot_round2(const long num[MARKS])
Packit bef1e6
{
Packit bef1e6
    long result = 0;
Packit bef1e6
    long scaled[MARKS];
Packit bef1e6
    long remain[MARKS];
Packit bef1e6
    long total = 0;
Packit bef1e6
    int i;
Packit bef1e6
Packit bef1e6
    for (i = 0; i < MARKS; i++)
Packit bef1e6
	total += num[i];
Packit bef1e6
Packit bef1e6
    if (total == 0)
Packit bef1e6
	return result;
Packit bef1e6
Packit bef1e6
    total = (total * plot_width + (plot_scale / 2)) / plot_scale;
Packit bef1e6
    /* display at least one character */
Packit bef1e6
    if (total == 0)
Packit bef1e6
	total++;
Packit bef1e6
Packit bef1e6
    for_each_mark(i) {
Packit bef1e6
	scaled[i] = num[i] * plot_width / plot_scale;
Packit bef1e6
	remain[i] = num[i] * plot_width - scaled[i] * plot_scale;
Packit bef1e6
	total -= scaled[i];
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    /* assign the missing chars using the largest remainder algo */
Packit bef1e6
    while (total) {
Packit bef1e6
	int largest, largest_count;	/* largest is a bit field */
Packit bef1e6
	long max_remain;
Packit bef1e6
Packit bef1e6
	/* search for the largest remainder */
Packit bef1e6
	largest = largest_count = 0;
Packit bef1e6
	max_remain = 0;
Packit bef1e6
	for_each_mark(i) {
Packit bef1e6
	    if (remain[i] > max_remain) {
Packit bef1e6
		largest = 1 << i;
Packit bef1e6
		largest_count = 1;
Packit bef1e6
		max_remain = remain[i];
Packit bef1e6
	    } else if (remain[i] == max_remain) {	/* ex aequo */
Packit bef1e6
		largest |= 1 << i;
Packit bef1e6
		largest_count++;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	/* if there are more greatest remainders than characters
Packit bef1e6
	   missing, don't assign them at all */
Packit bef1e6
	if (total < largest_count)
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	/* allocate the extra characters */
Packit bef1e6
	for_each_mark(i) {
Packit bef1e6
	    if (largest & (1 << i)) {
Packit bef1e6
		scaled[i]++;
Packit bef1e6
		total--;
Packit bef1e6
		remain[i] -= plot_width;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    for_each_mark(i) {
Packit bef1e6
	result += plot_bar(scaled[i], marks[i], colors[i]);
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
plot_numbers(const DATA * p)
Packit bef1e6
{
Packit bef1e6
    long temp = 0;
Packit bef1e6
    long used = 0;
Packit bef1e6
    int i;
Packit bef1e6
Packit bef1e6
    printf("%5ld ", TotalOf(p));
Packit bef1e6
Packit bef1e6
    if (format_opt & FMT_VERBOSE) {
Packit bef1e6
	printf("%5ld ", InsOf(p));
Packit bef1e6
	printf("%5ld ", DelOf(p));
Packit bef1e6
	printf("%5ld ", ModOf(p));
Packit bef1e6
	if (path_opt)
Packit bef1e6
	    printf("%5ld ", EqlOf(p));
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    if (format_opt == FMT_CONCISE) {
Packit bef1e6
	for_each_mark(i) {
Packit bef1e6
	    printf("\t%ld %c", p->count[i], marks[i]);
Packit bef1e6
	}
Packit bef1e6
    } else {
Packit bef1e6
	switch (round_opt) {
Packit bef1e6
	default:
Packit bef1e6
	    for_each_mark(i) {
Packit bef1e6
		used += plot_num(p->count[i], marks[i], colors[i], &temp);
Packit bef1e6
	    }
Packit bef1e6
	    break;
Packit bef1e6
	case 1:
Packit bef1e6
	    used = plot_round1(p->count);
Packit bef1e6
	    break;
Packit bef1e6
Packit bef1e6
	case 2:
Packit bef1e6
	    used = plot_round2(p->count);
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
	if ((format_opt & FMT_FILLED) != 0) {
Packit bef1e6
	    if (used > plot_width)
Packit bef1e6
		printf("%ld", used - plot_width);	/* oops */
Packit bef1e6
	    else
Packit bef1e6
		plot_bar(plot_width - used, '.', 0);
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
#define changed(p) (!merge_names \
Packit bef1e6
		    || (p)->cmt != Normal \
Packit bef1e6
		    || (TotalOf(p)) != 0)
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
show_data(const DATA * p)
Packit bef1e6
{
Packit bef1e6
    char *name = data_filename(p);
Packit bef1e6
    int width;
Packit bef1e6
Packit bef1e6
    if (summary_only) {
Packit bef1e6
	;
Packit bef1e6
    } else if (!changed(p)) {
Packit bef1e6
	;
Packit bef1e6
    } else if (p->cmt == Binary && suppress_binary == 1) {
Packit bef1e6
	;
Packit bef1e6
    } else if (table_opt == 1) {
Packit bef1e6
	if (names_only) {
Packit bef1e6
	    printf("%s\n", name);
Packit bef1e6
	} else {
Packit bef1e6
	    printf("%ld,%ld,%ld,",
Packit bef1e6
		   InsOf(p),
Packit bef1e6
		   DelOf(p),
Packit bef1e6
		   ModOf(p));
Packit bef1e6
	    if (path_opt)
Packit bef1e6
		printf("%ld,", EqlOf(p));
Packit bef1e6
	    if (count_files && !reverse_opt)
Packit bef1e6
		printf("%d,%d,%d,",
Packit bef1e6
		       (p->cmt == OnlyRight),
Packit bef1e6
		       (p->cmt == OnlyLeft),
Packit bef1e6
		       (p->cmt == Binary));
Packit bef1e6
	    printf("%s\n", name);
Packit bef1e6
	}
Packit bef1e6
    } else if (names_only) {
Packit bef1e6
	printf("%s\n", name);
Packit bef1e6
    } else {
Packit bef1e6
	printf("%s ", comment_opt);
Packit bef1e6
	if (max_name_wide > 0
Packit bef1e6
	    && max_name_wide < min_name_wide
Packit bef1e6
	    && max_name_wide < ((width = (int) strlen(name)))) {
Packit bef1e6
	    printf("%.*s", max_name_wide, name + (width - max_name_wide));
Packit bef1e6
	} else {
Packit bef1e6
	    width = ((max_name_wide > 0 && max_name_wide < min_name_wide)
Packit bef1e6
		     ? max_name_wide
Packit bef1e6
		     : min_name_wide);
Packit bef1e6
	    printf("%-*.*s", width, width, name);
Packit bef1e6
	}
Packit bef1e6
	if (table_opt == 2) {
Packit bef1e6
	    putchar('|');
Packit bef1e6
	    if (path_opt)
Packit bef1e6
		printf("%*ld ", number_len, EqlOf(p));
Packit bef1e6
	    printf("%*ld ", number_len, InsOf(p));
Packit bef1e6
	    printf("%*ld ", number_len, DelOf(p));
Packit bef1e6
	    printf("%*ld", number_len, ModOf(p));
Packit bef1e6
	}
Packit bef1e6
	putchar('|');
Packit bef1e6
	switch (p->cmt) {
Packit bef1e6
	default:
Packit bef1e6
	case Normal:
Packit bef1e6
	    plot_numbers(p);
Packit bef1e6
	    break;
Packit bef1e6
	case Binary:
Packit bef1e6
	    printf("binary");
Packit bef1e6
	    break;
Packit bef1e6
	case Differs:
Packit bef1e6
	    printf("differ");
Packit bef1e6
	    break;
Packit bef1e6
	case Only:
Packit bef1e6
	    printf("only");
Packit bef1e6
	    break;
Packit bef1e6
	case OnlyLeft:
Packit bef1e6
	    printf(count_files ? "deleted" : "only");
Packit bef1e6
	    break;
Packit bef1e6
	case OnlyRight:
Packit bef1e6
	    printf(count_files ? "added" : "only");
Packit bef1e6
	    break;
Packit bef1e6
	}
Packit bef1e6
	printf("\n");
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
static void
Packit bef1e6
show_tsearch(const void *nodep, const VISIT which, const int depth)
Packit bef1e6
{
Packit bef1e6
    const DATA *p = *(DATA * const *) nodep;
Packit bef1e6
    (void) depth;
Packit bef1e6
    if (which == postorder || which == leaf)
Packit bef1e6
	show_data(p);
Packit bef1e6
}
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
ignore_data(DATA * p)
Packit bef1e6
{
Packit bef1e6
    return ((!changed(p))
Packit bef1e6
	    || (p->cmt == Binary && suppress_binary));
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * Return the length of any directory-prefix from the given path.
Packit bef1e6
 */
Packit bef1e6
static size_t
Packit bef1e6
path_length(const char *path)
Packit bef1e6
{
Packit bef1e6
    size_t result = 0;
Packit bef1e6
    char *mark = strrchr(path, PATHSEP);
Packit bef1e6
    if (mark != 0 && mark != path)
Packit bef1e6
	result = (size_t) (mark + 1 - path);
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/*
Packit bef1e6
 * If we have an "only" filename, we can guess whether it was added or removed
Packit bef1e6
 * by looking at its directory and comparing that to other files' directories.
Packit bef1e6
 *
Packit bef1e6
 * TODO: -K -R combination is not yet supported because that relies on storing
Packit bef1e6
 * both left-/right-paths for each file; only the right-path is currently used.
Packit bef1e6
 */
Packit bef1e6
static Comment
Packit bef1e6
resolve_only(DATA * p)
Packit bef1e6
{
Packit bef1e6
    Comment result = p->cmt;
Packit bef1e6
    if (result == Only && !reverse_opt) {
Packit bef1e6
	DATA *q;
Packit bef1e6
	size_t len1 = path_length(p->name);
Packit bef1e6
	if (len1 != 0) {
Packit bef1e6
	    for (q = all_data; q; q = q->link) {
Packit bef1e6
		result = OnlyLeft;
Packit bef1e6
		if (q->cmt == Normal || q->cmt == Binary) {
Packit bef1e6
		    size_t len2 = path_length(q->name);
Packit bef1e6
		    if (len2 >= len1) {
Packit bef1e6
			if (!strncmp(p->name, q->name, len1)) {
Packit bef1e6
			    result = OnlyRight;
Packit bef1e6
			    break;
Packit bef1e6
			}
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
#ifdef HAVE_OPENDIR
Packit bef1e6
static void
Packit bef1e6
count_unmodified_files(const char *pathname, long *files, long *lines)
Packit bef1e6
{
Packit bef1e6
    DATA *p;
Packit bef1e6
    char *name;
Packit bef1e6
Packit bef1e6
    TRACE(("count_unmodified_files %s\n", pathname));
Packit bef1e6
    if (is_dir(pathname)) {
Packit bef1e6
	DIR *dp = opendir(pathname);
Packit bef1e6
	struct dirent *de;
Packit bef1e6
Packit bef1e6
	if (dp != 0) {
Packit bef1e6
	    while ((de = readdir(dp)) != 0) {
Packit bef1e6
		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
Packit bef1e6
		    continue;
Packit bef1e6
		name = malloc(strlen(pathname) + 2 + strlen(de->d_name));
Packit bef1e6
		if (name != 0) {
Packit bef1e6
		    sprintf(name, "%s%c%s", pathname, PATHSEP, de->d_name);
Packit bef1e6
		    count_unmodified_files(name, files, lines);
Packit bef1e6
		    free(name);
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    closedir(dp);
Packit bef1e6
	}
Packit bef1e6
    } else if (is_file(pathname)) {
Packit bef1e6
	/*
Packit bef1e6
	 * Given the pathname from the (-S) source directory, derive a
Packit bef1e6
	 * corresponding path for the destination directory.  Then check if
Packit bef1e6
	 * that path appears in the list of modified files.
Packit bef1e6
	 */
Packit bef1e6
	int found = 0;
Packit bef1e6
	const char *ref_name = (all_data ? all_data->name : pathname);
Packit bef1e6
Packit bef1e6
	if (prefix_opt >= 0) {
Packit bef1e6
	    int level_s = count_prefix(path_opt);
Packit bef1e6
	    int base_s = 0;
Packit bef1e6
	    int base_d = 0;
Packit bef1e6
	    (void) skip_prefix(pathname, level_s + 1, &base_s);
Packit bef1e6
	    (void) skip_prefix(ref_name, level_s + 1, &base_d);
Packit bef1e6
	    name = malloc(2 + strlen(pathname) + strlen(ref_name));
Packit bef1e6
	    sprintf(name, "%.*s%s", base_d, ref_name, base_s + pathname);
Packit bef1e6
	} else {
Packit bef1e6
	    const char *mark = unchanged ? ref_name : data_filename(all_data);
Packit bef1e6
	    int skip = 1 + (int) strlen(path_opt);
Packit bef1e6
	    name = malloc(strlen(ref_name) + 2 + strlen(pathname));
Packit bef1e6
	    sprintf(name, "%.*s%s",
Packit bef1e6
		    (int) (mark - ref_name),
Packit bef1e6
		    ref_name,
Packit bef1e6
		    pathname + skip);
Packit bef1e6
	}
Packit bef1e6
	if (is_file(name)) {
Packit bef1e6
	    for (p = all_data; p != 0 && !found; p = p->link) {
Packit bef1e6
		if (!strcmp(name, p->name)) {
Packit bef1e6
		    found = 1;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    if (!found) {
Packit bef1e6
		int len;
Packit bef1e6
		p = find_data(name);
Packit bef1e6
		*files += 1;
Packit bef1e6
		EqlOf(p) = count_lines(p);
Packit bef1e6
		*lines += EqlOf(p);
Packit bef1e6
Packit bef1e6
		if (unchanged) {
Packit bef1e6
		    len = (int) strlen(p->name);
Packit bef1e6
		    if (min_name_wide < (len - p->base))
Packit bef1e6
			min_name_wide = (len - p->base);
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
	free(name);
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
update_min_name_wide(long longest_name)
Packit bef1e6
{
Packit bef1e6
    if (prefix_opt < 0) {
Packit bef1e6
	if (prefix_len < 0)
Packit bef1e6
	    prefix_len = 0;
Packit bef1e6
	if ((longest_name - prefix_len) > min_name_wide)
Packit bef1e6
	    min_name_wide = (longest_name - prefix_len);
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    if (min_name_wide < 1)
Packit bef1e6
	min_name_wide = 0;
Packit bef1e6
    min_name_wide++;		/* make sure it's nonzero */
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
summarize(void)
Packit bef1e6
{
Packit bef1e6
    DATA *p;
Packit bef1e6
    long total_ins = 0;
Packit bef1e6
    long total_del = 0;
Packit bef1e6
    long total_mod = 0;
Packit bef1e6
    long total_eql = 0;
Packit bef1e6
    long files_added = 0;
Packit bef1e6
    long files_equal = 0;
Packit bef1e6
    long files_binary = 0;
Packit bef1e6
    long files_removed = 0;
Packit bef1e6
    long temp;
Packit bef1e6
    int num_files = 0, shortest_name = -1, longest_name = -1;
Packit bef1e6
Packit bef1e6
    plot_scale = 0;
Packit bef1e6
    for (p = all_data; p; p = p->link) {
Packit bef1e6
	int len = (int) strlen(p->name);
Packit bef1e6
Packit bef1e6
	if (ignore_data(p))
Packit bef1e6
	    continue;
Packit bef1e6
Packit bef1e6
	/*
Packit bef1e6
	 * If "-pX" option is given, prefix_opt is positive.
Packit bef1e6
	 *
Packit bef1e6
	 * "-p0" gives the whole pathname unmodified.  "-p1" strips
Packit bef1e6
	 * through the first path-separator, etc.
Packit bef1e6
	 */
Packit bef1e6
	if (prefix_opt >= 0) {
Packit bef1e6
	    /* p->base has been computed at node creation */
Packit bef1e6
	    if (min_name_wide < (len - p->base))
Packit bef1e6
		min_name_wide = (len - p->base);
Packit bef1e6
	} else {
Packit bef1e6
	    /*
Packit bef1e6
	     * If "-pX" option is not given, strip off any prefix which is
Packit bef1e6
	     * shared by all of the names.
Packit bef1e6
	     */
Packit bef1e6
	    if (len < prefix_len || prefix_len < 0)
Packit bef1e6
		prefix_len = len;
Packit bef1e6
	    while (prefix_len > 0) {
Packit bef1e6
		if (p->name[prefix_len - 1] != PATHSEP)
Packit bef1e6
		    prefix_len--;
Packit bef1e6
		else if (strncmp(all_data->name, p->name, (size_t) prefix_len))
Packit bef1e6
		    prefix_len--;
Packit bef1e6
		else
Packit bef1e6
		    break;
Packit bef1e6
	    }
Packit bef1e6
Packit bef1e6
	    if (len > longest_name)
Packit bef1e6
		longest_name = len;
Packit bef1e6
	    if (len < shortest_name || shortest_name < 0)
Packit bef1e6
		shortest_name = len;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    /*
Packit bef1e6
     * Get additional counts for files where we cannot count lines changed.
Packit bef1e6
     */
Packit bef1e6
    if (count_files) {
Packit bef1e6
	for (p = all_data; p; p = p->link) {
Packit bef1e6
	    switch (p->cmt) {
Packit bef1e6
	    case Binary:
Packit bef1e6
		files_binary++;
Packit bef1e6
		break;
Packit bef1e6
	    case Only:
Packit bef1e6
		switch (resolve_only(p)) {
Packit bef1e6
		case OnlyRight:
Packit bef1e6
		    p->cmt = OnlyRight;
Packit bef1e6
		    files_added++;
Packit bef1e6
		    break;
Packit bef1e6
		case OnlyLeft:
Packit bef1e6
		    p->cmt = OnlyLeft;
Packit bef1e6
		    files_removed++;
Packit bef1e6
		    break;
Packit bef1e6
		default:
Packit bef1e6
		    /* ignore - we could not guess */
Packit bef1e6
		    break;
Packit bef1e6
		}
Packit bef1e6
	    default:
Packit bef1e6
		break;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    /*
Packit bef1e6
     * Use a separate loop after computing prefix_len so we can apply the "-S"
Packit bef1e6
     * or "-D" options to find files that we can use as reference for the
Packit bef1e6
     * unchanged-count.
Packit bef1e6
     */
Packit bef1e6
    for (p = all_data; p; p = p->link) {
Packit bef1e6
	if (!ignore_data(p)) {
Packit bef1e6
	    EqlOf(p) = 0;
Packit bef1e6
	    if (reverse_opt) {
Packit bef1e6
		long save_ins = InsOf(p);
Packit bef1e6
		long save_del = DelOf(p);
Packit bef1e6
		InsOf(p) = save_del;
Packit bef1e6
		DelOf(p) = save_ins;
Packit bef1e6
	    }
Packit bef1e6
	    if (path_opt != 0) {
Packit bef1e6
		int count = count_lines(p);
Packit bef1e6
Packit bef1e6
		if (count >= 0) {
Packit bef1e6
		    EqlOf(p) = count - ModOf(p);
Packit bef1e6
		    if (path_dest != 0) {
Packit bef1e6
			EqlOf(p) -= InsOf(p);
Packit bef1e6
		    } else {
Packit bef1e6
			EqlOf(p) -= DelOf(p);
Packit bef1e6
		    }
Packit bef1e6
		    if (EqlOf(p) < 0)
Packit bef1e6
			EqlOf(p) = 0;
Packit bef1e6
		}
Packit bef1e6
	    }
Packit bef1e6
	    num_files++;
Packit bef1e6
	    total_ins += InsOf(p);
Packit bef1e6
	    total_del += DelOf(p);
Packit bef1e6
	    total_mod += ModOf(p);
Packit bef1e6
	    total_eql += EqlOf(p);
Packit bef1e6
	    temp = TotalOf(p);
Packit bef1e6
	    if (temp > plot_scale)
Packit bef1e6
		plot_scale = temp;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    update_min_name_wide(longest_name);
Packit bef1e6
Packit bef1e6
#ifdef HAVE_OPENDIR
Packit bef1e6
    if (path_opt != 0) {
Packit bef1e6
	unchanged = (all_data == 0);
Packit bef1e6
	count_unmodified_files(path_opt, &files_equal, &total_eql);
Packit bef1e6
	if (unchanged) {
Packit bef1e6
	    for (p = all_data; p; p = p->link) {
Packit bef1e6
		int len = (int) strlen(p->name);
Packit bef1e6
		if (longest_name < len)
Packit bef1e6
		    longest_name = len;
Packit bef1e6
		temp = TotalOf(p);
Packit bef1e6
		if (temp > plot_scale)
Packit bef1e6
		    plot_scale = temp;
Packit bef1e6
	    }
Packit bef1e6
	    update_min_name_wide(longest_name);
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
    plot_width = (max_width - min_name_wide - 8);
Packit bef1e6
    if (plot_width < 10)
Packit bef1e6
	plot_width = 10;
Packit bef1e6
Packit bef1e6
    if (plot_scale < plot_width)
Packit bef1e6
	plot_scale = plot_width;	/* 1:1 */
Packit bef1e6
Packit bef1e6
    if (table_opt == 1) {
Packit bef1e6
	if (!names_only) {
Packit bef1e6
	    printf("INSERTED,DELETED,MODIFIED,");
Packit bef1e6
	    if (path_opt)
Packit bef1e6
		printf("UNCHANGED,");
Packit bef1e6
	    if (count_files && !reverse_opt)
Packit bef1e6
		printf("FILE-ADDED,FILE-DELETED,FILE-BINARY,");
Packit bef1e6
	}
Packit bef1e6
	printf("FILENAME\n");
Packit bef1e6
    } else if (table_opt == 2) {
Packit bef1e6
	long largest = 0;
Packit bef1e6
	for (p = all_data; p; p = p->link) {
Packit bef1e6
	    if (path_opt)
Packit bef1e6
		largest = maximum(largest, EqlOf(p));
Packit bef1e6
	    largest = maximum(largest, InsOf(p));
Packit bef1e6
	    largest = maximum(largest, DelOf(p));
Packit bef1e6
	    largest = maximum(largest, ModOf(p));
Packit bef1e6
	}
Packit bef1e6
	number_len = 0;
Packit bef1e6
	while (largest > 0) {
Packit bef1e6
	    number_len++;
Packit bef1e6
	    largest /= 10;
Packit bef1e6
	}
Packit bef1e6
	number_len = maximum(number_len, 3);
Packit bef1e6
    }
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
    if (use_tsearch) {
Packit bef1e6
	twalk(sorted_data, show_tsearch);
Packit bef1e6
    } else
Packit bef1e6
#endif
Packit bef1e6
	for (p = all_data; p; p = p->link) {
Packit bef1e6
	    show_data(p);
Packit bef1e6
	}
Packit bef1e6
Packit bef1e6
    if ((table_opt != 1) && !names_only) {
Packit bef1e6
#define PLURAL(n) n, n != 1 ? "s" : ""
Packit bef1e6
	if (num_files > 0 || !quiet) {
Packit bef1e6
	    printf("%s %d file%s changed", comment_opt, PLURAL(num_files));
Packit bef1e6
	    if (total_ins)
Packit bef1e6
		printf(", %ld insertion%s(+)", PLURAL(total_ins));
Packit bef1e6
	    if (total_del)
Packit bef1e6
		printf(", %ld deletion%s(-)", PLURAL(total_del));
Packit bef1e6
	    if (total_mod)
Packit bef1e6
		printf(", %ld modification%s(!)", PLURAL(total_mod));
Packit bef1e6
	    if (total_eql && path_opt != 0)
Packit bef1e6
		printf(", %ld unchanged line%s(=)", PLURAL(total_eql));
Packit bef1e6
	    if (count_files) {
Packit bef1e6
		if (files_added)
Packit bef1e6
		    printf(", %ld file%s added", PLURAL(files_added));
Packit bef1e6
		if (files_removed)
Packit bef1e6
		    printf(", %ld file%s removed", PLURAL(files_removed));
Packit bef1e6
		if (files_binary)
Packit bef1e6
		    printf(", %ld binary file%s", PLURAL(files_binary));
Packit bef1e6
	    }
Packit bef1e6
	    (void) putchar('\n');
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
#ifdef HAVE_POPEN
Packit bef1e6
static const char *
Packit bef1e6
get_program(const char *name, const char *dft)
Packit bef1e6
{
Packit bef1e6
    const char *result = getenv(name);
Packit bef1e6
    if (result == 0 || *result == EOS)
Packit bef1e6
	result = dft;
Packit bef1e6
    TRACE(("get_program(%s) = %s\n", name, result));
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
#define GET_PROGRAM(name) get_program("DIFFSTAT_" #name, name)
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
decompressor(Decompress which, const char *name)
Packit bef1e6
{
Packit bef1e6
    const char *verb = 0;
Packit bef1e6
    const char *opts = "";
Packit bef1e6
    char *result = 0;
Packit bef1e6
    size_t len = strlen(name);
Packit bef1e6
Packit bef1e6
    switch (which) {
Packit bef1e6
    case dcBzip:
Packit bef1e6
	verb = GET_PROGRAM(BZCAT_PATH);
Packit bef1e6
	if (*verb == '\0') {
Packit bef1e6
	    verb = GET_PROGRAM(BZIP2_PATH);
Packit bef1e6
	    opts = "-dc";
Packit bef1e6
	}
Packit bef1e6
	break;
Packit bef1e6
    case dcCompress:
Packit bef1e6
	verb = GET_PROGRAM(ZCAT_PATH);
Packit bef1e6
	if (*verb == '\0') {
Packit bef1e6
	    verb = GET_PROGRAM(UNCOMPRESS_PATH);
Packit bef1e6
	    opts = "-c";
Packit bef1e6
	    if (*verb == '\0') {
Packit bef1e6
		/* not all compress's recognize the options, test this last */
Packit bef1e6
		verb = GET_PROGRAM(COMPRESS_PATH);
Packit bef1e6
		opts = "-dc";
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
	break;
Packit bef1e6
    case dcGzip:
Packit bef1e6
	verb = GET_PROGRAM(GZIP_PATH);
Packit bef1e6
	opts = "-dc";
Packit bef1e6
	break;
Packit bef1e6
    case dcLzma:
Packit bef1e6
	verb = GET_PROGRAM(LZCAT_PATH);
Packit bef1e6
	opts = "-dc";
Packit bef1e6
	break;
Packit bef1e6
    case dcPack:
Packit bef1e6
	verb = GET_PROGRAM(PCAT_PATH);
Packit bef1e6
	break;
Packit bef1e6
    case dcXz:
Packit bef1e6
	verb = GET_PROGRAM(XZ_PATH);
Packit bef1e6
	opts = "-dc";
Packit bef1e6
	break;
Packit bef1e6
    case dcEmpty:
Packit bef1e6
	/* FALLTHRU */
Packit bef1e6
    case dcNone:
Packit bef1e6
	break;
Packit bef1e6
    }
Packit bef1e6
    if (verb != 0 && *verb != '\0') {
Packit bef1e6
	result = (char *) xmalloc(strlen(verb) + 10 + len);
Packit bef1e6
	sprintf(result, "%s %s", verb, opts);
Packit bef1e6
	if (*name != '\0') {
Packit bef1e6
	    sprintf(result + strlen(result), " \"%s\"", name);
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
is_compressed(const char *name)
Packit bef1e6
{
Packit bef1e6
    size_t len = strlen(name);
Packit bef1e6
    Decompress which;
Packit bef1e6
Packit bef1e6
    if (len > 2 && !strcmp(name + len - 2, ".Z")) {
Packit bef1e6
	which = dcCompress;
Packit bef1e6
    } else if (len > 2 && !strcmp(name + len - 2, ".z")) {
Packit bef1e6
	which = dcPack;
Packit bef1e6
    } else if (len > 3 && !strcmp(name + len - 3, ".gz")) {
Packit bef1e6
	which = dcGzip;
Packit bef1e6
    } else if (len > 4 && !strcmp(name + len - 4, ".bz2")) {
Packit bef1e6
	which = dcBzip;
Packit bef1e6
    } else if (len > 5 && !strcmp(name + len - 5, ".lzma")) {
Packit bef1e6
	which = dcLzma;
Packit bef1e6
    } else if (len > 3 && !strcmp(name + len - 3, ".xz")) {
Packit bef1e6
	which = dcXz;
Packit bef1e6
    } else {
Packit bef1e6
	which = dcNone;
Packit bef1e6
    }
Packit bef1e6
    return decompressor(which, name);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
#ifdef HAVE_MKDTEMP
Packit bef1e6
#define MY_MKDTEMP(path) mkdtemp(path)
Packit bef1e6
#else
Packit bef1e6
/*
Packit bef1e6
 * mktemp is supposedly marked obsolete at the same point that mkdtemp is
Packit bef1e6
 * introduced.
Packit bef1e6
 */
Packit bef1e6
static char *
Packit bef1e6
my_mkdtemp(char *path)
Packit bef1e6
{
Packit bef1e6
    char *result = mktemp(path);
Packit bef1e6
    if (result != 0) {
Packit bef1e6
	if (MKDIR(result, 0700) < 0) {
Packit bef1e6
	    result = 0;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return path;
Packit bef1e6
}
Packit bef1e6
#define MY_MKDTEMP(path) my_mkdtemp(path)
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
static char *
Packit bef1e6
copy_stdin(char **dirpath)
Packit bef1e6
{
Packit bef1e6
    const char *tmp = getenv("TMPDIR");
Packit bef1e6
    char *result = 0;
Packit bef1e6
    int ch;
Packit bef1e6
    FILE *fp;
Packit bef1e6
Packit bef1e6
    if (tmp == 0)
Packit bef1e6
	tmp = "/tmp/";
Packit bef1e6
    *dirpath = xmalloc(strlen(tmp) + 12);
Packit bef1e6
Packit bef1e6
    strcpy(*dirpath, tmp);
Packit bef1e6
    strcat(*dirpath, "/diffXXXXXX");
Packit bef1e6
    if (MY_MKDTEMP(*dirpath) != 0) {
Packit bef1e6
	result = xmalloc(strlen(*dirpath) + 10);
Packit bef1e6
	sprintf(result, "%s/stdin", *dirpath);
Packit bef1e6
Packit bef1e6
	if ((fp = fopen(result, "w")) != 0) {
Packit bef1e6
	    while ((ch = MY_GETC(stdin)) != EOF) {
Packit bef1e6
		fputc(ch, fp);
Packit bef1e6
	    }
Packit bef1e6
	    fclose(fp);
Packit bef1e6
	} else {
Packit bef1e6
	    free(result);
Packit bef1e6
	    result = 0;
Packit bef1e6
	    rmdir(*dirpath);	/* Assume that the /stdin file was not created */
Packit bef1e6
	    free(*dirpath);
Packit bef1e6
	    *dirpath = 0;
Packit bef1e6
	}
Packit bef1e6
    } else {
Packit bef1e6
	free(*dirpath);
Packit bef1e6
	*dirpath = 0;
Packit bef1e6
    }
Packit bef1e6
    return result;
Packit bef1e6
}
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
set_path_opt(char *value, int destination)
Packit bef1e6
{
Packit bef1e6
    path_opt = value;
Packit bef1e6
    path_dest = destination;
Packit bef1e6
    if (*path_opt != 0) {
Packit bef1e6
	if (is_dir(path_opt)) {
Packit bef1e6
	    num_marks = 4;
Packit bef1e6
	} else {
Packit bef1e6
	    fprintf(stderr, "Not a directory:%s\n", path_opt);
Packit bef1e6
	    exit(EXIT_FAILURE);
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static void
Packit bef1e6
usage(FILE *fp)
Packit bef1e6
{
Packit bef1e6
    static const char *msg[] =
Packit bef1e6
    {
Packit bef1e6
	"Usage: diffstat [options] [files]",
Packit bef1e6
	"",
Packit bef1e6
	"Reads from one or more input files which contain output from 'diff',",
Packit bef1e6
	"producing a histogram of total lines changed for each file referenced.",
Packit bef1e6
	"If no filename is given on the command line, reads from standard input.",
Packit bef1e6
	"",
Packit bef1e6
	"Options:",
Packit bef1e6
	"  -b      ignore lines matching \"Binary files XXX and YYY differ\"",
Packit bef1e6
	"  -c      prefix each line with comment (#)",
Packit bef1e6
	"  -C      add SGR color escape sequences to highlight the histogram",
Packit bef1e6
#if OPT_TRACE
Packit bef1e6
	"  -d      debug - prints a lot of information",
Packit bef1e6
#endif
Packit bef1e6
	"  -D PATH specify location of patched files, use for unchanged-count",
Packit bef1e6
	"  -e FILE redirect standard error to FILE",
Packit bef1e6
	"  -E      trim escape-sequences, e.g., from colordiff",
Packit bef1e6
	"  -f NUM  format (0=concise, 1=normal, 2=filled, 4=values)",
Packit bef1e6
	"  -h      print this message",
Packit bef1e6
	"  -k      do not merge filenames",
Packit bef1e6
	"  -K      resolve ambiguity of \"only\" filenames",
Packit bef1e6
	"  -l      list filenames only",
Packit bef1e6
	"  -m      merge insert/delete data in chunks as modified-lines",
Packit bef1e6
	"  -n NUM  specify minimum width for the filenames (default: auto)",
Packit bef1e6
	"  -N NUM  specify maximum width for the filenames (default: auto)",
Packit bef1e6
	"  -o FILE redirect standard output to FILE",
Packit bef1e6
	"  -p NUM  specify number of pathname-separators to strip (default: common)",
Packit bef1e6
	"  -q      suppress the \"0 files changed\" message for empty diffs",
Packit bef1e6
	"  -r NUM  specify rounding for histogram (0=none, 1=simple, 2=adjusted)",
Packit bef1e6
	"  -R      assume patch was created with old and new files swapped",
Packit bef1e6
	"  -s      show only the summary line",
Packit bef1e6
	"  -S PATH specify location of original files, use for unchanged-count",
Packit bef1e6
	"  -t      print a table (comma-separated-values) rather than histogram",
Packit bef1e6
	"  -T      print amounts (like -t option) in addition to histogram",
Packit bef1e6
	"  -u      do not sort the input list",
Packit bef1e6
	"  -v      show progress if output is redirected to a file",
Packit bef1e6
	"  -V      prints the version number",
Packit bef1e6
	"  -w NUM  specify maximum width of the output (default: 80)",
Packit bef1e6
    };
Packit bef1e6
    unsigned j;
Packit bef1e6
    for (j = 0; j < sizeof(msg) / sizeof(msg[0]); j++)
Packit bef1e6
	fprintf(fp, "%s\n", msg[j]);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
/* Wrapper around getopt that also parses "--help" and "--version".  
Packit bef1e6
 * argc, argv, opts, return value, and globals optarg, optind,
Packit bef1e6
 * opterr, and optopt are as in getopt().  help and version designate
Packit bef1e6
 * what should be returned if --help or --version are encountered. */
Packit bef1e6
static int
Packit bef1e6
getopt_helper(int argc, char *const argv[], const char *opts,
Packit bef1e6
	      int help, int version)
Packit bef1e6
{
Packit bef1e6
    if (optind < argc && argv[optind] != NULL) {
Packit bef1e6
	if (strcmp(argv[optind], "--help") == 0) {
Packit bef1e6
	    optind++;
Packit bef1e6
	    return help;
Packit bef1e6
	} else if (strcmp(argv[optind], "--version") == 0) {
Packit bef1e6
	    optind++;
Packit bef1e6
	    return version;
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
    return getopt(argc, argv, opts);
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
static int
Packit bef1e6
getopt_value(void)
Packit bef1e6
{
Packit bef1e6
    char *next = 0;
Packit bef1e6
    long value = strtol(optarg, &next, 0);
Packit bef1e6
    if (next == 0 || *next != '\0') {
Packit bef1e6
	fprintf(stderr, "expected a number, have '%s'\n", optarg);
Packit bef1e6
	exit(EXIT_FAILURE);
Packit bef1e6
    }
Packit bef1e6
    return (int) value;
Packit bef1e6
}
Packit bef1e6
Packit bef1e6
int
Packit bef1e6
main(int argc, char *argv[])
Packit bef1e6
{
Packit bef1e6
    int j;
Packit bef1e6
    char version[80];
Packit bef1e6
Packit bef1e6
    max_width = 80;
Packit bef1e6
Packit bef1e6
    while ((j = getopt_helper(argc, argv,
Packit bef1e6
			      "bcCdD:e:Ef:hkKlmn:N:o:p:qr:RsS:tTuvVw:", 'h', 'V'))
Packit bef1e6
	   != -1) {
Packit bef1e6
	switch (j) {
Packit bef1e6
	case 'b':
Packit bef1e6
	    suppress_binary = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'c':
Packit bef1e6
	    comment_opt = "#";
Packit bef1e6
	    break;
Packit bef1e6
	case 'C':
Packit bef1e6
	    show_colors = 1;
Packit bef1e6
	    break;
Packit bef1e6
#if OPT_TRACE
Packit bef1e6
	case 'd':
Packit bef1e6
	    trace_opt = 1;
Packit bef1e6
	    break;
Packit bef1e6
#endif
Packit bef1e6
	case 'D':
Packit bef1e6
	    set_path_opt(optarg, 1);
Packit bef1e6
	    break;
Packit bef1e6
	case 'e':
Packit bef1e6
	    if (freopen(optarg, "w", stderr) == 0)
Packit bef1e6
		failed(optarg);
Packit bef1e6
	    break;
Packit bef1e6
	case 'E':
Packit bef1e6
	    trim_escapes = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'f':
Packit bef1e6
	    format_opt = getopt_value();
Packit bef1e6
	    break;
Packit bef1e6
	case 'h':
Packit bef1e6
	    usage(stdout);
Packit bef1e6
	    return (EXIT_SUCCESS);
Packit bef1e6
	case 'k':
Packit bef1e6
	    merge_names = 0;
Packit bef1e6
	    break;
Packit bef1e6
	case 'K':
Packit bef1e6
	    count_files = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'l':
Packit bef1e6
	    names_only = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'm':
Packit bef1e6
	    merge_opt = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'n':
Packit bef1e6
	    min_name_wide = getopt_value();
Packit bef1e6
	    break;
Packit bef1e6
	case 'N':
Packit bef1e6
	    max_name_wide = getopt_value();
Packit bef1e6
	    break;
Packit bef1e6
	case 'o':
Packit bef1e6
	    if (freopen(optarg, "w", stdout) == 0)
Packit bef1e6
		failed(optarg);
Packit bef1e6
	    break;
Packit bef1e6
	case 'p':
Packit bef1e6
	    prefix_opt = getopt_value();
Packit bef1e6
	    break;
Packit bef1e6
	case 'r':
Packit bef1e6
	    round_opt = getopt_value();
Packit bef1e6
	    break;
Packit bef1e6
	case 'R':
Packit bef1e6
	    reverse_opt = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 's':
Packit bef1e6
	    summary_only = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'S':
Packit bef1e6
	    set_path_opt(optarg, 0);
Packit bef1e6
	    break;
Packit bef1e6
	case 't':
Packit bef1e6
	    table_opt = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'T':
Packit bef1e6
	    table_opt = 2;
Packit bef1e6
	    break;
Packit bef1e6
	case 'u':
Packit bef1e6
	    sort_names = 0;
Packit bef1e6
	    break;
Packit bef1e6
	case 'v':
Packit bef1e6
	    verbose = 1;
Packit bef1e6
	    break;
Packit bef1e6
	case 'V':
Packit bef1e6
#ifndef	NO_IDENT
Packit bef1e6
	    if (!sscanf(Id, "%*s %*s %s", version))
Packit bef1e6
#endif
Packit bef1e6
		(void) strcpy(version, "?");
Packit bef1e6
	    printf("diffstat version %s\n", version);
Packit bef1e6
	    return (EXIT_SUCCESS);
Packit bef1e6
	case 'w':
Packit bef1e6
	    max_width = getopt_value();
Packit bef1e6
	    break;
Packit bef1e6
	case 'q':
Packit bef1e6
	    quiet = 1;
Packit bef1e6
	    break;
Packit bef1e6
	default:
Packit bef1e6
	    usage(stderr);
Packit bef1e6
	    return (EXIT_FAILURE);
Packit bef1e6
	}
Packit bef1e6
    }
Packit bef1e6
Packit bef1e6
    /*
Packit bef1e6
     * The numbers from -S/-D options will only be useful if the merge option
Packit bef1e6
     * is added.
Packit bef1e6
     */
Packit bef1e6
    if (path_opt)
Packit bef1e6
	merge_opt = 1;
Packit bef1e6
Packit bef1e6
    show_progress = verbose && (!isatty(fileno(stdout))
Packit bef1e6
				&& isatty(fileno(stderr)));
Packit bef1e6
Packit bef1e6
#ifdef HAVE_TSEARCH
Packit bef1e6
    use_tsearch = (sort_names && merge_names);
Packit bef1e6
#endif
Packit bef1e6
Packit bef1e6
    if (optind < argc) {
Packit bef1e6
	while (optind < argc) {
Packit bef1e6
	    FILE *fp;
Packit bef1e6
	    char *name = argv[optind++];
Packit bef1e6
#ifdef HAVE_POPEN
Packit bef1e6
	    char *command = is_compressed(name);
Packit bef1e6
	    if (command != 0) {
Packit bef1e6
		if ((fp = popen(command, "r")) != 0) {
Packit bef1e6
		    if (show_progress) {
Packit bef1e6
			(void) fprintf(stderr, "%s\n", name);
Packit bef1e6
			(void) fflush(stderr);
Packit bef1e6
		    }
Packit bef1e6
		    do_file(fp, name);
Packit bef1e6
		    (void) pclose(fp);
Packit bef1e6
		}
Packit bef1e6
		free(command);
Packit bef1e6
	    } else
Packit bef1e6
#endif
Packit bef1e6
	    if ((fp = fopen(name, "rb")) != 0) {
Packit bef1e6
		if (show_progress) {
Packit bef1e6
		    (void) fprintf(stderr, "%s\n", name);
Packit bef1e6
		    (void) fflush(stderr);
Packit bef1e6
		}
Packit bef1e6
		do_file(fp, name);
Packit bef1e6
		(void) fclose(fp);
Packit bef1e6
	    } else {
Packit bef1e6
		failed(name);
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
    } else {
Packit bef1e6
#ifdef HAVE_POPEN
Packit bef1e6
	FILE *fp;
Packit bef1e6
	Decompress which = dcEmpty;
Packit bef1e6
	char *stdin_dir = 0;
Packit bef1e6
	char *myfile;
Packit bef1e6
	char sniff[8];
Packit bef1e6
	int ch;
Packit bef1e6
	unsigned got = 0;
Packit bef1e6
	char *command;
Packit bef1e6
Packit bef1e6
	if ((ch = MY_GETC(stdin)) != EOF) {
Packit bef1e6
	    which = dcNone;
Packit bef1e6
	    if (ch == 'B') {	/* perhaps bzip2 (poor magic design...) */
Packit bef1e6
		sniff[got++] = (char) ch;
Packit bef1e6
		while (got < 5) {
Packit bef1e6
		    if ((ch = MY_GETC(stdin)) == EOF)
Packit bef1e6
			break;
Packit bef1e6
		    sniff[got++] = (char) ch;
Packit bef1e6
		}
Packit bef1e6
		if (got == 5
Packit bef1e6
		    && !strncmp(sniff, "BZh", (size_t) 3)
Packit bef1e6
		    && isdigit((unsigned char) sniff[3])
Packit bef1e6
		    && isdigit((unsigned char) sniff[4])) {
Packit bef1e6
		    which = dcBzip;
Packit bef1e6
		}
Packit bef1e6
	    } else if (ch == ']') {	/* perhaps lzma */
Packit bef1e6
		sniff[got++] = (char) ch;
Packit bef1e6
		while (got < 4) {
Packit bef1e6
		    if ((ch = MY_GETC(stdin)) == EOF)
Packit bef1e6
			break;
Packit bef1e6
		    sniff[got++] = (char) ch;
Packit bef1e6
		}
Packit bef1e6
		if (got == 4
Packit bef1e6
		    && !memcmp(sniff, "]\0\0\200", (size_t) 4)) {
Packit bef1e6
		    which = dcLzma;
Packit bef1e6
		}
Packit bef1e6
	    } else if (ch == 0xfd) {	/* perhaps xz */
Packit bef1e6
		sniff[got++] = (char) ch;
Packit bef1e6
		while (got < 6) {
Packit bef1e6
		    if ((ch = MY_GETC(stdin)) == EOF)
Packit bef1e6
			break;
Packit bef1e6
		    sniff[got++] = (char) ch;
Packit bef1e6
		}
Packit bef1e6
		if (got == 6
Packit bef1e6
		    && !memcmp(sniff, "\3757zXZ\0", (size_t) 6)) {
Packit bef1e6
		    which = dcXz;
Packit bef1e6
		}
Packit bef1e6
	    } else if (ch == '\037') {	/* perhaps compress, etc. */
Packit bef1e6
		sniff[got++] = (char) ch;
Packit bef1e6
		if ((ch = MY_GETC(stdin)) != EOF) {
Packit bef1e6
		    sniff[got++] = (char) ch;
Packit bef1e6
		    switch (ch) {
Packit bef1e6
		    case 0213:
Packit bef1e6
			which = dcGzip;
Packit bef1e6
			break;
Packit bef1e6
		    case 0235:
Packit bef1e6
			which = dcCompress;
Packit bef1e6
			break;
Packit bef1e6
		    case 0036:
Packit bef1e6
			which = dcPack;
Packit bef1e6
			break;
Packit bef1e6
		    }
Packit bef1e6
		}
Packit bef1e6
	    } else {
Packit bef1e6
		sniff[got++] = (char) ch;
Packit bef1e6
	    }
Packit bef1e6
	}
Packit bef1e6
	/*
Packit bef1e6
	 * The C standard only guarantees one ungetc;
Packit bef1e6
	 * virtually everyone allows more.
Packit bef1e6
	 */
Packit bef1e6
	while (got != 0) {
Packit bef1e6
	    ungetc(sniff[--got], stdin);
Packit bef1e6
	}
Packit bef1e6
	if (which != dcNone
Packit bef1e6
	    && which != dcEmpty
Packit bef1e6
	    && (myfile = copy_stdin(&stdin_dir)) != 0) {
Packit bef1e6
Packit bef1e6
	    /* open pipe to decompress temporary file */
Packit bef1e6
	    command = decompressor(which, myfile);
Packit bef1e6
	    if ((fp = popen(command, "r")) != 0) {
Packit bef1e6
		do_file(fp, "stdin");
Packit bef1e6
		(void) pclose(fp);
Packit bef1e6
	    }
Packit bef1e6
	    free(command);
Packit bef1e6
Packit bef1e6
	    unlink(myfile);
Packit bef1e6
	    free(myfile);
Packit bef1e6
	    myfile = 0;
Packit bef1e6
	    rmdir(stdin_dir);
Packit bef1e6
	    free(stdin_dir);
Packit bef1e6
	    stdin_dir = 0;
Packit bef1e6
	} else if (which != dcEmpty)
Packit bef1e6
#endif
Packit bef1e6
	    do_file(stdin, "stdin");
Packit bef1e6
    }
Packit bef1e6
    summarize();
Packit bef1e6
#if defined(NO_LEAKS)
Packit bef1e6
    while (all_data != 0) {
Packit bef1e6
	delink(all_data);
Packit bef1e6
    }
Packit bef1e6
#endif
Packit bef1e6
    return (EXIT_SUCCESS);
Packit bef1e6
}