|
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 |
}
|