diff --git a/lib/quotearg.c b/lib/quotearg.c
index a4fcc2e..f657033 100644
--- a/lib/quotearg.c
+++ b/lib/quotearg.c
@@ -886,9 +886,8 @@ quotearg_n_options (int n, char const *arg, size_t argsize,
if (nslots <= n)
{
bool preallocated = (sv == &slotvec0);
- int nmax = MIN (INT_MAX, MIN (PTRDIFF_MAX, SIZE_MAX) / sizeof *sv) - 1;
-
- if (nmax < n)
+
+ if (MIN (INT_MAX, MIN (PTRDIFF_MAX, SIZE_MAX) / sizeof *sv) <= n)
xalloc_die ();
slotvec = sv = xrealloc (preallocated ? NULL : sv, (n + 1) * sizeof *sv);
diff --git a/lib/quotearg.c.covscan b/lib/quotearg.c.covscan
deleted file mode 100644
index f657033..0000000
--- a/lib/quotearg.c.covscan
+++ /dev/null
@@ -1,1080 +0,0 @@
-/* quotearg.c - quote arguments for output
-
- Copyright (C) 1998-2002, 2004-2017 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-/* Written by Paul Eggert */
-
-/* Without this pragma, gcc 4.7.0 20111124 mistakenly suggests that
- the quoting_options_from_style function might be candidate for
- attribute 'pure' */
-#if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__
-# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
-#endif
-
-#include
-
-#include "quotearg.h"
-#include "quote.h"
-
-#include "minmax.h"
-#include "xalloc.h"
-#include "c-strcaseeq.h"
-#include "localcharset.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-#define N_(msgid) msgid
-
-#ifndef SIZE_MAX
-# define SIZE_MAX ((size_t) -1)
-#endif
-
-#define INT_BITS (sizeof (int) * CHAR_BIT)
-
-#ifndef FALLTHROUGH
-# if __GNUC__ < 7
-# define FALLTHROUGH ((void) 0)
-# else
-# define FALLTHROUGH __attribute__ ((__fallthrough__))
-# endif
-#endif
-
-struct quoting_options
-{
- /* Basic quoting style. */
- enum quoting_style style;
-
- /* Additional flags. Bitwise combination of enum quoting_flags. */
- int flags;
-
- /* Quote the characters indicated by this bit vector even if the
- quoting style would not normally require them to be quoted. */
- unsigned int quote_these_too[(UCHAR_MAX / INT_BITS) + 1];
-
- /* The left quote for custom_quoting_style. */
- char const *left_quote;
-
- /* The right quote for custom_quoting_style. */
- char const *right_quote;
-};
-
-/* Names of quoting styles. */
-char const *const quoting_style_args[] =
-{
- "literal",
- "shell",
- "shell-always",
- "shell-escape",
- "shell-escape-always",
- "c",
- "c-maybe",
- "escape",
- "locale",
- "clocale",
- 0
-};
-
-/* Correspondences to quoting style names. */
-enum quoting_style const quoting_style_vals[] =
-{
- literal_quoting_style,
- shell_quoting_style,
- shell_always_quoting_style,
- shell_escape_quoting_style,
- shell_escape_always_quoting_style,
- c_quoting_style,
- c_maybe_quoting_style,
- escape_quoting_style,
- locale_quoting_style,
- clocale_quoting_style
-};
-
-/* The default quoting options. */
-static struct quoting_options default_quoting_options;
-
-/* Allocate a new set of quoting options, with contents initially identical
- to O if O is not null, or to the default if O is null.
- It is the caller's responsibility to free the result. */
-struct quoting_options *
-clone_quoting_options (struct quoting_options *o)
-{
- int e = errno;
- struct quoting_options *p = xmemdup (o ? o : &default_quoting_options,
- sizeof *o);
- errno = e;
- return p;
-}
-
-/* Get the value of O's quoting style. If O is null, use the default. */
-enum quoting_style
-get_quoting_style (struct quoting_options const *o)
-{
- return (o ? o : &default_quoting_options)->style;
-}
-
-/* In O (or in the default if O is null),
- set the value of the quoting style to S. */
-void
-set_quoting_style (struct quoting_options *o, enum quoting_style s)
-{
- (o ? o : &default_quoting_options)->style = s;
-}
-
-/* In O (or in the default if O is null),
- set the value of the quoting options for character C to I.
- Return the old value. Currently, the only values defined for I are
- 0 (the default) and 1 (which means to quote the character even if
- it would not otherwise be quoted). */
-int
-set_char_quoting (struct quoting_options *o, char c, int i)
-{
- unsigned char uc = c;
- unsigned int *p =
- (o ? o : &default_quoting_options)->quote_these_too + uc / INT_BITS;
- int shift = uc % INT_BITS;
- int r = (*p >> shift) & 1;
- *p ^= ((i & 1) ^ r) << shift;
- return r;
-}
-
-/* In O (or in the default if O is null),
- set the value of the quoting options flag to I, which can be a
- bitwise combination of enum quoting_flags, or 0 for default
- behavior. Return the old value. */
-int
-set_quoting_flags (struct quoting_options *o, int i)
-{
- int r;
- if (!o)
- o = &default_quoting_options;
- r = o->flags;
- o->flags = i;
- return r;
-}
-
-void
-set_custom_quoting (struct quoting_options *o,
- char const *left_quote, char const *right_quote)
-{
- if (!o)
- o = &default_quoting_options;
- o->style = custom_quoting_style;
- if (!left_quote || !right_quote)
- abort ();
- o->left_quote = left_quote;
- o->right_quote = right_quote;
-}
-
-/* Return quoting options for STYLE, with no extra quoting. */
-static struct quoting_options /* NOT PURE!! */
-quoting_options_from_style (enum quoting_style style)
-{
- struct quoting_options o = { literal_quoting_style, 0, { 0 }, NULL, NULL };
- if (style == custom_quoting_style)
- abort ();
- o.style = style;
- return o;
-}
-
-/* MSGID approximates a quotation mark. Return its translation if it
- has one; otherwise, return either it or "\"", depending on S.
-
- S is either clocale_quoting_style or locale_quoting_style. */
-static char const *
-gettext_quote (char const *msgid, enum quoting_style s)
-{
- char const *translation = _(msgid);
- char const *locale_code;
-
- if (translation != msgid)
- return translation;
-
- /* For UTF-8 and GB-18030, use single quotes U+2018 and U+2019.
- Here is a list of other locales that include U+2018 and U+2019:
-
- ISO-8859-7 0xA1 KOI8-T 0x91
- CP869 0x8B CP874 0x91
- CP932 0x81 0x65 CP936 0xA1 0xAE
- CP949 0xA1 0xAE CP950 0xA1 0xA5
- CP1250 0x91 CP1251 0x91
- CP1252 0x91 CP1253 0x91
- CP1254 0x91 CP1255 0x91
- CP1256 0x91 CP1257 0x91
- EUC-JP 0xA1 0xC6 EUC-KR 0xA1 0xAE
- EUC-TW 0xA1 0xE4 BIG5 0xA1 0xA5
- BIG5-HKSCS 0xA1 0xA5 EUC-CN 0xA1 0xAE
- GBK 0xA1 0xAE Georgian-PS 0x91
- PT154 0x91
-
- None of these is still in wide use; using iconv is overkill. */
- locale_code = locale_charset ();
- if (STRCASEEQ (locale_code, "UTF-8", 'U','T','F','-','8',0,0,0,0))
- return msgid[0] == '`' ? "\xe2\x80\x98": "\xe2\x80\x99";
- if (STRCASEEQ (locale_code, "GB18030", 'G','B','1','8','0','3','0',0,0))
- return msgid[0] == '`' ? "\xa1\ae": "\xa1\xaf";
-
- return (s == clocale_quoting_style ? "\"" : "'");
-}
-
-/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
- argument ARG (of size ARGSIZE), using QUOTING_STYLE, FLAGS, and
- QUOTE_THESE_TOO to control quoting.
- Terminate the output with a null character, and return the written
- size of the output, not counting the terminating null.
- If BUFFERSIZE is too small to store the output string, return the
- value that would have been returned had BUFFERSIZE been large enough.
- If ARGSIZE is SIZE_MAX, use the string length of the argument for ARGSIZE.
-
- This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG,
- ARGSIZE, O), except it breaks O into its component pieces and is
- not careful about errno. */
-
-static size_t
-quotearg_buffer_restyled (char *buffer, size_t buffersize,
- char const *arg, size_t argsize,
- enum quoting_style quoting_style, int flags,
- unsigned int const *quote_these_too,
- char const *left_quote,
- char const *right_quote)
-{
- size_t i;
- size_t len = 0;
- size_t orig_buffersize = 0;
- char const *quote_string = 0;
- size_t quote_string_len = 0;
- bool backslash_escapes = false;
- bool unibyte_locale = MB_CUR_MAX == 1;
- bool elide_outer_quotes = (flags & QA_ELIDE_OUTER_QUOTES) != 0;
- bool pending_shell_escape_end = false;
- bool encountered_single_quote = false;
- bool all_c_and_shell_quote_compat = true;
-
-#define STORE(c) \
- do \
- { \
- if (len < buffersize) \
- buffer[len] = (c); \
- len++; \
- } \
- while (0)
-
-#define START_ESC() \
- do \
- { \
- if (elide_outer_quotes) \
- goto force_outer_quoting_style; \
- escaping = true; \
- if (quoting_style == shell_always_quoting_style \
- && ! pending_shell_escape_end) \
- { \
- STORE ('\''); \
- STORE ('$'); \
- STORE ('\''); \
- pending_shell_escape_end = true; \
- } \
- STORE ('\\'); \
- } \
- while (0)
-
-#define END_ESC() \
- do \
- { \
- if (pending_shell_escape_end && ! escaping) \
- { \
- STORE ('\''); \
- STORE ('\''); \
- pending_shell_escape_end = false; \
- } \
- } \
- while (0)
-
- process_input:
-
- switch (quoting_style)
- {
- case c_maybe_quoting_style:
- quoting_style = c_quoting_style;
- elide_outer_quotes = true;
- FALLTHROUGH;
- case c_quoting_style:
- if (!elide_outer_quotes)
- STORE ('"');
- backslash_escapes = true;
- quote_string = "\"";
- quote_string_len = 1;
- break;
-
- case escape_quoting_style:
- backslash_escapes = true;
- elide_outer_quotes = false;
- break;
-
- case locale_quoting_style:
- case clocale_quoting_style:
- case custom_quoting_style:
- {
- if (quoting_style != custom_quoting_style)
- {
- /* TRANSLATORS:
- Get translations for open and closing quotation marks.
- The message catalog should translate "`" to a left
- quotation mark suitable for the locale, and similarly for
- "'". For example, a French Unicode local should translate
- these to U+00AB (LEFT-POINTING DOUBLE ANGLE
- QUOTATION MARK), and U+00BB (RIGHT-POINTING DOUBLE ANGLE
- QUOTATION MARK), respectively.
-
- If the catalog has no translation, we will try to
- use Unicode U+2018 (LEFT SINGLE QUOTATION MARK) and
- Unicode U+2019 (RIGHT SINGLE QUOTATION MARK). If the
- current locale is not Unicode, locale_quoting_style
- will quote 'like this', and clocale_quoting_style will
- quote "like this". You should always include translations
- for "`" and "'" even if U+2018 and U+2019 are appropriate
- for your locale.
-
- If you don't know what to put here, please see
-
- and use glyphs suitable for your language. */
- left_quote = gettext_quote (N_("`"), quoting_style);
- right_quote = gettext_quote (N_("'"), quoting_style);
- }
- if (!elide_outer_quotes)
- for (quote_string = left_quote; *quote_string; quote_string++)
- STORE (*quote_string);
- backslash_escapes = true;
- quote_string = right_quote;
- quote_string_len = strlen (quote_string);
- }
- break;
-
- case shell_escape_quoting_style:
- backslash_escapes = true;
- FALLTHROUGH;
- case shell_quoting_style:
- elide_outer_quotes = true;
- FALLTHROUGH;
- case shell_escape_always_quoting_style:
- if (!elide_outer_quotes)
- backslash_escapes = true;
- FALLTHROUGH;
- case shell_always_quoting_style:
- quoting_style = shell_always_quoting_style;
- if (!elide_outer_quotes)
- STORE ('\'');
- quote_string = "'";
- quote_string_len = 1;
- break;
-
- case literal_quoting_style:
- elide_outer_quotes = false;
- break;
-
- default:
- abort ();
- }
-
- for (i = 0; ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize); i++)
- {
- unsigned char c;
- unsigned char esc;
- bool is_right_quote = false;
- bool escaping = false;
- bool c_and_shell_quote_compat = false;
-
- if (backslash_escapes
- && quoting_style != shell_always_quoting_style
- && quote_string_len
- && (i + quote_string_len
- <= (argsize == SIZE_MAX && 1 < quote_string_len
- /* Use strlen only if we must: when argsize is SIZE_MAX,
- and when the quote string is more than 1 byte long.
- If we do call strlen, save the result. */
- ? (argsize = strlen (arg)) : argsize))
- && memcmp (arg + i, quote_string, quote_string_len) == 0)
- {
- if (elide_outer_quotes)
- goto force_outer_quoting_style;
- is_right_quote = true;
- }
-
- c = arg[i];
- switch (c)
- {
- case '\0':
- if (backslash_escapes)
- {
- START_ESC ();
- /* If quote_string were to begin with digits, we'd need to
- test for the end of the arg as well. However, it's
- hard to imagine any locale that would use digits in
- quotes, and set_custom_quoting is documented not to
- accept them. Use only a single \0 with shell-escape
- as currently digits are not printed within $'...' */
- if (quoting_style != shell_always_quoting_style
- && i + 1 < argsize && '0' <= arg[i + 1] && arg[i + 1] <= '9')
- {
- STORE ('0');
- STORE ('0');
- }
- c = '0';
- /* We don't have to worry that this last '0' will be
- backslash-escaped because, again, quote_string should
- not start with it and because quote_these_too is
- documented as not accepting it. */
- }
- else if (flags & QA_ELIDE_NULL_BYTES)
- continue;
- break;
-
- case '?':
- switch (quoting_style)
- {
- case shell_always_quoting_style:
- if (elide_outer_quotes)
- goto force_outer_quoting_style;
- break;
-
- case c_quoting_style:
- if ((flags & QA_SPLIT_TRIGRAPHS)
- && i + 2 < argsize && arg[i + 1] == '?')
- switch (arg[i + 2])
- {
- case '!': case '\'':
- case '(': case ')': case '-': case '/':
- case '<': case '=': case '>':
- /* Escape the second '?' in what would otherwise be
- a trigraph. */
- if (elide_outer_quotes)
- goto force_outer_quoting_style;
- c = arg[i + 2];
- i += 2;
- STORE ('?');
- STORE ('"');
- STORE ('"');
- STORE ('?');
- break;
-
- default:
- break;
- }
- break;
-
- default:
- break;
- }
- break;
-
- case '\a': esc = 'a'; goto c_escape;
- case '\b': esc = 'b'; goto c_escape;
- case '\f': esc = 'f'; goto c_escape;
- case '\n': esc = 'n'; goto c_and_shell_escape;
- case '\r': esc = 'r'; goto c_and_shell_escape;
- case '\t': esc = 't'; goto c_and_shell_escape;
- case '\v': esc = 'v'; goto c_escape;
- case '\\': esc = c;
- /* Never need to escape '\' in shell case. */
- if (quoting_style == shell_always_quoting_style)
- {
- if (elide_outer_quotes)
- goto force_outer_quoting_style;
- goto store_c;
- }
-
- /* No need to escape the escape if we are trying to elide
- outer quotes and nothing else is problematic. */
- if (backslash_escapes && elide_outer_quotes && quote_string_len)
- goto store_c;
-
- c_and_shell_escape:
- if (quoting_style == shell_always_quoting_style
- && elide_outer_quotes)
- goto force_outer_quoting_style;
- c_escape:
- if (backslash_escapes)
- {
- c = esc;
- goto store_escape;
- }
- break;
-
- case '{': case '}': /* sometimes special if isolated */
- if (! (argsize == SIZE_MAX ? arg[1] == '\0' : argsize == 1))
- break;
- FALLTHROUGH;
- case '#': case '~':
- if (i != 0)
- break;
- FALLTHROUGH;
- case ' ':
- c_and_shell_quote_compat = true;
- FALLTHROUGH;
- case '!': /* special in bash */
- case '"': case '$': case '&':
- case '(': case ')': case '*': case ';':
- case '<':
- case '=': /* sometimes special in 0th or (with "set -k") later args */
- case '>': case '[':
- case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */
- case '`': case '|':
- /* A shell special character. In theory, '$' and '`' could
- be the first bytes of multibyte characters, which means
- we should check them with mbrtowc, but in practice this
- doesn't happen so it's not worth worrying about. */
- if (quoting_style == shell_always_quoting_style
- && elide_outer_quotes)
- goto force_outer_quoting_style;
- break;
-
- case '\'':
- encountered_single_quote = true;
- c_and_shell_quote_compat = true;
- if (quoting_style == shell_always_quoting_style)
- {
- if (elide_outer_quotes)
- goto force_outer_quoting_style;
-
- if (buffersize && ! orig_buffersize)
- {
- /* Just scan string to see if supports a more concise
- representation, rather than writing a longer string
- but returning the length of the more concise form. */
- orig_buffersize = buffersize;
- buffersize = 0;
- }
-
- STORE ('\'');
- STORE ('\\');
- STORE ('\'');
- pending_shell_escape_end = false;
- }
- break;
-
- case '%': case '+': case ',': case '-': case '.': case '/':
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9': case ':':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z': case ']': case '_': case 'a': case 'b':
- case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
- case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
- case 'o': case 'p': case 'q': case 'r': case 's': case 't':
- case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
- /* These characters don't cause problems, no matter what the
- quoting style is. They cannot start multibyte sequences.
- A digit or a special letter would cause trouble if it
- appeared at the beginning of quote_string because we'd then
- escape by prepending a backslash. However, it's hard to
- imagine any locale that would use digits or letters as
- quotes, and set_custom_quoting is documented not to accept
- them. Also, a digit or a special letter would cause
- trouble if it appeared in quote_these_too, but that's also
- documented as not accepting them. */
- c_and_shell_quote_compat = true;
- break;
-
- default:
- /* If we have a multibyte sequence, copy it until we reach
- its end, find an error, or come back to the initial shift
- state. For C-like styles, if the sequence has
- unprintable characters, escape the whole sequence, since
- we can't easily escape single characters within it. */
- {
- /* Length of multibyte sequence found so far. */
- size_t m;
-
- bool printable;
-
- if (unibyte_locale)
- {
- m = 1;
- printable = isprint (c) != 0;
- }
- else
- {
- mbstate_t mbstate;
- memset (&mbstate, 0, sizeof mbstate);
-
- m = 0;
- printable = true;
- if (argsize == SIZE_MAX)
- argsize = strlen (arg);
-
- do
- {
- wchar_t w;
- size_t bytes = mbrtowc (&w, &arg[i + m],
- argsize - (i + m), &mbstate);
- if (bytes == 0)
- break;
- else if (bytes == (size_t) -1)
- {
- printable = false;
- break;
- }
- else if (bytes == (size_t) -2)
- {
- printable = false;
- while (i + m < argsize && arg[i + m])
- m++;
- break;
- }
- else
- {
- /* Work around a bug with older shells that "see" a '\'
- that is really the 2nd byte of a multibyte character.
- In practice the problem is limited to ASCII
- chars >= '@' that are shell special chars. */
- if ('[' == 0x5b && elide_outer_quotes
- && quoting_style == shell_always_quoting_style)
- {
- size_t j;
- for (j = 1; j < bytes; j++)
- switch (arg[i + m + j])
- {
- case '[': case '\\': case '^':
- case '`': case '|':
- goto force_outer_quoting_style;
-
- default:
- break;
- }
- }
-
- if (! iswprint (w))
- printable = false;
- m += bytes;
- }
- }
- while (! mbsinit (&mbstate));
- }
-
- c_and_shell_quote_compat = printable;
-
- if (1 < m || (backslash_escapes && ! printable))
- {
- /* Output a multibyte sequence, or an escaped
- unprintable unibyte character. */
- size_t ilim = i + m;
-
- for (;;)
- {
- if (backslash_escapes && ! printable)
- {
- START_ESC ();
- STORE ('0' + (c >> 6));
- STORE ('0' + ((c >> 3) & 7));
- c = '0' + (c & 7);
- }
- else if (is_right_quote)
- {
- STORE ('\\');
- is_right_quote = false;
- }
- if (ilim <= i + 1)
- break;
- END_ESC ();
- STORE (c);
- c = arg[++i];
- }
-
- goto store_c;
- }
- }
- }
-
- if (! (((backslash_escapes && quoting_style != shell_always_quoting_style)
- || elide_outer_quotes)
- && quote_these_too
- && quote_these_too[c / INT_BITS] >> (c % INT_BITS) & 1)
- && !is_right_quote)
- goto store_c;
-
- store_escape:
- START_ESC ();
-
- store_c:
- END_ESC ();
- STORE (c);
-
- if (! c_and_shell_quote_compat)
- all_c_and_shell_quote_compat = false;
- }
-
- if (len == 0 && quoting_style == shell_always_quoting_style
- && elide_outer_quotes)
- goto force_outer_quoting_style;
-
- /* Single shell quotes (') are commonly enough used as an apostrophe,
- that we attempt to minimize the quoting in this case. Note itʼs
- better to use the apostrophe modifier "\u02BC" if possible, as that
- renders better and works with the word match regex \W+ etc. */
- if (quoting_style == shell_always_quoting_style && ! elide_outer_quotes
- && encountered_single_quote)
- {
- if (all_c_and_shell_quote_compat)
- return quotearg_buffer_restyled (buffer, orig_buffersize, arg, argsize,
- c_quoting_style,
- flags, quote_these_too,
- left_quote, right_quote);
- else if (! buffersize && orig_buffersize)
- {
- /* Disable read-only scan, and reprocess to write quoted string. */
- buffersize = orig_buffersize;
- len = 0;
- goto process_input;
- }
- }
-
- if (quote_string && !elide_outer_quotes)
- for (; *quote_string; quote_string++)
- STORE (*quote_string);
-
- if (len < buffersize)
- buffer[len] = '\0';
- return len;
-
- force_outer_quoting_style:
- /* Don't reuse quote_these_too, since the addition of outer quotes
- sufficiently quotes the specified characters. */
- if (quoting_style == shell_always_quoting_style && backslash_escapes)
- quoting_style = shell_escape_always_quoting_style;
- return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
- quoting_style,
- flags & ~QA_ELIDE_OUTER_QUOTES, NULL,
- left_quote, right_quote);
-}
-
-/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
- argument ARG (of size ARGSIZE), using O to control quoting.
- If O is null, use the default.
- Terminate the output with a null character, and return the written
- size of the output, not counting the terminating null.
- If BUFFERSIZE is too small to store the output string, return the
- value that would have been returned had BUFFERSIZE been large enough.
- If ARGSIZE is SIZE_MAX, use the string length of the argument for
- ARGSIZE. */
-size_t
-quotearg_buffer (char *buffer, size_t buffersize,
- char const *arg, size_t argsize,
- struct quoting_options const *o)
-{
- struct quoting_options const *p = o ? o : &default_quoting_options;
- int e = errno;
- size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
- p->style, p->flags, p->quote_these_too,
- p->left_quote, p->right_quote);
- errno = e;
- return r;
-}
-
-/* Equivalent to quotearg_alloc (ARG, ARGSIZE, NULL, O). */
-char *
-quotearg_alloc (char const *arg, size_t argsize,
- struct quoting_options const *o)
-{
- return quotearg_alloc_mem (arg, argsize, NULL, o);
-}
-
-/* Like quotearg_buffer (..., ARG, ARGSIZE, O), except return newly
- allocated storage containing the quoted string, and store the
- resulting size into *SIZE, if non-NULL. The result can contain
- embedded null bytes only if ARGSIZE is not SIZE_MAX, SIZE is not
- NULL, and set_quoting_flags has not set the null byte elision
- flag. */
-char *
-quotearg_alloc_mem (char const *arg, size_t argsize, size_t *size,
- struct quoting_options const *o)
-{
- struct quoting_options const *p = o ? o : &default_quoting_options;
- int e = errno;
- /* Elide embedded null bytes if we can't return a size. */
- int flags = p->flags | (size ? 0 : QA_ELIDE_NULL_BYTES);
- size_t bufsize = quotearg_buffer_restyled (0, 0, arg, argsize, p->style,
- flags, p->quote_these_too,
- p->left_quote,
- p->right_quote) + 1;
- char *buf = xcharalloc (bufsize);
- quotearg_buffer_restyled (buf, bufsize, arg, argsize, p->style, flags,
- p->quote_these_too,
- p->left_quote, p->right_quote);
- errno = e;
- if (size)
- *size = bufsize - 1;
- return buf;
-}
-
-/* A storage slot with size and pointer to a value. */
-struct slotvec
-{
- size_t size;
- char *val;
-};
-
-/* Preallocate a slot 0 buffer, so that the caller can always quote
- one small component of a "memory exhausted" message in slot 0. */
-static char slot0[256];
-static int nslots = 1;
-static struct slotvec slotvec0 = {sizeof slot0, slot0};
-static struct slotvec *slotvec = &slotvec0;
-
-void
-quotearg_free (void)
-{
- struct slotvec *sv = slotvec;
- int i;
- for (i = 1; i < nslots; i++)
- free (sv[i].val);
- if (sv[0].val != slot0)
- {
- free (sv[0].val);
- slotvec0.size = sizeof slot0;
- slotvec0.val = slot0;
- }
- if (sv != &slotvec0)
- {
- free (sv);
- slotvec = &slotvec0;
- }
- nslots = 1;
-}
-
-/* Use storage slot N to return a quoted version of argument ARG.
- ARG is of size ARGSIZE, but if that is SIZE_MAX, ARG is a
- null-terminated string.
- OPTIONS specifies the quoting options.
- The returned value points to static storage that can be
- reused by the next call to this function with the same value of N.
- N must be nonnegative. N is deliberately declared with type "int"
- to allow for future extensions (using negative values). */
-static char *
-quotearg_n_options (int n, char const *arg, size_t argsize,
- struct quoting_options const *options)
-{
- int e = errno;
-
- struct slotvec *sv = slotvec;
-
- if (n < 0)
- abort ();
-
- if (nslots <= n)
- {
- bool preallocated = (sv == &slotvec0);
-
- if (MIN (INT_MAX, MIN (PTRDIFF_MAX, SIZE_MAX) / sizeof *sv) <= n)
- xalloc_die ();
-
- slotvec = sv = xrealloc (preallocated ? NULL : sv, (n + 1) * sizeof *sv);
- if (preallocated)
- *sv = slotvec0;
- memset (sv + nslots, 0, (n + 1 - nslots) * sizeof *sv);
- nslots = n + 1;
- }
-
- {
- size_t size = sv[n].size;
- char *val = sv[n].val;
- /* Elide embedded null bytes since we don't return a size. */
- int flags = options->flags | QA_ELIDE_NULL_BYTES;
- size_t qsize = quotearg_buffer_restyled (val, size, arg, argsize,
- options->style, flags,
- options->quote_these_too,
- options->left_quote,
- options->right_quote);
-
- if (size <= qsize)
- {
- sv[n].size = size = qsize + 1;
- if (val != slot0)
- free (val);
- sv[n].val = val = xcharalloc (size);
- quotearg_buffer_restyled (val, size, arg, argsize, options->style,
- flags, options->quote_these_too,
- options->left_quote,
- options->right_quote);
- }
-
- errno = e;
- return val;
- }
-}
-
-char *
-quotearg_n (int n, char const *arg)
-{
- return quotearg_n_options (n, arg, SIZE_MAX, &default_quoting_options);
-}
-
-char *
-quotearg_n_mem (int n, char const *arg, size_t argsize)
-{
- return quotearg_n_options (n, arg, argsize, &default_quoting_options);
-}
-
-char *
-quotearg (char const *arg)
-{
- return quotearg_n (0, arg);
-}
-
-char *
-quotearg_mem (char const *arg, size_t argsize)
-{
- return quotearg_n_mem (0, arg, argsize);
-}
-
-char *
-quotearg_n_style (int n, enum quoting_style s, char const *arg)
-{
- struct quoting_options const o = quoting_options_from_style (s);
- return quotearg_n_options (n, arg, SIZE_MAX, &o);
-}
-
-char *
-quotearg_n_style_mem (int n, enum quoting_style s,
- char const *arg, size_t argsize)
-{
- struct quoting_options const o = quoting_options_from_style (s);
- return quotearg_n_options (n, arg, argsize, &o);
-}
-
-char *
-quotearg_style (enum quoting_style s, char const *arg)
-{
- return quotearg_n_style (0, s, arg);
-}
-
-char *
-quotearg_style_mem (enum quoting_style s, char const *arg, size_t argsize)
-{
- return quotearg_n_style_mem (0, s, arg, argsize);
-}
-
-char *
-quotearg_char_mem (char const *arg, size_t argsize, char ch)
-{
- struct quoting_options options;
- options = default_quoting_options;
- set_char_quoting (&options, ch, 1);
- return quotearg_n_options (0, arg, argsize, &options);
-}
-
-char *
-quotearg_char (char const *arg, char ch)
-{
- return quotearg_char_mem (arg, SIZE_MAX, ch);
-}
-
-char *
-quotearg_colon (char const *arg)
-{
- return quotearg_char (arg, ':');
-}
-
-char *
-quotearg_colon_mem (char const *arg, size_t argsize)
-{
- return quotearg_char_mem (arg, argsize, ':');
-}
-
-char *
-quotearg_n_style_colon (int n, enum quoting_style s, char const *arg)
-{
- struct quoting_options options;
- options = quoting_options_from_style (s);
- set_char_quoting (&options, ':', 1);
- return quotearg_n_options (n, arg, SIZE_MAX, &options);
-}
-
-char *
-quotearg_n_custom (int n, char const *left_quote,
- char const *right_quote, char const *arg)
-{
- return quotearg_n_custom_mem (n, left_quote, right_quote, arg,
- SIZE_MAX);
-}
-
-char *
-quotearg_n_custom_mem (int n, char const *left_quote,
- char const *right_quote,
- char const *arg, size_t argsize)
-{
- struct quoting_options o = default_quoting_options;
- set_custom_quoting (&o, left_quote, right_quote);
- return quotearg_n_options (n, arg, argsize, &o);
-}
-
-char *
-quotearg_custom (char const *left_quote, char const *right_quote,
- char const *arg)
-{
- return quotearg_n_custom (0, left_quote, right_quote, arg);
-}
-
-char *
-quotearg_custom_mem (char const *left_quote, char const *right_quote,
- char const *arg, size_t argsize)
-{
- return quotearg_n_custom_mem (0, left_quote, right_quote, arg,
- argsize);
-}
-
-
-/* The quoting option used by the functions of quote.h. */
-struct quoting_options quote_quoting_options =
- {
- locale_quoting_style,
- 0,
- { 0 },
- NULL, NULL
- };
-
-char const *
-quote_n_mem (int n, char const *arg, size_t argsize)
-{
- return quotearg_n_options (n, arg, argsize, "e_quoting_options);
-}
-
-char const *
-quote_mem (char const *arg, size_t argsize)
-{
- return quote_n_mem (0, arg, argsize);
-}
-
-char const *
-quote_n (int n, char const *arg)
-{
- return quote_n_mem (n, arg, SIZE_MAX);
-}
-
-char const *
-quote (char const *arg)
-{
- return quote_n (0, arg);
-}
diff --git a/src/cmp.c b/src/cmp.c
index e711668..bc39153 100644
--- a/src/cmp.c
+++ b/src/cmp.c
@@ -330,15 +330,12 @@ main (int argc, char **argv)
/* If only a return code is needed,
and if both input descriptors are associated with plain files,
- and if both files are larger than 0 bytes (procfs files are always 0),
conclude that the files differ if they have different sizes
and if more bytes will be compared than are in the smaller file. */
if (comparison_type == type_status
&& S_ISREG (stat_buf[0].st_mode)
- && S_ISREG (stat_buf[1].st_mode)
- && stat_buf[0].st_size > 0
- && stat_buf[1].st_size > 0)
+ && S_ISREG (stat_buf[1].st_mode))
{
off_t s0 = stat_buf[0].st_size - file_position (0);
off_t s1 = stat_buf[1].st_size - file_position (1);
@@ -642,11 +639,9 @@ count_newlines (char *buf, size_t bufsize)
size_t count = 0;
char *p;
char *lim = buf + bufsize;
- char ch = *lim;
*lim = '\n';
for (p = buf; (p = rawmemchr (p, '\n')) != lim; p++)
count++;
- *lim = ch;
return count;
}
diff --git a/src/cmp.c.cmp-s-empty b/src/cmp.c.cmp-s-empty
deleted file mode 100644
index bc39153..0000000
--- a/src/cmp.c.cmp-s-empty
+++ /dev/null
@@ -1,693 +0,0 @@
-/* cmp - compare two files byte by byte
-
- Copyright (C) 1990-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
- 2015-2017 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "system.h"
-#include "paths.h"
-
-#include
-
-#include
-#include
-#include "die.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* The official name of this program (e.g., no 'g' prefix). */
-#define PROGRAM_NAME "cmp"
-
-#define AUTHORS \
- proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
- proper_name ("David MacKenzie")
-
-#if defined LC_MESSAGES && ENABLE_NLS
-# define hard_locale_LC_MESSAGES hard_locale (LC_MESSAGES)
-#else
-# define hard_locale_LC_MESSAGES 0
-#endif
-
-static int cmp (void);
-static off_t file_position (int);
-static size_t block_compare (word const *, word const *) _GL_ATTRIBUTE_PURE;
-static size_t count_newlines (char *, size_t);
-static void sprintc (char *, unsigned char);
-
-/* Filenames of the compared files. */
-static char const *file[2];
-
-/* File descriptors of the files. */
-static int file_desc[2];
-
-/* Status of the files. */
-static struct stat stat_buf[2];
-
-/* Read buffers for the files. */
-static word *buffer[2];
-
-/* Optimal block size for the files. */
-static size_t buf_size;
-
-/* Initial prefix to ignore for each file. */
-static off_t ignore_initial[2];
-
-/* Number of bytes to compare. */
-static uintmax_t bytes = UINTMAX_MAX;
-
-/* Output format. */
-static enum comparison_type
- {
- type_first_diff, /* Print the first difference. */
- type_all_diffs, /* Print all differences. */
- type_no_stdout, /* Do not output to stdout; only stderr. */
- type_status /* Exit status only. */
- } comparison_type;
-
-/* If nonzero, print values of bytes quoted like cat -t does. */
-static bool opt_print_bytes;
-
-/* Values for long options that do not have single-letter equivalents. */
-enum
-{
- HELP_OPTION = CHAR_MAX + 1
-};
-
-static struct option const long_options[] =
-{
- {"print-bytes", 0, 0, 'b'},
- {"print-chars", 0, 0, 'c'}, /* obsolescent as of diffutils 2.7.3 */
- {"ignore-initial", 1, 0, 'i'},
- {"verbose", 0, 0, 'l'},
- {"bytes", 1, 0, 'n'},
- {"silent", 0, 0, 's'},
- {"quiet", 0, 0, 's'},
- {"version", 0, 0, 'v'},
- {"help", 0, 0, HELP_OPTION},
- {0, 0, 0, 0}
-};
-
-static void try_help (char const *, char const *) __attribute__((noreturn));
-static void
-try_help (char const *reason_msgid, char const *operand)
-{
- if (reason_msgid)
- error (0, 0, _(reason_msgid), operand);
- die (EXIT_TROUBLE, 0,
- _("Try '%s --help' for more information."), program_name);
-}
-
-static char const valid_suffixes[] = "kKMGTPEZY0";
-
-/* Update ignore_initial[F] according to the result of parsing an
- *operand ARGPTR of --ignore-initial, updating *ARGPTR to point
- *after the operand. If DELIMITER is nonzero, the operand may be
- *followed by DELIMITER; otherwise it must be null-terminated. */
-static void
-specify_ignore_initial (int f, char **argptr, char delimiter)
-{
- uintmax_t val;
- char const *arg = *argptr;
- strtol_error e = xstrtoumax (arg, argptr, 0, &val, valid_suffixes);
- if (! (e == LONGINT_OK
- || (e == LONGINT_INVALID_SUFFIX_CHAR && **argptr == delimiter))
- || TYPE_MAXIMUM (off_t) < val)
- try_help ("invalid --ignore-initial value '%s'", arg);
- if (ignore_initial[f] < val)
- ignore_initial[f] = val;
-}
-
-/* Specify the output format. */
-static void
-specify_comparison_type (enum comparison_type t)
-{
- if (comparison_type && comparison_type != t)
- try_help ("options -l and -s are incompatible", 0);
- comparison_type = t;
-}
-
-static void
-check_stdout (void)
-{
- if (ferror (stdout))
- die (EXIT_TROUBLE, 0, "%s", _("write failed"));
- else if (fclose (stdout) != 0)
- die (EXIT_TROUBLE, errno, "%s", _("standard output"));
-}
-
-static char const * const option_help_msgid[] = {
- N_("-b, --print-bytes print differing bytes"),
- N_("-i, --ignore-initial=SKIP skip first SKIP bytes of both inputs"),
- N_("-i, --ignore-initial=SKIP1:SKIP2 skip first SKIP1 bytes of FILE1 and\n"
- " first SKIP2 bytes of FILE2"),
- N_("-l, --verbose output byte numbers and differing byte values"),
- N_("-n, --bytes=LIMIT compare at most LIMIT bytes"),
- N_("-s, --quiet, --silent suppress all normal output"),
- N_(" --help display this help and exit"),
- N_("-v, --version output version information and exit"),
- 0
-};
-
-static void
-usage (void)
-{
- char const * const *p;
-
- printf (_("Usage: %s [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\n"),
- program_name);
- printf ("%s\n", _("Compare two files byte by byte."));
- printf ("\n%s\n\n",
-_("The optional SKIP1 and SKIP2 specify the number of bytes to skip\n"
- "at the beginning of each file (zero by default)."));
-
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
- for (p = option_help_msgid; *p; p++)
- printf (" %s\n", _(*p));
- printf ("\n%s\n\n%s\n%s\n",
- _("SKIP values may be followed by the following multiplicative suffixes:\n\
-kB 1000, K 1024, MB 1,000,000, M 1,048,576,\n\
-GB 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y."),
- _("If a FILE is '-' or missing, read standard input."),
- _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."));
- emit_bug_reporting_address ();
-}
-
-int
-main (int argc, char **argv)
-{
- int c, f, exit_status;
- size_t words_per_buffer;
-
- exit_failure = EXIT_TROUBLE;
- initialize_main (&argc, &argv);
- set_program_name (argv[0]);
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
- c_stack_action (0);
-
- /* Parse command line options. */
-
- while ((c = getopt_long (argc, argv, "bci:ln:sv", long_options, 0))
- != -1)
- switch (c)
- {
- case 'b':
- case 'c': /* 'c' is obsolescent as of diffutils 2.7.3 */
- opt_print_bytes = true;
- break;
-
- case 'i':
- specify_ignore_initial (0, &optarg, ':');
- if (*optarg++ == ':')
- specify_ignore_initial (1, &optarg, 0);
- else if (ignore_initial[1] < ignore_initial[0])
- ignore_initial[1] = ignore_initial[0];
- break;
-
- case 'l':
- specify_comparison_type (type_all_diffs);
- break;
-
- case 'n':
- {
- uintmax_t n;
- if (xstrtoumax (optarg, 0, 0, &n, valid_suffixes) != LONGINT_OK)
- try_help ("invalid --bytes value '%s'", optarg);
- if (n < bytes)
- bytes = n;
- }
- break;
-
- case 's':
- specify_comparison_type (type_status);
- break;
-
- case 'v':
- version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
- AUTHORS, (char *) NULL);
- check_stdout ();
- return EXIT_SUCCESS;
-
- case HELP_OPTION:
- usage ();
- check_stdout ();
- return EXIT_SUCCESS;
-
- default:
- try_help (0, 0);
- }
-
- if (optind == argc)
- try_help ("missing operand after '%s'", argv[argc - 1]);
-
- file[0] = argv[optind++];
- file[1] = optind < argc ? argv[optind++] : "-";
-
- for (f = 0; f < 2 && optind < argc; f++)
- {
- char *arg = argv[optind++];
- specify_ignore_initial (f, &arg, 0);
- }
-
- if (optind < argc)
- try_help ("extra operand '%s'", argv[optind]);
-
- for (f = 0; f < 2; f++)
- {
- /* If file[1] is "-", treat it first; this avoids a misdiagnostic if
- stdin is closed and opening file[0] yields file descriptor 0. */
- int f1 = f ^ (STREQ (file[1], "-"));
-
- /* Two files with the same name and offset are identical.
- But wait until we open the file once, for proper diagnostics. */
- if (f && ignore_initial[0] == ignore_initial[1]
- && file_name_cmp (file[0], file[1]) == 0)
- return EXIT_SUCCESS;
-
- if (STREQ (file[f1], "-"))
- {
- file_desc[f1] = STDIN_FILENO;
- if (O_BINARY && ! isatty (STDIN_FILENO))
- set_binary_mode (STDIN_FILENO, O_BINARY);
- }
- else
- file_desc[f1] = open (file[f1], O_RDONLY | O_BINARY, 0);
-
- if (file_desc[f1] < 0 || fstat (file_desc[f1], stat_buf + f1) != 0)
- {
- if (file_desc[f1] < 0 && comparison_type == type_status)
- exit (EXIT_TROUBLE);
- else
- die (EXIT_TROUBLE, errno, "%s", file[f1]);
- }
- }
-
- /* If the files are links to the same inode and have the same file position,
- they are identical. */
-
- if (0 < same_file (&stat_buf[0], &stat_buf[1])
- && same_file_attributes (&stat_buf[0], &stat_buf[1])
- && file_position (0) == file_position (1))
- return EXIT_SUCCESS;
-
- /* If output is redirected to the null device, we can avoid some of
- the work. */
-
- if (comparison_type != type_status)
- {
- struct stat outstat, nullstat;
-
- if (fstat (STDOUT_FILENO, &outstat) == 0
- && stat (NULL_DEVICE, &nullstat) == 0
- && 0 < same_file (&outstat, &nullstat))
- comparison_type = type_no_stdout;
- }
-
- /* If only a return code is needed,
- and if both input descriptors are associated with plain files,
- conclude that the files differ if they have different sizes
- and if more bytes will be compared than are in the smaller file. */
-
- if (comparison_type == type_status
- && S_ISREG (stat_buf[0].st_mode)
- && S_ISREG (stat_buf[1].st_mode))
- {
- off_t s0 = stat_buf[0].st_size - file_position (0);
- off_t s1 = stat_buf[1].st_size - file_position (1);
- if (s0 < 0)
- s0 = 0;
- if (s1 < 0)
- s1 = 0;
- if (s0 != s1 && MIN (s0, s1) < bytes)
- exit (EXIT_FAILURE);
- }
-
- /* Get the optimal block size of the files. */
-
- buf_size = buffer_lcm (STAT_BLOCKSIZE (stat_buf[0]),
- STAT_BLOCKSIZE (stat_buf[1]),
- PTRDIFF_MAX - sizeof (word));
-
- /* Allocate word-aligned buffers, with space for sentinels at the end. */
-
- words_per_buffer = (buf_size + 2 * sizeof (word) - 1) / sizeof (word);
- buffer[0] = xmalloc (2 * sizeof (word) * words_per_buffer);
- buffer[1] = buffer[0] + words_per_buffer;
-
- exit_status = cmp ();
-
- for (f = 0; f < 2; f++)
- if (close (file_desc[f]) != 0)
- die (EXIT_TROUBLE, errno, "%s", file[f]);
- if (exit_status != EXIT_SUCCESS && comparison_type < type_no_stdout)
- check_stdout ();
- exit (exit_status);
- return exit_status;
-}
-
-/* Compare the two files already open on 'file_desc[0]' and 'file_desc[1]',
- using 'buffer[0]' and 'buffer[1]'.
- Return EXIT_SUCCESS if identical, EXIT_FAILURE if different,
- >1 if error. */
-
-static int
-cmp (void)
-{
- bool at_line_start = true;
- off_t line_number = 1; /* Line number (1...) of difference. */
- off_t byte_number = 1; /* Byte number (1...) of difference. */
- uintmax_t remaining = bytes; /* Remaining number of bytes to compare. */
- size_t read0, read1; /* Number of bytes read from each file. */
- size_t first_diff; /* Offset (0...) in buffers of 1st diff. */
- size_t smaller; /* The lesser of 'read0' and 'read1'. */
- word *buffer0 = buffer[0];
- word *buffer1 = buffer[1];
- char *buf0 = (char *) buffer0;
- char *buf1 = (char *) buffer1;
- int differing = 0;
- int f;
- int offset_width IF_LINT (= 0);
-
- if (comparison_type == type_all_diffs)
- {
- off_t byte_number_max = MIN (bytes, TYPE_MAXIMUM (off_t));
-
- for (f = 0; f < 2; f++)
- if (S_ISREG (stat_buf[f].st_mode))
- {
- off_t file_bytes = stat_buf[f].st_size - file_position (f);
- if (file_bytes < byte_number_max)
- byte_number_max = file_bytes;
- }
-
- for (offset_width = 1; (byte_number_max /= 10) != 0; offset_width++)
- continue;
- }
-
- for (f = 0; f < 2; f++)
- {
- off_t ig = ignore_initial[f];
- if (ig && file_position (f) == -1)
- {
- /* lseek failed; read and discard the ignored initial prefix. */
- do
- {
- size_t bytes_to_read = MIN (ig, buf_size);
- size_t r = block_read (file_desc[f], buf0, bytes_to_read);
- if (r != bytes_to_read)
- {
- if (r == SIZE_MAX)
- die (EXIT_TROUBLE, errno, "%s", file[f]);
- break;
- }
- ig -= r;
- }
- while (ig);
- }
- }
-
- do
- {
- size_t bytes_to_read = buf_size;
-
- if (remaining != UINTMAX_MAX)
- {
- if (remaining < bytes_to_read)
- bytes_to_read = remaining;
- remaining -= bytes_to_read;
- }
-
- read0 = block_read (file_desc[0], buf0, bytes_to_read);
- if (read0 == SIZE_MAX)
- die (EXIT_TROUBLE, errno, "%s", file[0]);
- read1 = block_read (file_desc[1], buf1, bytes_to_read);
- if (read1 == SIZE_MAX)
- die (EXIT_TROUBLE, errno, "%s", file[1]);
-
- smaller = MIN (read0, read1);
-
- /* Optimize the common case where the buffers are the same. */
- if (memcmp (buf0, buf1, smaller) == 0)
- first_diff = smaller;
- else
- {
- /* Insert sentinels for the block compare. */
- buf0[read0] = ~buf1[read0];
- buf1[read1] = ~buf0[read1];
-
- first_diff = block_compare (buffer0, buffer1);
- }
-
- byte_number += first_diff;
- if (comparison_type == type_first_diff && first_diff != 0)
- {
- line_number += count_newlines (buf0, first_diff);
- at_line_start = buf0[first_diff - 1] == '\n';
- }
-
- if (first_diff < smaller)
- {
- switch (comparison_type)
- {
- case type_first_diff:
- {
- char byte_buf[INT_BUFSIZE_BOUND (off_t)];
- char line_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *byte_num = offtostr (byte_number, byte_buf);
- char const *line_num = offtostr (line_number, line_buf);
- if (!opt_print_bytes)
- {
- /* See POSIX for this format. This message is
- used only in the POSIX locale, so it need not
- be translated. */
- static char const char_message[] =
- "%s %s differ: char %s, line %s\n";
-
- /* The POSIX rationale recommends using the word
- "byte" outside the POSIX locale. Some gettext
- implementations translate even in the POSIX
- locale if certain other environment variables
- are set, so use "byte" if a translation is
- available, or if outside the POSIX locale. */
- static char const byte_msgid[] =
- N_("%s %s differ: byte %s, line %s\n");
- char const *byte_message = _(byte_msgid);
- bool use_byte_message = (byte_message != byte_msgid
- || hard_locale_LC_MESSAGES);
-
- printf (use_byte_message ? byte_message : char_message,
- file[0], file[1], byte_num, line_num);
- }
- else
- {
- unsigned char c0 = buf0[first_diff];
- unsigned char c1 = buf1[first_diff];
- char s0[5];
- char s1[5];
- sprintc (s0, c0);
- sprintc (s1, c1);
- printf (_("%s %s differ: byte %s, line %s is %3o %s %3o %s\n"),
- file[0], file[1], byte_num, line_num,
- c0, s0, c1, s1);
- }
- }
- FALLTHROUGH;
- case type_status:
- return EXIT_FAILURE;
-
- case type_all_diffs:
- do
- {
- unsigned char c0 = buf0[first_diff];
- unsigned char c1 = buf1[first_diff];
- if (c0 != c1)
- {
- char byte_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *byte_num = offtostr (byte_number, byte_buf);
- if (!opt_print_bytes)
- {
- /* See POSIX for this format. */
- printf ("%*s %3o %3o\n",
- offset_width, byte_num, c0, c1);
- }
- else
- {
- char s0[5];
- char s1[5];
- sprintc (s0, c0);
- sprintc (s1, c1);
- printf ("%*s %3o %-4s %3o %s\n",
- offset_width, byte_num, c0, s0, c1, s1);
- }
- }
- byte_number++;
- first_diff++;
- }
- while (first_diff < smaller);
- differing = -1;
- break;
-
- case type_no_stdout:
- differing = 1;
- break;
- }
- }
-
- if (read0 != read1)
- {
- if (differing <= 0 && comparison_type != type_status)
- {
- char const *shorter_file = file[read1 < read0];
-
- /* POSIX says that each of these format strings must be
- "cmp: EOF on %s", optionally followed by a blank and
- extra text sans newline, then terminated by "\n". */
- if (byte_number == 1)
- fprintf (stderr, _("cmp: EOF on %s which is empty\n"),
- shorter_file);
- else
- {
- char byte_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *byte_num = offtostr (byte_number - 1, byte_buf);
-
- if (comparison_type == type_first_diff)
- {
- char line_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *line_num
- = offtostr (line_number - at_line_start, line_buf);
- fprintf (stderr,
- (at_line_start
- ? _("cmp: EOF on %s after byte %s, line %s\n")
- : _("cmp: EOF on %s after byte %s,"
- " in line %s\n")),
- shorter_file, byte_num, line_num);
- }
- else
- fprintf (stderr,
- _("cmp: EOF on %s after byte %s\n"),
- shorter_file, byte_num);
- }
- }
-
- return EXIT_FAILURE;
- }
- }
- while (differing <= 0 && read0 == buf_size);
-
- return differing == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-}
-
-/* Compare two blocks of memory P0 and P1 until they differ.
- If the blocks are not guaranteed to be different, put sentinels at the ends
- of the blocks before calling this function.
-
- Return the offset of the first byte that differs. */
-
-static size_t
-block_compare (word const *p0, word const *p1)
-{
- word const *l0, *l1;
- char const *c0, *c1;
-
- /* Find the rough position of the first difference by reading words,
- not bytes. */
-
- for (l0 = p0, l1 = p1; *l0 == *l1; l0++, l1++)
- continue;
-
- /* Find the exact differing position (endianness independent). */
-
- for (c0 = (char const *) l0, c1 = (char const *) l1;
- *c0 == *c1;
- c0++, c1++)
- continue;
-
- return c0 - (char const *) p0;
-}
-
-/* Return the number of newlines in BUF, of size BUFSIZE,
- where BUF[NBYTES] is available for use as a sentinel. */
-
-static size_t
-count_newlines (char *buf, size_t bufsize)
-{
- size_t count = 0;
- char *p;
- char *lim = buf + bufsize;
- *lim = '\n';
- for (p = buf; (p = rawmemchr (p, '\n')) != lim; p++)
- count++;
- return count;
-}
-
-/* Put into BUF the unsigned char C, making unprintable bytes
- visible by quoting like cat -t does. */
-
-static void
-sprintc (char *buf, unsigned char c)
-{
- if (! isprint (c))
- {
- if (c >= 128)
- {
- *buf++ = 'M';
- *buf++ = '-';
- c -= 128;
- }
- if (c < 32)
- {
- *buf++ = '^';
- c += 64;
- }
- else if (c == 127)
- {
- *buf++ = '^';
- c = '?';
- }
- }
-
- *buf++ = c;
- *buf = 0;
-}
-
-/* Position file F to ignore_initial[F] bytes from its initial position,
- and yield its new position. Don't try more than once. */
-
-static off_t
-file_position (int f)
-{
- static bool positioned[2];
- static off_t position[2];
-
- if (! positioned[f])
- {
- positioned[f] = true;
- position[f] = lseek (file_desc[f], ignore_initial[f], SEEK_CUR);
- }
- return position[f];
-}
diff --git a/src/cmp.c.shows_incorrect_data b/src/cmp.c.shows_incorrect_data
deleted file mode 100644
index 431b4c6..0000000
--- a/src/cmp.c.shows_incorrect_data
+++ /dev/null
@@ -1,696 +0,0 @@
-/* cmp - compare two files byte by byte
-
- Copyright (C) 1990-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
- 2015-2017 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "system.h"
-#include "paths.h"
-
-#include
-
-#include
-#include
-#include "die.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* The official name of this program (e.g., no 'g' prefix). */
-#define PROGRAM_NAME "cmp"
-
-#define AUTHORS \
- proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
- proper_name ("David MacKenzie")
-
-#if defined LC_MESSAGES && ENABLE_NLS
-# define hard_locale_LC_MESSAGES hard_locale (LC_MESSAGES)
-#else
-# define hard_locale_LC_MESSAGES 0
-#endif
-
-static int cmp (void);
-static off_t file_position (int);
-static size_t block_compare (word const *, word const *) _GL_ATTRIBUTE_PURE;
-static size_t count_newlines (char *, size_t);
-static void sprintc (char *, unsigned char);
-
-/* Filenames of the compared files. */
-static char const *file[2];
-
-/* File descriptors of the files. */
-static int file_desc[2];
-
-/* Status of the files. */
-static struct stat stat_buf[2];
-
-/* Read buffers for the files. */
-static word *buffer[2];
-
-/* Optimal block size for the files. */
-static size_t buf_size;
-
-/* Initial prefix to ignore for each file. */
-static off_t ignore_initial[2];
-
-/* Number of bytes to compare. */
-static uintmax_t bytes = UINTMAX_MAX;
-
-/* Output format. */
-static enum comparison_type
- {
- type_first_diff, /* Print the first difference. */
- type_all_diffs, /* Print all differences. */
- type_no_stdout, /* Do not output to stdout; only stderr. */
- type_status /* Exit status only. */
- } comparison_type;
-
-/* If nonzero, print values of bytes quoted like cat -t does. */
-static bool opt_print_bytes;
-
-/* Values for long options that do not have single-letter equivalents. */
-enum
-{
- HELP_OPTION = CHAR_MAX + 1
-};
-
-static struct option const long_options[] =
-{
- {"print-bytes", 0, 0, 'b'},
- {"print-chars", 0, 0, 'c'}, /* obsolescent as of diffutils 2.7.3 */
- {"ignore-initial", 1, 0, 'i'},
- {"verbose", 0, 0, 'l'},
- {"bytes", 1, 0, 'n'},
- {"silent", 0, 0, 's'},
- {"quiet", 0, 0, 's'},
- {"version", 0, 0, 'v'},
- {"help", 0, 0, HELP_OPTION},
- {0, 0, 0, 0}
-};
-
-static void try_help (char const *, char const *) __attribute__((noreturn));
-static void
-try_help (char const *reason_msgid, char const *operand)
-{
- if (reason_msgid)
- error (0, 0, _(reason_msgid), operand);
- die (EXIT_TROUBLE, 0,
- _("Try '%s --help' for more information."), program_name);
-}
-
-static char const valid_suffixes[] = "kKMGTPEZY0";
-
-/* Update ignore_initial[F] according to the result of parsing an
- *operand ARGPTR of --ignore-initial, updating *ARGPTR to point
- *after the operand. If DELIMITER is nonzero, the operand may be
- *followed by DELIMITER; otherwise it must be null-terminated. */
-static void
-specify_ignore_initial (int f, char **argptr, char delimiter)
-{
- uintmax_t val;
- char const *arg = *argptr;
- strtol_error e = xstrtoumax (arg, argptr, 0, &val, valid_suffixes);
- if (! (e == LONGINT_OK
- || (e == LONGINT_INVALID_SUFFIX_CHAR && **argptr == delimiter))
- || TYPE_MAXIMUM (off_t) < val)
- try_help ("invalid --ignore-initial value '%s'", arg);
- if (ignore_initial[f] < val)
- ignore_initial[f] = val;
-}
-
-/* Specify the output format. */
-static void
-specify_comparison_type (enum comparison_type t)
-{
- if (comparison_type && comparison_type != t)
- try_help ("options -l and -s are incompatible", 0);
- comparison_type = t;
-}
-
-static void
-check_stdout (void)
-{
- if (ferror (stdout))
- die (EXIT_TROUBLE, 0, "%s", _("write failed"));
- else if (fclose (stdout) != 0)
- die (EXIT_TROUBLE, errno, "%s", _("standard output"));
-}
-
-static char const * const option_help_msgid[] = {
- N_("-b, --print-bytes print differing bytes"),
- N_("-i, --ignore-initial=SKIP skip first SKIP bytes of both inputs"),
- N_("-i, --ignore-initial=SKIP1:SKIP2 skip first SKIP1 bytes of FILE1 and\n"
- " first SKIP2 bytes of FILE2"),
- N_("-l, --verbose output byte numbers and differing byte values"),
- N_("-n, --bytes=LIMIT compare at most LIMIT bytes"),
- N_("-s, --quiet, --silent suppress all normal output"),
- N_(" --help display this help and exit"),
- N_("-v, --version output version information and exit"),
- 0
-};
-
-static void
-usage (void)
-{
- char const * const *p;
-
- printf (_("Usage: %s [OPTION]... FILE1 [FILE2 [SKIP1 [SKIP2]]]\n"),
- program_name);
- printf ("%s\n", _("Compare two files byte by byte."));
- printf ("\n%s\n\n",
-_("The optional SKIP1 and SKIP2 specify the number of bytes to skip\n"
- "at the beginning of each file (zero by default)."));
-
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
- for (p = option_help_msgid; *p; p++)
- printf (" %s\n", _(*p));
- printf ("\n%s\n\n%s\n%s\n",
- _("SKIP values may be followed by the following multiplicative suffixes:\n\
-kB 1000, K 1024, MB 1,000,000, M 1,048,576,\n\
-GB 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y."),
- _("If a FILE is '-' or missing, read standard input."),
- _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."));
- emit_bug_reporting_address ();
-}
-
-int
-main (int argc, char **argv)
-{
- int c, f, exit_status;
- size_t words_per_buffer;
-
- exit_failure = EXIT_TROUBLE;
- initialize_main (&argc, &argv);
- set_program_name (argv[0]);
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
- c_stack_action (0);
-
- /* Parse command line options. */
-
- while ((c = getopt_long (argc, argv, "bci:ln:sv", long_options, 0))
- != -1)
- switch (c)
- {
- case 'b':
- case 'c': /* 'c' is obsolescent as of diffutils 2.7.3 */
- opt_print_bytes = true;
- break;
-
- case 'i':
- specify_ignore_initial (0, &optarg, ':');
- if (*optarg++ == ':')
- specify_ignore_initial (1, &optarg, 0);
- else if (ignore_initial[1] < ignore_initial[0])
- ignore_initial[1] = ignore_initial[0];
- break;
-
- case 'l':
- specify_comparison_type (type_all_diffs);
- break;
-
- case 'n':
- {
- uintmax_t n;
- if (xstrtoumax (optarg, 0, 0, &n, valid_suffixes) != LONGINT_OK)
- try_help ("invalid --bytes value '%s'", optarg);
- if (n < bytes)
- bytes = n;
- }
- break;
-
- case 's':
- specify_comparison_type (type_status);
- break;
-
- case 'v':
- version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
- AUTHORS, (char *) NULL);
- check_stdout ();
- return EXIT_SUCCESS;
-
- case HELP_OPTION:
- usage ();
- check_stdout ();
- return EXIT_SUCCESS;
-
- default:
- try_help (0, 0);
- }
-
- if (optind == argc)
- try_help ("missing operand after '%s'", argv[argc - 1]);
-
- file[0] = argv[optind++];
- file[1] = optind < argc ? argv[optind++] : "-";
-
- for (f = 0; f < 2 && optind < argc; f++)
- {
- char *arg = argv[optind++];
- specify_ignore_initial (f, &arg, 0);
- }
-
- if (optind < argc)
- try_help ("extra operand '%s'", argv[optind]);
-
- for (f = 0; f < 2; f++)
- {
- /* If file[1] is "-", treat it first; this avoids a misdiagnostic if
- stdin is closed and opening file[0] yields file descriptor 0. */
- int f1 = f ^ (STREQ (file[1], "-"));
-
- /* Two files with the same name and offset are identical.
- But wait until we open the file once, for proper diagnostics. */
- if (f && ignore_initial[0] == ignore_initial[1]
- && file_name_cmp (file[0], file[1]) == 0)
- return EXIT_SUCCESS;
-
- if (STREQ (file[f1], "-"))
- {
- file_desc[f1] = STDIN_FILENO;
- if (O_BINARY && ! isatty (STDIN_FILENO))
- set_binary_mode (STDIN_FILENO, O_BINARY);
- }
- else
- file_desc[f1] = open (file[f1], O_RDONLY | O_BINARY, 0);
-
- if (file_desc[f1] < 0 || fstat (file_desc[f1], stat_buf + f1) != 0)
- {
- if (file_desc[f1] < 0 && comparison_type == type_status)
- exit (EXIT_TROUBLE);
- else
- die (EXIT_TROUBLE, errno, "%s", file[f1]);
- }
- }
-
- /* If the files are links to the same inode and have the same file position,
- they are identical. */
-
- if (0 < same_file (&stat_buf[0], &stat_buf[1])
- && same_file_attributes (&stat_buf[0], &stat_buf[1])
- && file_position (0) == file_position (1))
- return EXIT_SUCCESS;
-
- /* If output is redirected to the null device, we can avoid some of
- the work. */
-
- if (comparison_type != type_status)
- {
- struct stat outstat, nullstat;
-
- if (fstat (STDOUT_FILENO, &outstat) == 0
- && stat (NULL_DEVICE, &nullstat) == 0
- && 0 < same_file (&outstat, &nullstat))
- comparison_type = type_no_stdout;
- }
-
- /* If only a return code is needed,
- and if both input descriptors are associated with plain files,
- and if both files are larger than 0 bytes (procfs files are always 0),
- conclude that the files differ if they have different sizes
- and if more bytes will be compared than are in the smaller file. */
-
- if (comparison_type == type_status
- && S_ISREG (stat_buf[0].st_mode)
- && S_ISREG (stat_buf[1].st_mode)
- && stat_buf[0].st_size > 0
- && stat_buf[1].st_size > 0)
- {
- off_t s0 = stat_buf[0].st_size - file_position (0);
- off_t s1 = stat_buf[1].st_size - file_position (1);
- if (s0 < 0)
- s0 = 0;
- if (s1 < 0)
- s1 = 0;
- if (s0 != s1 && MIN (s0, s1) < bytes)
- exit (EXIT_FAILURE);
- }
-
- /* Get the optimal block size of the files. */
-
- buf_size = buffer_lcm (STAT_BLOCKSIZE (stat_buf[0]),
- STAT_BLOCKSIZE (stat_buf[1]),
- PTRDIFF_MAX - sizeof (word));
-
- /* Allocate word-aligned buffers, with space for sentinels at the end. */
-
- words_per_buffer = (buf_size + 2 * sizeof (word) - 1) / sizeof (word);
- buffer[0] = xmalloc (2 * sizeof (word) * words_per_buffer);
- buffer[1] = buffer[0] + words_per_buffer;
-
- exit_status = cmp ();
-
- for (f = 0; f < 2; f++)
- if (close (file_desc[f]) != 0)
- die (EXIT_TROUBLE, errno, "%s", file[f]);
- if (exit_status != EXIT_SUCCESS && comparison_type < type_no_stdout)
- check_stdout ();
- exit (exit_status);
- return exit_status;
-}
-
-/* Compare the two files already open on 'file_desc[0]' and 'file_desc[1]',
- using 'buffer[0]' and 'buffer[1]'.
- Return EXIT_SUCCESS if identical, EXIT_FAILURE if different,
- >1 if error. */
-
-static int
-cmp (void)
-{
- bool at_line_start = true;
- off_t line_number = 1; /* Line number (1...) of difference. */
- off_t byte_number = 1; /* Byte number (1...) of difference. */
- uintmax_t remaining = bytes; /* Remaining number of bytes to compare. */
- size_t read0, read1; /* Number of bytes read from each file. */
- size_t first_diff; /* Offset (0...) in buffers of 1st diff. */
- size_t smaller; /* The lesser of 'read0' and 'read1'. */
- word *buffer0 = buffer[0];
- word *buffer1 = buffer[1];
- char *buf0 = (char *) buffer0;
- char *buf1 = (char *) buffer1;
- int differing = 0;
- int f;
- int offset_width IF_LINT (= 0);
-
- if (comparison_type == type_all_diffs)
- {
- off_t byte_number_max = MIN (bytes, TYPE_MAXIMUM (off_t));
-
- for (f = 0; f < 2; f++)
- if (S_ISREG (stat_buf[f].st_mode))
- {
- off_t file_bytes = stat_buf[f].st_size - file_position (f);
- if (file_bytes < byte_number_max)
- byte_number_max = file_bytes;
- }
-
- for (offset_width = 1; (byte_number_max /= 10) != 0; offset_width++)
- continue;
- }
-
- for (f = 0; f < 2; f++)
- {
- off_t ig = ignore_initial[f];
- if (ig && file_position (f) == -1)
- {
- /* lseek failed; read and discard the ignored initial prefix. */
- do
- {
- size_t bytes_to_read = MIN (ig, buf_size);
- size_t r = block_read (file_desc[f], buf0, bytes_to_read);
- if (r != bytes_to_read)
- {
- if (r == SIZE_MAX)
- die (EXIT_TROUBLE, errno, "%s", file[f]);
- break;
- }
- ig -= r;
- }
- while (ig);
- }
- }
-
- do
- {
- size_t bytes_to_read = buf_size;
-
- if (remaining != UINTMAX_MAX)
- {
- if (remaining < bytes_to_read)
- bytes_to_read = remaining;
- remaining -= bytes_to_read;
- }
-
- read0 = block_read (file_desc[0], buf0, bytes_to_read);
- if (read0 == SIZE_MAX)
- die (EXIT_TROUBLE, errno, "%s", file[0]);
- read1 = block_read (file_desc[1], buf1, bytes_to_read);
- if (read1 == SIZE_MAX)
- die (EXIT_TROUBLE, errno, "%s", file[1]);
-
- smaller = MIN (read0, read1);
-
- /* Optimize the common case where the buffers are the same. */
- if (memcmp (buf0, buf1, smaller) == 0)
- first_diff = smaller;
- else
- {
- /* Insert sentinels for the block compare. */
- buf0[read0] = ~buf1[read0];
- buf1[read1] = ~buf0[read1];
-
- first_diff = block_compare (buffer0, buffer1);
- }
-
- byte_number += first_diff;
- if (comparison_type == type_first_diff && first_diff != 0)
- {
- line_number += count_newlines (buf0, first_diff);
- at_line_start = buf0[first_diff - 1] == '\n';
- }
-
- if (first_diff < smaller)
- {
- switch (comparison_type)
- {
- case type_first_diff:
- {
- char byte_buf[INT_BUFSIZE_BOUND (off_t)];
- char line_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *byte_num = offtostr (byte_number, byte_buf);
- char const *line_num = offtostr (line_number, line_buf);
- if (!opt_print_bytes)
- {
- /* See POSIX for this format. This message is
- used only in the POSIX locale, so it need not
- be translated. */
- static char const char_message[] =
- "%s %s differ: char %s, line %s\n";
-
- /* The POSIX rationale recommends using the word
- "byte" outside the POSIX locale. Some gettext
- implementations translate even in the POSIX
- locale if certain other environment variables
- are set, so use "byte" if a translation is
- available, or if outside the POSIX locale. */
- static char const byte_msgid[] =
- N_("%s %s differ: byte %s, line %s\n");
- char const *byte_message = _(byte_msgid);
- bool use_byte_message = (byte_message != byte_msgid
- || hard_locale_LC_MESSAGES);
-
- printf (use_byte_message ? byte_message : char_message,
- file[0], file[1], byte_num, line_num);
- }
- else
- {
- unsigned char c0 = buf0[first_diff];
- unsigned char c1 = buf1[first_diff];
- char s0[5];
- char s1[5];
- sprintc (s0, c0);
- sprintc (s1, c1);
- printf (_("%s %s differ: byte %s, line %s is %3o %s %3o %s\n"),
- file[0], file[1], byte_num, line_num,
- c0, s0, c1, s1);
- }
- }
- FALLTHROUGH;
- case type_status:
- return EXIT_FAILURE;
-
- case type_all_diffs:
- do
- {
- unsigned char c0 = buf0[first_diff];
- unsigned char c1 = buf1[first_diff];
- if (c0 != c1)
- {
- char byte_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *byte_num = offtostr (byte_number, byte_buf);
- if (!opt_print_bytes)
- {
- /* See POSIX for this format. */
- printf ("%*s %3o %3o\n",
- offset_width, byte_num, c0, c1);
- }
- else
- {
- char s0[5];
- char s1[5];
- sprintc (s0, c0);
- sprintc (s1, c1);
- printf ("%*s %3o %-4s %3o %s\n",
- offset_width, byte_num, c0, s0, c1, s1);
- }
- }
- byte_number++;
- first_diff++;
- }
- while (first_diff < smaller);
- differing = -1;
- break;
-
- case type_no_stdout:
- differing = 1;
- break;
- }
- }
-
- if (read0 != read1)
- {
- if (differing <= 0 && comparison_type != type_status)
- {
- char const *shorter_file = file[read1 < read0];
-
- /* POSIX says that each of these format strings must be
- "cmp: EOF on %s", optionally followed by a blank and
- extra text sans newline, then terminated by "\n". */
- if (byte_number == 1)
- fprintf (stderr, _("cmp: EOF on %s which is empty\n"),
- shorter_file);
- else
- {
- char byte_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *byte_num = offtostr (byte_number - 1, byte_buf);
-
- if (comparison_type == type_first_diff)
- {
- char line_buf[INT_BUFSIZE_BOUND (off_t)];
- char const *line_num
- = offtostr (line_number - at_line_start, line_buf);
- fprintf (stderr,
- (at_line_start
- ? _("cmp: EOF on %s after byte %s, line %s\n")
- : _("cmp: EOF on %s after byte %s,"
- " in line %s\n")),
- shorter_file, byte_num, line_num);
- }
- else
- fprintf (stderr,
- _("cmp: EOF on %s after byte %s\n"),
- shorter_file, byte_num);
- }
- }
-
- return EXIT_FAILURE;
- }
- }
- while (differing <= 0 && read0 == buf_size);
-
- return differing == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-}
-
-/* Compare two blocks of memory P0 and P1 until they differ.
- If the blocks are not guaranteed to be different, put sentinels at the ends
- of the blocks before calling this function.
-
- Return the offset of the first byte that differs. */
-
-static size_t
-block_compare (word const *p0, word const *p1)
-{
- word const *l0, *l1;
- char const *c0, *c1;
-
- /* Find the rough position of the first difference by reading words,
- not bytes. */
-
- for (l0 = p0, l1 = p1; *l0 == *l1; l0++, l1++)
- continue;
-
- /* Find the exact differing position (endianness independent). */
-
- for (c0 = (char const *) l0, c1 = (char const *) l1;
- *c0 == *c1;
- c0++, c1++)
- continue;
-
- return c0 - (char const *) p0;
-}
-
-/* Return the number of newlines in BUF, of size BUFSIZE,
- where BUF[NBYTES] is available for use as a sentinel. */
-
-static size_t
-count_newlines (char *buf, size_t bufsize)
-{
- size_t count = 0;
- char *p;
- char *lim = buf + bufsize;
- *lim = '\n';
- for (p = buf; (p = rawmemchr (p, '\n')) != lim; p++)
- count++;
- return count;
-}
-
-/* Put into BUF the unsigned char C, making unprintable bytes
- visible by quoting like cat -t does. */
-
-static void
-sprintc (char *buf, unsigned char c)
-{
- if (! isprint (c))
- {
- if (c >= 128)
- {
- *buf++ = 'M';
- *buf++ = '-';
- c -= 128;
- }
- if (c < 32)
- {
- *buf++ = '^';
- c += 64;
- }
- else if (c == 127)
- {
- *buf++ = '^';
- c = '?';
- }
- }
-
- *buf++ = c;
- *buf = 0;
-}
-
-/* Position file F to ignore_initial[F] bytes from its initial position,
- and yield its new position. Don't try more than once. */
-
-static off_t
-file_position (int f)
-{
- static bool positioned[2];
- static off_t position[2];
-
- if (! positioned[f])
- {
- positioned[f] = true;
- position[f] = lseek (file_desc[f], ignore_initial[f], SEEK_CUR);
- }
- return position[f];
-}
diff --git a/src/diff.c b/src/diff.c
index 60c6ce7..df3338c 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -76,8 +76,6 @@ static void try_help (char const *, char const *) __attribute__((noreturn));
static void check_stdout (void);
static void usage (void);
-bool (*lines_differ) (char const *, size_t, char const *, size_t);
-
/* If comparing directories, compare their common subdirectories
recursively. */
static bool recursive;
@@ -300,13 +298,6 @@ main (int argc, char **argv)
excluded = new_exclude ();
presume_output_tty = false;
-#ifdef HANDLE_MULTIBYTE
- if (MB_CUR_MAX > 1)
- lines_differ = lines_differ_multibyte;
- else
-#endif
- lines_differ = lines_differ_singlebyte;
-
/* Decode the options. */
while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
diff --git a/src/diff.c.i18n b/src/diff.c.i18n
deleted file mode 100644
index df3338c..0000000
--- a/src/diff.c.i18n
+++ /dev/null
@@ -1,1473 +0,0 @@
-/* diff - compare files line by line
-
- Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
- 2009-2013, 2015-2017 Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#define GDIFF_MAIN
-#include "diff.h"
-#include "die.h"
-#include
-#include "paths.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* The official name of this program (e.g., no 'g' prefix). */
-#define PROGRAM_NAME "diff"
-
-#define AUTHORS \
- proper_name ("Paul Eggert"), \
- proper_name ("Mike Haertel"), \
- proper_name ("David Hayes"), \
- proper_name ("Richard Stallman"), \
- proper_name ("Len Tower")
-
-#ifndef GUTTER_WIDTH_MINIMUM
-# define GUTTER_WIDTH_MINIMUM 3
-#endif
-
-struct regexp_list
-{
- char *regexps; /* chars representing disjunction of the regexps */
- size_t len; /* chars used in 'regexps' */
- size_t size; /* size malloc'ed for 'regexps'; 0 if not malloc'ed */
- bool multiple_regexps;/* Does 'regexps' represent a disjunction? */
- struct re_pattern_buffer *buf;
-};
-
-static int compare_files (struct comparison const *, char const *, char const *);
-static void add_regexp (struct regexp_list *, char const *);
-static void summarize_regexp_list (struct regexp_list *);
-static void specify_style (enum output_style);
-static void specify_value (char const **, char const *, char const *);
-static void specify_colors_style (char const *);
-static void try_help (char const *, char const *) __attribute__((noreturn));
-static void check_stdout (void);
-static void usage (void);
-
-/* If comparing directories, compare their common subdirectories
- recursively. */
-static bool recursive;
-
-/* In context diffs, show previous lines that match these regexps. */
-static struct regexp_list function_regexp_list;
-
-/* Ignore changes affecting only lines that match these regexps. */
-static struct regexp_list ignore_regexp_list;
-
-#if O_BINARY
-/* Use binary I/O when reading and writing data (--binary).
- On POSIX hosts, this has no effect. */
-static bool binary;
-#else
-enum { binary = true };
-#endif
-
-/* If one file is missing, treat it as present but empty (-N). */
-static bool new_file;
-
-/* If the first file is missing, treat it as present but empty
- (--unidirectional-new-file). */
-static bool unidirectional_new_file;
-
-/* Report files compared that are the same (-s).
- Normally nothing is output when that happens. */
-static bool report_identical_files;
-
-static char const shortopts[] =
-"0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:yZ";
-
-/* Values for long options that do not have single-letter equivalents. */
-enum
-{
- BINARY_OPTION = CHAR_MAX + 1,
- FROM_FILE_OPTION,
- HELP_OPTION,
- HORIZON_LINES_OPTION,
- IGNORE_FILE_NAME_CASE_OPTION,
- INHIBIT_HUNK_MERGE_OPTION,
- LEFT_COLUMN_OPTION,
- LINE_FORMAT_OPTION,
- NO_DEREFERENCE_OPTION,
- NO_IGNORE_FILE_NAME_CASE_OPTION,
- NORMAL_OPTION,
- SDIFF_MERGE_ASSIST_OPTION,
- STRIP_TRAILING_CR_OPTION,
- SUPPRESS_BLANK_EMPTY_OPTION,
- SUPPRESS_COMMON_LINES_OPTION,
- TABSIZE_OPTION,
- TO_FILE_OPTION,
-
- /* These options must be in sequence. */
- UNCHANGED_LINE_FORMAT_OPTION,
- OLD_LINE_FORMAT_OPTION,
- NEW_LINE_FORMAT_OPTION,
-
- /* These options must be in sequence. */
- UNCHANGED_GROUP_FORMAT_OPTION,
- OLD_GROUP_FORMAT_OPTION,
- NEW_GROUP_FORMAT_OPTION,
- CHANGED_GROUP_FORMAT_OPTION,
-
- COLOR_OPTION,
- COLOR_PALETTE_OPTION,
-
- PRESUME_OUTPUT_TTY_OPTION,
-};
-
-static char const group_format_option[][sizeof "--unchanged-group-format"] =
- {
- "--unchanged-group-format",
- "--old-group-format",
- "--new-group-format",
- "--changed-group-format"
- };
-
-static char const line_format_option[][sizeof "--unchanged-line-format"] =
- {
- "--unchanged-line-format",
- "--old-line-format",
- "--new-line-format"
- };
-
-static struct option const longopts[] =
-{
- {"binary", 0, 0, BINARY_OPTION},
- {"brief", 0, 0, 'q'},
- {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
- {"color", 2, 0, COLOR_OPTION},
- {"context", 2, 0, 'C'},
- {"ed", 0, 0, 'e'},
- {"exclude", 1, 0, 'x'},
- {"exclude-from", 1, 0, 'X'},
- {"expand-tabs", 0, 0, 't'},
- {"forward-ed", 0, 0, 'f'},
- {"from-file", 1, 0, FROM_FILE_OPTION},
- {"help", 0, 0, HELP_OPTION},
- {"horizon-lines", 1, 0, HORIZON_LINES_OPTION},
- {"ifdef", 1, 0, 'D'},
- {"ignore-all-space", 0, 0, 'w'},
- {"ignore-blank-lines", 0, 0, 'B'},
- {"ignore-case", 0, 0, 'i'},
- {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION},
- {"ignore-matching-lines", 1, 0, 'I'},
- {"ignore-space-change", 0, 0, 'b'},
- {"ignore-tab-expansion", 0, 0, 'E'},
- {"ignore-trailing-space", 0, 0, 'Z'},
- {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION},
- {"initial-tab", 0, 0, 'T'},
- {"label", 1, 0, 'L'},
- {"left-column", 0, 0, LEFT_COLUMN_OPTION},
- {"line-format", 1, 0, LINE_FORMAT_OPTION},
- {"minimal", 0, 0, 'd'},
- {"new-file", 0, 0, 'N'},
- {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION},
- {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION},
- {"no-dereference", 0, 0, NO_DEREFERENCE_OPTION},
- {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION},
- {"normal", 0, 0, NORMAL_OPTION},
- {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
- {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
- {"paginate", 0, 0, 'l'},
- {"palette", 1, 0, COLOR_PALETTE_OPTION},
- {"rcs", 0, 0, 'n'},
- {"recursive", 0, 0, 'r'},
- {"report-identical-files", 0, 0, 's'},
- {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION},
- {"show-c-function", 0, 0, 'p'},
- {"show-function-line", 1, 0, 'F'},
- {"side-by-side", 0, 0, 'y'},
- {"speed-large-files", 0, 0, 'H'},
- {"starting-file", 1, 0, 'S'},
- {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
- {"suppress-blank-empty", 0, 0, SUPPRESS_BLANK_EMPTY_OPTION},
- {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION},
- {"tabsize", 1, 0, TABSIZE_OPTION},
- {"text", 0, 0, 'a'},
- {"to-file", 1, 0, TO_FILE_OPTION},
- {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION},
- {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION},
- {"unidirectional-new-file", 0, 0, 'P'},
- {"unified", 2, 0, 'U'},
- {"version", 0, 0, 'v'},
- {"width", 1, 0, 'W'},
-
- /* This is solely for testing. Do not document. */
- {"-presume-output-tty", no_argument, NULL, PRESUME_OUTPUT_TTY_OPTION},
- {0, 0, 0, 0}
-};
-
-/* Return a string containing the command options with which diff was invoked.
- Spaces appear between what were separate ARGV-elements.
- There is a space at the beginning but none at the end.
- If there were no options, the result is an empty string.
-
- Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
- the length of that vector. */
-
-static char *
-option_list (char **optionvec, int count)
-{
- int i;
- size_t size = 1;
- char *result;
- char *p;
-
- for (i = 0; i < count; i++)
- size += 1 + shell_quote_length (optionvec[i]);
-
- p = result = xmalloc (size);
-
- for (i = 0; i < count; i++)
- {
- *p++ = ' ';
- p = shell_quote_copy (p, optionvec[i]);
- }
-
- *p = '\0';
- return result;
-}
-
-
-/* Return an option value suitable for add_exclude. */
-
-static int
-exclude_options (void)
-{
- return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
-}
-
-int
-main (int argc, char **argv)
-{
- int exit_status = EXIT_SUCCESS;
- int c;
- int i;
- int prev = -1;
- lin ocontext = -1;
- bool explicit_context = false;
- size_t width = 0;
- bool show_c_function = false;
- char const *from_file = NULL;
- char const *to_file = NULL;
- uintmax_t numval;
- char *numend;
-
- /* Do our initializations. */
- exit_failure = EXIT_TROUBLE;
- initialize_main (&argc, &argv);
- set_program_name (argv[0]);
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
- c_stack_action (0);
- function_regexp_list.buf = &function_regexp;
- ignore_regexp_list.buf = &ignore_regexp;
- re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING);
- excluded = new_exclude ();
- presume_output_tty = false;
-
- /* Decode the options. */
-
- while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
- {
- switch (c)
- {
- case 0:
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- ocontext = (! ISDIGIT (prev)
- ? c - '0'
- : (ocontext - (c - '0' <= CONTEXT_MAX % 10)
- < CONTEXT_MAX / 10)
- ? 10 * ocontext + (c - '0')
- : CONTEXT_MAX);
- break;
-
- case 'a':
- text = true;
- break;
-
- case 'b':
- if (ignore_white_space < IGNORE_SPACE_CHANGE)
- ignore_white_space = IGNORE_SPACE_CHANGE;
- break;
-
- case 'Z':
- if (ignore_white_space < IGNORE_SPACE_CHANGE)
- ignore_white_space |= IGNORE_TRAILING_SPACE;
- break;
-
- case 'B':
- ignore_blank_lines = true;
- break;
-
- case 'C':
- case 'U':
- {
- if (optarg)
- {
- numval = strtoumax (optarg, &numend, 10);
- if (*numend)
- try_help ("invalid context length '%s'", optarg);
- if (CONTEXT_MAX < numval)
- numval = CONTEXT_MAX;
- }
- else
- numval = 3;
-
- specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
- if (context < numval)
- context = numval;
- explicit_context = true;
- }
- break;
-
- case 'c':
- specify_style (OUTPUT_CONTEXT);
- if (context < 3)
- context = 3;
- break;
-
- case 'd':
- minimal = true;
- break;
-
- case 'D':
- specify_style (OUTPUT_IFDEF);
- {
- static char const C_ifdef_group_formats[] =
- "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
- char *b = xmalloc (sizeof C_ifdef_group_formats
- + 7 * strlen (optarg) - 14 /* 7*"%s" */
- - 8 /* 5*"%%" + 3*"%c" */);
- sprintf (b, C_ifdef_group_formats,
- 0,
- optarg, optarg, 0,
- optarg, optarg, 0,
- optarg, optarg, optarg);
- for (i = 0; i < sizeof group_format / sizeof group_format[0]; i++)
- {
- specify_value (&group_format[i], b, "-D");
- b += strlen (b) + 1;
- }
- }
- break;
-
- case 'e':
- specify_style (OUTPUT_ED);
- break;
-
- case 'E':
- if (ignore_white_space < IGNORE_SPACE_CHANGE)
- ignore_white_space |= IGNORE_TAB_EXPANSION;
- break;
-
- case 'f':
- specify_style (OUTPUT_FORWARD_ED);
- break;
-
- case 'F':
- add_regexp (&function_regexp_list, optarg);
- break;
-
- case 'h':
- /* Split the files into chunks for faster processing.
- Usually does not change the result.
-
- This currently has no effect. */
- break;
-
- case 'H':
- speed_large_files = true;
- break;
-
- case 'i':
- ignore_case = true;
- break;
-
- case 'I':
- add_regexp (&ignore_regexp_list, optarg);
- break;
-
- case 'l':
- if (!pr_program[0])
- try_help ("pagination not supported on this host", NULL);
- paginate = true;
-#ifdef SIGCHLD
- /* Pagination requires forking and waiting, and
- System V fork+wait does not work if SIGCHLD is ignored. */
- signal (SIGCHLD, SIG_DFL);
-#endif
- break;
-
- case 'L':
- if (!file_label[0])
- file_label[0] = optarg;
- else if (!file_label[1])
- file_label[1] = optarg;
- else
- fatal ("too many file label options");
- break;
-
- case 'n':
- specify_style (OUTPUT_RCS);
- break;
-
- case 'N':
- new_file = true;
- break;
-
- case 'p':
- show_c_function = true;
- add_regexp (&function_regexp_list, "^[[:alpha:]$_]");
- break;
-
- case 'P':
- unidirectional_new_file = true;
- break;
-
- case 'q':
- brief = true;
- break;
-
- case 'r':
- recursive = true;
- break;
-
- case 's':
- report_identical_files = true;
- break;
-
- case 'S':
- specify_value (&starting_file, optarg, "-S");
- break;
-
- case 't':
- expand_tabs = true;
- break;
-
- case 'T':
- initial_tab = true;
- break;
-
- case 'u':
- specify_style (OUTPUT_UNIFIED);
- if (context < 3)
- context = 3;
- break;
-
- case 'v':
- version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
- AUTHORS, (char *) NULL);
- check_stdout ();
- return EXIT_SUCCESS;
-
- case 'w':
- ignore_white_space = IGNORE_ALL_SPACE;
- break;
-
- case 'x':
- add_exclude (excluded, optarg, exclude_options ());
- break;
-
- case 'X':
- if (add_exclude_file (add_exclude, excluded, optarg,
- exclude_options (), '\n'))
- pfatal_with_name (optarg);
- break;
-
- case 'y':
- specify_style (OUTPUT_SDIFF);
- break;
-
- case 'W':
- numval = strtoumax (optarg, &numend, 10);
- if (! (0 < numval && numval <= SIZE_MAX) || *numend)
- try_help ("invalid width '%s'", optarg);
- if (width != numval)
- {
- if (width)
- fatal ("conflicting width options");
- width = numval;
- }
- break;
-
- case BINARY_OPTION:
-#if O_BINARY
- binary = true;
- if (! isatty (STDOUT_FILENO))
- set_binary_mode (STDOUT_FILENO, O_BINARY);
-#endif
- break;
-
- case FROM_FILE_OPTION:
- specify_value (&from_file, optarg, "--from-file");
- break;
-
- case HELP_OPTION:
- usage ();
- check_stdout ();
- return EXIT_SUCCESS;
-
- case HORIZON_LINES_OPTION:
- numval = strtoumax (optarg, &numend, 10);
- if (*numend)
- try_help ("invalid horizon length '%s'", optarg);
- horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX));
- break;
-
- case IGNORE_FILE_NAME_CASE_OPTION:
- ignore_file_name_case = true;
- break;
-
- case INHIBIT_HUNK_MERGE_OPTION:
- /* This option is obsolete, but accept it for backward
- compatibility. */
- break;
-
- case LEFT_COLUMN_OPTION:
- left_column = true;
- break;
-
- case LINE_FORMAT_OPTION:
- specify_style (OUTPUT_IFDEF);
- for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
- specify_value (&line_format[i], optarg, "--line-format");
- break;
-
- case NO_DEREFERENCE_OPTION:
- no_dereference_symlinks = true;
- break;
-
- case NO_IGNORE_FILE_NAME_CASE_OPTION:
- ignore_file_name_case = false;
- break;
-
- case NORMAL_OPTION:
- specify_style (OUTPUT_NORMAL);
- break;
-
- case SDIFF_MERGE_ASSIST_OPTION:
- specify_style (OUTPUT_SDIFF);
- sdiff_merge_assist = true;
- break;
-
- case STRIP_TRAILING_CR_OPTION:
- strip_trailing_cr = true;
- break;
-
- case SUPPRESS_BLANK_EMPTY_OPTION:
- suppress_blank_empty = true;
- break;
-
- case SUPPRESS_COMMON_LINES_OPTION:
- suppress_common_lines = true;
- break;
-
- case TABSIZE_OPTION:
- numval = strtoumax (optarg, &numend, 10);
- if (! (0 < numval && numval <= SIZE_MAX - GUTTER_WIDTH_MINIMUM)
- || *numend)
- try_help ("invalid tabsize '%s'", optarg);
- if (tabsize != numval)
- {
- if (tabsize)
- fatal ("conflicting tabsize options");
- tabsize = numval;
- }
- break;
-
- case TO_FILE_OPTION:
- specify_value (&to_file, optarg, "--to-file");
- break;
-
- case UNCHANGED_LINE_FORMAT_OPTION:
- case OLD_LINE_FORMAT_OPTION:
- case NEW_LINE_FORMAT_OPTION:
- specify_style (OUTPUT_IFDEF);
- c -= UNCHANGED_LINE_FORMAT_OPTION;
- specify_value (&line_format[c], optarg, line_format_option[c]);
- break;
-
- case UNCHANGED_GROUP_FORMAT_OPTION:
- case OLD_GROUP_FORMAT_OPTION:
- case NEW_GROUP_FORMAT_OPTION:
- case CHANGED_GROUP_FORMAT_OPTION:
- specify_style (OUTPUT_IFDEF);
- c -= UNCHANGED_GROUP_FORMAT_OPTION;
- specify_value (&group_format[c], optarg, group_format_option[c]);
- break;
-
- case COLOR_OPTION:
- specify_colors_style (optarg);
- break;
-
- case COLOR_PALETTE_OPTION:
- set_color_palette (optarg);
- break;
-
- case PRESUME_OUTPUT_TTY_OPTION:
- presume_output_tty = true;
- break;
-
- default:
- try_help (NULL, NULL);
- }
- prev = c;
- }
-
- if (colors_style == AUTO)
- {
- char const *t = getenv ("TERM");
- if (t && STREQ (t, "dumb"))
- colors_style = NEVER;
- }
-
- if (output_style == OUTPUT_UNSPECIFIED)
- {
- if (show_c_function)
- {
- specify_style (OUTPUT_CONTEXT);
- if (ocontext < 0)
- context = 3;
- }
- else
- specify_style (OUTPUT_NORMAL);
- }
-
- if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
- {
-#if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
- || defined HAVE_STRUCT_STAT_ST_SPARE1)
- time_format = "%Y-%m-%d %H:%M:%S.%N %z";
-#else
- time_format = "%Y-%m-%d %H:%M:%S %z";
-#endif
- }
- else
- {
- /* See POSIX 1003.1-2001 for this format. */
- time_format = "%a %b %e %T %Y";
- }
-
- if (0 <= ocontext
- && (output_style == OUTPUT_CONTEXT
- || output_style == OUTPUT_UNIFIED)
- && (context < ocontext
- || (ocontext < context && ! explicit_context)))
- context = ocontext;
-
- if (! tabsize)
- tabsize = 8;
- if (! width)
- width = 130;
-
- {
- /* Maximize first the half line width, and then the gutter width,
- according to the following constraints:
-
- 1. Two half lines plus a gutter must fit in a line.
- 2. If the half line width is nonzero:
- a. The gutter width is at least GUTTER_WIDTH_MINIMUM.
- b. If tabs are not expanded to spaces,
- a half line plus a gutter is an integral number of tabs,
- so that tabs in the right column line up. */
-
- size_t t = expand_tabs ? 1 : tabsize;
- size_t w = width;
- size_t t_plus_g = t + GUTTER_WIDTH_MINIMUM;
- size_t unaligned_off = (w >> 1) + (t_plus_g >> 1) + (w & t_plus_g & 1);
- size_t off = unaligned_off - unaligned_off % t;
- sdiff_half_width = (off <= GUTTER_WIDTH_MINIMUM || w <= off
- ? 0
- : MIN (off - GUTTER_WIDTH_MINIMUM, w - off));
- sdiff_column2_offset = sdiff_half_width ? off : w;
- }
-
- /* Make the horizon at least as large as the context, so that
- shift_boundaries has more freedom to shift the first and last hunks. */
- if (horizon_lines < context)
- horizon_lines = context;
-
- summarize_regexp_list (&function_regexp_list);
- summarize_regexp_list (&ignore_regexp_list);
-
- if (output_style == OUTPUT_IFDEF)
- {
- for (i = 0; i < sizeof line_format / sizeof line_format[0]; i++)
- if (!line_format[i])
- line_format[i] = "%l\n";
- if (!group_format[OLD])
- group_format[OLD]
- = group_format[CHANGED] ? group_format[CHANGED] : "%<";
- if (!group_format[NEW])
- group_format[NEW]
- = group_format[CHANGED] ? group_format[CHANGED] : "%>";
- if (!group_format[UNCHANGED])
- group_format[UNCHANGED] = "%=";
- if (!group_format[CHANGED])
- group_format[CHANGED] = concat (group_format[OLD],
- group_format[NEW], "");
- }
-
- no_diff_means_no_output =
- (output_style == OUTPUT_IFDEF ?
- (!*group_format[UNCHANGED]
- || (STREQ (group_format[UNCHANGED], "%=")
- && !*line_format[UNCHANGED]))
- : (output_style != OUTPUT_SDIFF) | suppress_common_lines);
-
- files_can_be_treated_as_binary =
- (brief & binary
- & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr
- | (ignore_regexp_list.regexps || ignore_white_space)));
-
- switch_string = option_list (argv + 1, optind - 1);
-
- if (from_file)
- {
- if (to_file)
- fatal ("--from-file and --to-file both specified");
- else
- for (; optind < argc; optind++)
- {
- int status = compare_files (NULL, from_file, argv[optind]);
- if (exit_status < status)
- exit_status = status;
- }
- }
- else
- {
- if (to_file)
- for (; optind < argc; optind++)
- {
- int status = compare_files (NULL, argv[optind], to_file);
- if (exit_status < status)
- exit_status = status;
- }
- else
- {
- if (argc - optind != 2)
- {
- if (argc - optind < 2)
- try_help ("missing operand after '%s'", argv[argc - 1]);
- else
- try_help ("extra operand '%s'", argv[optind + 2]);
- }
-
- exit_status = compare_files (NULL, argv[optind], argv[optind + 1]);
- }
- }
-
- /* Print any messages that were saved up for last. */
- print_message_queue ();
-
- check_stdout ();
- exit (exit_status);
- return exit_status;
-}
-
-/* Append to REGLIST the regexp PATTERN. */
-
-static void
-add_regexp (struct regexp_list *reglist, char const *pattern)
-{
- size_t patlen = strlen (pattern);
- char const *m = re_compile_pattern (pattern, patlen, reglist->buf);
-
- if (m != 0)
- error (EXIT_TROUBLE, 0, "%s: %s", pattern, m);
- else
- {
- char *regexps = reglist->regexps;
- size_t len = reglist->len;
- bool multiple_regexps = reglist->multiple_regexps = regexps != 0;
- size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen;
- size_t size = reglist->size;
-
- if (size <= newlen)
- {
- if (!size)
- size = 1;
-
- do size *= 2;
- while (size <= newlen);
-
- reglist->size = size;
- reglist->regexps = regexps = xrealloc (regexps, size);
- }
- if (multiple_regexps)
- {
- regexps[len++] = '\\';
- regexps[len++] = '|';
- }
- memcpy (regexps + len, pattern, patlen + 1);
- }
-}
-
-/* Ensure that REGLIST represents the disjunction of its regexps.
- This is done here, rather than earlier, to avoid O(N^2) behavior. */
-
-static void
-summarize_regexp_list (struct regexp_list *reglist)
-{
- if (reglist->regexps)
- {
- /* At least one regexp was specified. Allocate a fastmap for it. */
- reglist->buf->fastmap = xmalloc (1 << CHAR_BIT);
- if (reglist->multiple_regexps)
- {
- /* Compile the disjunction of the regexps.
- (If just one regexp was specified, it is already compiled.) */
- char const *m = re_compile_pattern (reglist->regexps, reglist->len,
- reglist->buf);
- if (m)
- die (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m);
- }
- }
-}
-
-static void
-try_help (char const *reason_msgid, char const *operand)
-{
- if (reason_msgid)
- error (0, 0, _(reason_msgid), operand);
- die (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
- program_name);
-}
-
-static void
-check_stdout (void)
-{
- if (ferror (stdout))
- fatal ("write failed");
- else if (fclose (stdout) != 0)
- pfatal_with_name (_("standard output"));
-}
-
-static char const * const option_help_msgid[] = {
- N_(" --normal output a normal diff (the default)"),
- N_("-q, --brief report only when files differ"),
- N_("-s, --report-identical-files report when two files are the same"),
- N_("-c, -C NUM, --context[=NUM] output NUM (default 3) lines of copied context"),
- N_("-u, -U NUM, --unified[=NUM] output NUM (default 3) lines of unified context"),
- N_("-e, --ed output an ed script"),
- N_("-n, --rcs output an RCS format diff"),
- N_("-y, --side-by-side output in two columns"),
- N_("-W, --width=NUM output at most NUM (default 130) print columns"),
- N_(" --left-column output only the left column of common lines"),
- N_(" --suppress-common-lines do not output common lines"),
- "",
- N_("-p, --show-c-function show which C function each change is in"),
- N_("-F, --show-function-line=RE show the most recent line matching RE"),
- N_(" --label LABEL use LABEL instead of file name and timestamp\n"
- " (can be repeated)"),
- "",
- N_("-t, --expand-tabs expand tabs to spaces in output"),
- N_("-T, --initial-tab make tabs line up by prepending a tab"),
- N_(" --tabsize=NUM tab stops every NUM (default 8) print columns"),
- N_(" --suppress-blank-empty suppress space or tab before empty output lines"),
- N_("-l, --paginate pass output through 'pr' to paginate it"),
- "",
- N_("-r, --recursive recursively compare any subdirectories found"),
- N_(" --no-dereference don't follow symbolic links"),
- N_("-N, --new-file treat absent files as empty"),
- N_(" --unidirectional-new-file treat absent first files as empty"),
- N_(" --ignore-file-name-case ignore case when comparing file names"),
- N_(" --no-ignore-file-name-case consider case when comparing file names"),
- N_("-x, --exclude=PAT exclude files that match PAT"),
- N_("-X, --exclude-from=FILE exclude files that match any pattern in FILE"),
- N_("-S, --starting-file=FILE start with FILE when comparing directories"),
- N_(" --from-file=FILE1 compare FILE1 to all operands;\n"
- " FILE1 can be a directory"),
- N_(" --to-file=FILE2 compare all operands to FILE2;\n"
- " FILE2 can be a directory"),
- "",
- N_("-i, --ignore-case ignore case differences in file contents"),
- N_("-E, --ignore-tab-expansion ignore changes due to tab expansion"),
- N_("-Z, --ignore-trailing-space ignore white space at line end"),
- N_("-b, --ignore-space-change ignore changes in the amount of white space"),
- N_("-w, --ignore-all-space ignore all white space"),
- N_("-B, --ignore-blank-lines ignore changes where lines are all blank"),
- N_("-I, --ignore-matching-lines=RE ignore changes where all lines match RE"),
- "",
- N_("-a, --text treat all files as text"),
- N_(" --strip-trailing-cr strip trailing carriage return on input"),
-#if O_BINARY
- N_(" --binary read and write data in binary mode"),
-#endif
- "",
- N_("-D, --ifdef=NAME output merged file with '#ifdef NAME' diffs"),
- N_(" --GTYPE-group-format=GFMT format GTYPE input groups with GFMT"),
- N_(" --line-format=LFMT format all input lines with LFMT"),
- N_(" --LTYPE-line-format=LFMT format LTYPE input lines with LFMT"),
- N_(" These format options provide fine-grained control over the output\n"
- " of diff, generalizing -D/--ifdef."),
- N_(" LTYPE is 'old', 'new', or 'unchanged'. GTYPE is LTYPE or 'changed'."),
- N_(" GFMT (only) may contain:\n\
- %< lines from FILE1\n\
- %> lines from FILE2\n\
- %= lines common to FILE1 and FILE2\n\
- %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\
- LETTERs are as follows for new group, lower case for old group:\n\
- F first line number\n\
- L last line number\n\
- N number of lines = L-F+1\n\
- E F-1\n\
- M L+1\n\
- %(A=B?T:E) if A equals B then T else E"),
- N_(" LFMT (only) may contain:\n\
- %L contents of line\n\
- %l contents of line, excluding any trailing newline\n\
- %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"),
- N_(" Both GFMT and LFMT may contain:\n\
- %% %\n\
- %c'C' the single character C\n\
- %c'\\OOO' the character with octal code OOO\n\
- C the character C (other characters represent themselves)"),
- "",
- N_("-d, --minimal try hard to find a smaller set of changes"),
- N_(" --horizon-lines=NUM keep NUM lines of the common prefix and suffix"),
- N_(" --speed-large-files assume large files and many scattered small changes"),
- N_(" --color[=WHEN] colorize the output; WHEN can be 'never', 'always',\n"
- " or 'auto' (the default)"),
- N_(" --palette=PALETTE the colors to use when --color is active; PALETTE is\n"
- " a colon-separated list of terminfo capabilities"),
- "",
- N_(" --help display this help and exit"),
- N_("-v, --version output version information and exit"),
- "",
- N_("FILES are 'FILE1 FILE2' or 'DIR1 DIR2' or 'DIR FILE' or 'FILE DIR'."),
- N_("If --from-file or --to-file is given, there are no restrictions on FILE(s)."),
- N_("If a FILE is '-', read standard input."),
- N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
- 0
-};
-
-static void
-usage (void)
-{
- char const * const *p;
-
- printf (_("Usage: %s [OPTION]... FILES\n"), program_name);
- printf ("%s\n\n", _("Compare FILES line by line."));
-
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
-
- for (p = option_help_msgid; *p; p++)
- {
- if (!**p)
- putchar ('\n');
- else
- {
- char const *msg = _(*p);
- char const *nl;
- while ((nl = strchr (msg, '\n')))
- {
- int msglen = nl + 1 - msg;
- printf (" %.*s", msglen, msg);
- msg = nl + 1;
- }
-
- printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg);
- }
- }
- emit_bug_reporting_address ();
-}
-
-/* Set VAR to VALUE, reporting an OPTION error if this is a
- conflict. */
-static void
-specify_value (char const **var, char const *value, char const *option)
-{
- if (*var && ! STREQ (*var, value))
- {
- error (0, 0, _("conflicting %s option value '%s'"), option, value);
- try_help (NULL, NULL);
- }
- *var = value;
-}
-
-/* Set the output style to STYLE, diagnosing conflicts. */
-static void
-specify_style (enum output_style style)
-{
- if (output_style != style)
- {
- if (output_style != OUTPUT_UNSPECIFIED)
- try_help ("conflicting output style options", NULL);
- output_style = style;
- }
-}
-
-/* Set the color mode. */
-static void
-specify_colors_style (char const *value)
-{
- if (value == NULL || STREQ (value, "auto"))
- colors_style = AUTO;
- else if (STREQ (value, "always"))
- colors_style = ALWAYS;
- else if (STREQ (value, "never"))
- colors_style = NEVER;
- else
- try_help ("invalid color '%s'", value);
-}
-
-
-/* Set the last-modified time of *ST to be the current time. */
-
-static void
-set_mtime_to_now (struct stat *st)
-{
-#ifdef STAT_TIMESPEC
- gettime (&STAT_TIMESPEC (st, st_mtim));
-#else
- struct timespec t;
- gettime (&t);
- st->st_mtime = t.tv_sec;
-# if defined STAT_TIMESPEC_NS
- STAT_TIMESPEC_NS (st, st_mtim) = t.tv_nsec;
-# elif defined HAVE_STRUCT_STAT_ST_SPARE1
- st->st_spare1 = t.tv_nsec / 1000;
-# endif
-#endif
-}
-
-/* Compare two files (or dirs) with parent comparison PARENT
- and names NAME0 and NAME1.
- (If PARENT is null, then the first name is just NAME0, etc.)
- This is self-contained; it opens the files and closes them.
-
- Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if
- different, EXIT_TROUBLE if there is a problem opening them. */
-
-static int
-compare_files (struct comparison const *parent,
- char const *name0,
- char const *name1)
-{
- struct comparison cmp;
-#define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0)
- register int f;
- int status = EXIT_SUCCESS;
- bool same_files;
- char *free0;
- char *free1;
-
- /* If this is directory comparison, perhaps we have a file
- that exists only in one of the directories.
- If so, just print a message to that effect. */
-
- if (! ((name0 && name1)
- || (unidirectional_new_file && name1)
- || new_file))
- {
- char const *name = name0 ? name0 : name1;
- char const *dir = parent->file[!name0].name;
-
- /* See POSIX 1003.1-2001 for this format. */
- message ("Only in %s: %s\n", dir, name);
-
- /* Return EXIT_FAILURE so that diff_dirs will return
- EXIT_FAILURE ("some files differ"). */
- return EXIT_FAILURE;
- }
-
- memset (cmp.file, 0, sizeof cmp.file);
- cmp.parent = parent;
-
- /* cmp.file[f].desc markers */
-#define NONEXISTENT (-1) /* nonexistent file */
-#define UNOPENED (-2) /* unopened file (e.g. directory) */
-#define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */
-
-#define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */
-
- cmp.file[0].desc = name0 ? UNOPENED : NONEXISTENT;
- cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT;
-
- /* Now record the full name of each file, including nonexistent ones. */
-
- if (!name0)
- name0 = name1;
- if (!name1)
- name1 = name0;
-
- if (!parent)
- {
- free0 = NULL;
- free1 = NULL;
- cmp.file[0].name = name0;
- cmp.file[1].name = name1;
- }
- else
- {
- cmp.file[0].name = free0
- = file_name_concat (parent->file[0].name, name0, NULL);
- cmp.file[1].name = free1
- = file_name_concat (parent->file[1].name, name1, NULL);
- }
-
- /* Stat the files. */
-
- for (f = 0; f < 2; f++)
- {
- if (cmp.file[f].desc != NONEXISTENT)
- {
- if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0)
- {
- cmp.file[f].desc = cmp.file[0].desc;
- cmp.file[f].stat = cmp.file[0].stat;
- }
- else if (STREQ (cmp.file[f].name, "-"))
- {
- cmp.file[f].desc = STDIN_FILENO;
- if (binary && ! isatty (STDIN_FILENO))
- set_binary_mode (STDIN_FILENO, O_BINARY);
- if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
- cmp.file[f].desc = ERRNO_ENCODE (errno);
- else
- {
- if (S_ISREG (cmp.file[f].stat.st_mode))
- {
- off_t pos = lseek (STDIN_FILENO, 0, SEEK_CUR);
- if (pos < 0)
- cmp.file[f].desc = ERRNO_ENCODE (errno);
- else
- cmp.file[f].stat.st_size =
- MAX (0, cmp.file[f].stat.st_size - pos);
- }
-
- /* POSIX 1003.1-2001 requires current time for
- stdin. */
- set_mtime_to_now (&cmp.file[f].stat);
- }
- }
- else if ((no_dereference_symlinks
- ? lstat (cmp.file[f].name, &cmp.file[f].stat)
- : stat (cmp.file[f].name, &cmp.file[f].stat))
- != 0)
- cmp.file[f].desc = ERRNO_ENCODE (errno);
- }
- }
-
- /* Mark files as nonexistent as needed for -N and -P, if they are
- inaccessible empty regular files (the kind of files that 'patch'
- creates to indicate nonexistent backups), or if they are
- top-level files that do not exist but their counterparts do
- exist. */
- for (f = 0; f < 2; f++)
- if ((new_file || (f == 0 && unidirectional_new_file))
- && (cmp.file[f].desc == UNOPENED
- ? (S_ISREG (cmp.file[f].stat.st_mode)
- && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))
- && cmp.file[f].stat.st_size == 0)
- : ((cmp.file[f].desc == ERRNO_ENCODE (ENOENT)
- || cmp.file[f].desc == ERRNO_ENCODE (EBADF))
- && ! parent
- && (cmp.file[1 - f].desc == UNOPENED
- || cmp.file[1 - f].desc == STDIN_FILENO))))
- cmp.file[f].desc = NONEXISTENT;
-
- for (f = 0; f < 2; f++)
- if (cmp.file[f].desc == NONEXISTENT)
- {
- memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat);
- cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode;
- }
-
- for (f = 0; f < 2; f++)
- {
- int e = ERRNO_DECODE (cmp.file[f].desc);
- if (0 <= e)
- {
- errno = e;
- perror_with_name (cmp.file[f].name);
- status = EXIT_TROUBLE;
- }
- }
-
- if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1))
- {
- /* If one is a directory, and it was specified in the command line,
- use the file in that dir with the other file's basename. */
-
- int fnm_arg = DIR_P (0);
- int dir_arg = 1 - fnm_arg;
- char const *fnm = cmp.file[fnm_arg].name;
- char const *dir = cmp.file[dir_arg].name;
- char const *filename = cmp.file[dir_arg].name = free0
- = find_dir_file_pathname (dir, last_component (fnm));
-
- if (STREQ (fnm, "-"))
- fatal ("cannot compare '-' to a directory");
-
- if ((no_dereference_symlinks
- ? lstat (filename, &cmp.file[dir_arg].stat)
- : stat (filename, &cmp.file[dir_arg].stat))
- != 0)
- {
- perror_with_name (filename);
- status = EXIT_TROUBLE;
- }
- }
-
- if (status != EXIT_SUCCESS)
- {
- /* One of the files should exist but does not. */
- }
- else if (cmp.file[0].desc == NONEXISTENT
- && cmp.file[1].desc == NONEXISTENT)
- {
- /* Neither file "exists", so there's nothing to compare. */
- }
- else if ((same_files
- = (cmp.file[0].desc != NONEXISTENT
- && cmp.file[1].desc != NONEXISTENT
- && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat)
- && same_file_attributes (&cmp.file[0].stat,
- &cmp.file[1].stat)))
- && no_diff_means_no_output)
- {
- /* The two named files are actually the same physical file.
- We know they are identical without actually reading them. */
- }
- else if (DIR_P (0) & DIR_P (1))
- {
- if (output_style == OUTPUT_IFDEF)
- fatal ("-D option not supported with directories");
-
- /* If both are directories, compare the files in them. */
-
- if (parent && !recursive)
- {
- /* But don't compare dir contents one level down
- unless -r was specified.
- See POSIX 1003.1-2001 for this format. */
- message ("Common subdirectories: %s and %s\n",
- cmp.file[0].name, cmp.file[1].name);
- }
- else
- status = diff_dirs (&cmp, compare_files);
- }
- else if ((DIR_P (0) | DIR_P (1))
- || (parent
- && !((S_ISREG (cmp.file[0].stat.st_mode)
- || S_ISLNK (cmp.file[0].stat.st_mode))
- && (S_ISREG (cmp.file[1].stat.st_mode)
- || S_ISLNK (cmp.file[1].stat.st_mode)))))
- {
- if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT)
- {
- /* We have a subdirectory that exists only in one directory. */
-
- if ((DIR_P (0) | DIR_P (1))
- && recursive
- && (new_file
- || (unidirectional_new_file
- && cmp.file[0].desc == NONEXISTENT)))
- status = diff_dirs (&cmp, compare_files);
- else
- {
- char const *dir;
-
- /* PARENT must be non-NULL here. */
- assert (parent);
- dir = parent->file[cmp.file[0].desc == NONEXISTENT].name;
-
- /* See POSIX 1003.1-2001 for this format. */
- message ("Only in %s: %s\n", dir, name0);
-
- status = EXIT_FAILURE;
- }
- }
- else
- {
- /* We have two files that are not to be compared. */
-
- /* See POSIX 1003.1-2001 for this format. */
- message5 ("File %s is a %s while file %s is a %s\n",
- file_label[0] ? file_label[0] : cmp.file[0].name,
- file_type (&cmp.file[0].stat),
- file_label[1] ? file_label[1] : cmp.file[1].name,
- file_type (&cmp.file[1].stat));
-
- /* This is a difference. */
- status = EXIT_FAILURE;
- }
- }
- else if (S_ISLNK (cmp.file[0].stat.st_mode)
- || S_ISLNK (cmp.file[1].stat.st_mode))
- {
- /* We get here only if we use lstat(), not stat(). */
- assert (no_dereference_symlinks);
-
- if (S_ISLNK (cmp.file[0].stat.st_mode)
- && S_ISLNK (cmp.file[1].stat.st_mode))
- {
- /* Compare the values of the symbolic links. */
- char *link_value[2] = { NULL, NULL };
-
- for (f = 0; f < 2; f++)
- {
- link_value[f] = xreadlink (cmp.file[f].name);
- if (link_value[f] == NULL)
- {
- perror_with_name (cmp.file[f].name);
- status = EXIT_TROUBLE;
- break;
- }
- }
- if (status == EXIT_SUCCESS)
- {
- if ( ! STREQ (link_value[0], link_value[1]))
- {
- message ("Symbolic links %s and %s differ\n",
- cmp.file[0].name, cmp.file[1].name);
- /* This is a difference. */
- status = EXIT_FAILURE;
- }
- }
- for (f = 0; f < 2; f++)
- free (link_value[f]);
- }
- else
- {
- /* We have two files that are not to be compared, because
- one of them is a symbolic link and the other one is not. */
-
- message5 ("File %s is a %s while file %s is a %s\n",
- file_label[0] ? file_label[0] : cmp.file[0].name,
- file_type (&cmp.file[0].stat),
- file_label[1] ? file_label[1] : cmp.file[1].name,
- file_type (&cmp.file[1].stat));
-
- /* This is a difference. */
- status = EXIT_FAILURE;
- }
- }
- else if (files_can_be_treated_as_binary
- && S_ISREG (cmp.file[0].stat.st_mode)
- && S_ISREG (cmp.file[1].stat.st_mode)
- && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size
- && 0 < cmp.file[0].stat.st_size
- && 0 < cmp.file[1].stat.st_size)
- {
- message ("Files %s and %s differ\n",
- file_label[0] ? file_label[0] : cmp.file[0].name,
- file_label[1] ? file_label[1] : cmp.file[1].name);
- status = EXIT_FAILURE;
- }
- else
- {
- /* Both exist and neither is a directory. */
-
- /* Open the files and record their descriptors. */
-
- int oflags = O_RDONLY | (binary ? O_BINARY : 0);
-
- if (cmp.file[0].desc == UNOPENED)
- if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0)
- {
- perror_with_name (cmp.file[0].name);
- status = EXIT_TROUBLE;
- }
- if (cmp.file[1].desc == UNOPENED)
- {
- if (same_files)
- cmp.file[1].desc = cmp.file[0].desc;
- else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0)
- {
- perror_with_name (cmp.file[1].name);
- status = EXIT_TROUBLE;
- }
- }
-
- /* Compare the files, if no error was found. */
-
- if (status == EXIT_SUCCESS)
- status = diff_2_files (&cmp);
-
- /* Close the file descriptors. */
-
- if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0)
- {
- perror_with_name (cmp.file[0].name);
- status = EXIT_TROUBLE;
- }
- if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc
- && close (cmp.file[1].desc) != 0)
- {
- perror_with_name (cmp.file[1].name);
- status = EXIT_TROUBLE;
- }
- }
-
- /* Now the comparison has been done, if no error prevented it,
- and STATUS is the value this function will return. */
-
- if (status == EXIT_SUCCESS)
- {
- if (report_identical_files && !DIR_P (0))
- message ("Files %s and %s are identical\n",
- file_label[0] ? file_label[0] : cmp.file[0].name,
- file_label[1] ? file_label[1] : cmp.file[1].name);
- }
- else
- {
- /* Flush stdout so that the user sees differences immediately.
- This can hurt performance, unfortunately. */
- if (fflush (stdout) != 0)
- pfatal_with_name (_("standard output"));
- }
-
- free (free0);
- free (free1);
-
- return status;
-}
diff --git a/src/diff.h b/src/diff.h
index e5f1218..c8cf436 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -23,17 +23,6 @@
#include
#include
-/* For platforms which support the ISO C ammendment 1 functionality we
- support user-defined character classes. */
-#if defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H
-/* Solaris 2.5 has a bug: must be included before . */
-# include
-# include
-# if defined (HAVE_MBRTOWC)
-# define HANDLE_MULTIBYTE 1
-# endif
-#endif
-
/* What kind of changes a hunk contains. */
enum changes
{
@@ -392,11 +381,7 @@ extern void print_sdiff_script (struct change *);
extern char const change_letter[4];
extern char const pr_program[];
extern char *concat (char const *, char const *, char const *);
-extern bool (*lines_differ) (char const *, size_t, char const *, size_t);
-extern bool lines_differ_singlebyte (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
-#ifdef HANDLE_MULTIBYTE
-extern bool lines_differ_multibyte (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
-#endif
+extern bool lines_differ (char const *, char const *) _GL_ATTRIBUTE_PURE;
extern lin translate_line_number (struct file_data const *, lin);
extern struct change *find_change (struct change *);
extern struct change *find_reverse_change (struct change *);
diff --git a/src/diff.h.covscan b/src/diff.h.covscan
deleted file mode 100644
index a438a8e..0000000
--- a/src/diff.h.covscan
+++ /dev/null
@@ -1,438 +0,0 @@
-/* Shared definitions for GNU DIFF
-
- Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2009-2013,
- 2015-2017 Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "system.h"
-#include
-#include
-#include
-
-/* For platforms which support the ISO C ammendment 1 functionality we
- support user-defined character classes. */
-#if defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H
-/* Solaris 2.5 has a bug: must be included before . */
-# include
-# include
-# if defined (HAVE_MBRTOWC)
-# define HANDLE_MULTIBYTE 1
-# endif
-#endif
-
-/* What kind of changes a hunk contains. */
-enum changes
-{
- /* No changes: lines common to both files. */
- UNCHANGED,
-
- /* Deletes only: lines taken from just the first file. */
- OLD,
-
- /* Inserts only: lines taken from just the second file. */
- NEW,
-
- /* Both deletes and inserts: a hunk containing both old and new lines. */
- CHANGED
-};
-
-/* When colors should be used in the output. */
-enum colors_style
-{
- /* Never output colors. */
- NEVER,
-
- /* Output colors if the output is a terminal. */
- AUTO,
-
- /* Always output colors. */
- ALWAYS,
-};
-
-/* Variables for command line options */
-
-#ifndef GDIFF_MAIN
-# define XTERN extern
-#else
-# define XTERN
-#endif
-
-enum output_style
-{
- /* No output style specified. */
- OUTPUT_UNSPECIFIED,
-
- /* Default output style. */
- OUTPUT_NORMAL,
-
- /* Output the differences with lines of context before and after (-c). */
- OUTPUT_CONTEXT,
-
- /* Output the differences in a unified context diff format (-u). */
- OUTPUT_UNIFIED,
-
- /* Output the differences as commands suitable for 'ed' (-e). */
- OUTPUT_ED,
-
- /* Output the diff as a forward ed script (-f). */
- OUTPUT_FORWARD_ED,
-
- /* Like -f, but output a count of changed lines in each "command" (-n). */
- OUTPUT_RCS,
-
- /* Output merged #ifdef'd file (-D). */
- OUTPUT_IFDEF,
-
- /* Output sdiff style (-y). */
- OUTPUT_SDIFF
-};
-
-/* True for output styles that are robust,
- i.e. can handle a file that ends in a non-newline. */
-#define ROBUST_OUTPUT_STYLE(S) ((S) != OUTPUT_ED && (S) != OUTPUT_FORWARD_ED)
-
-XTERN enum output_style output_style;
-
-/* Define the current color context used to print a line. */
-XTERN enum colors_style colors_style;
-
-/* Nonzero if output cannot be generated for identical files. */
-XTERN bool no_diff_means_no_output;
-
-/* Number of lines of context to show in each set of diffs.
- This is zero when context is not to be shown. */
-XTERN lin context;
-
-/* Consider all files as text files (-a).
- Don't interpret codes over 0177 as implying a "binary file". */
-XTERN bool text;
-
-/* Number of lines to keep in identical prefix and suffix. */
-XTERN lin horizon_lines;
-
-/* The significance of white space during comparisons. */
-enum DIFF_white_space
-{
- /* All white space is significant (the default). */
- IGNORE_NO_WHITE_SPACE,
-
- /* Ignore changes due to tab expansion (-E). */
- IGNORE_TAB_EXPANSION,
-
- /* Ignore changes in trailing horizontal white space (-Z). */
- IGNORE_TRAILING_SPACE,
-
- /* IGNORE_TAB_EXPANSION and IGNORE_TRAILING_SPACE are a special case
- because they are independent and can be ORed together, yielding
- IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE. */
- IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE,
-
- /* Ignore changes in horizontal white space (-b). */
- IGNORE_SPACE_CHANGE,
-
- /* Ignore all horizontal white space (-w). */
- IGNORE_ALL_SPACE
-};
-XTERN enum DIFF_white_space ignore_white_space;
-
-/* Ignore changes that affect only blank lines (-B). */
-XTERN bool ignore_blank_lines;
-
-/* Files can be compared byte-by-byte, as if they were binary.
- This depends on various options. */
-XTERN bool files_can_be_treated_as_binary;
-
-/* Ignore differences in case of letters (-i). */
-XTERN bool ignore_case;
-
-/* Ignore differences in case of letters in file names. */
-XTERN bool ignore_file_name_case;
-
-/* Act on symbolic links themselves rather than on their target
- (--no-dereference). */
-XTERN bool no_dereference_symlinks;
-
-/* File labels for '-c' output headers (--label). */
-XTERN char *file_label[2];
-
-/* Regexp to identify function-header lines (-F). */
-XTERN struct re_pattern_buffer function_regexp;
-
-/* Ignore changes that affect only lines matching this regexp (-I). */
-XTERN struct re_pattern_buffer ignore_regexp;
-
-/* Say only whether files differ, not how (-q). */
-XTERN bool brief;
-
-/* Expand tabs in the output so the text lines up properly
- despite the characters added to the front of each line (-t). */
-XTERN bool expand_tabs;
-
-/* Number of columns between tab stops. */
-XTERN size_t tabsize;
-
-/* Use a tab in the output, rather than a space, before the text of an
- input line, so as to keep the proper alignment in the input line
- without changing the characters in it (-T). */
-XTERN bool initial_tab;
-
-/* Do not output an initial space or tab before the text of an empty line. */
-XTERN bool suppress_blank_empty;
-
-/* Remove trailing carriage returns from input. */
-XTERN bool strip_trailing_cr;
-
-/* In directory comparison, specify file to start with (-S).
- This is used for resuming an aborted comparison.
- All file names less than this name are ignored. */
-XTERN char const *starting_file;
-
-/* Pipe each file's output through pr (-l). */
-XTERN bool paginate;
-
-/* Line group formats for unchanged, old, new, and changed groups. */
-XTERN char const *group_format[CHANGED + 1];
-
-/* Line formats for unchanged, old, and new lines. */
-XTERN char const *line_format[NEW + 1];
-
-/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */
-XTERN bool sdiff_merge_assist;
-
-/* Tell OUTPUT_SDIFF to show only the left version of common lines. */
-XTERN bool left_column;
-
-/* Tell OUTPUT_SDIFF to not show common lines. */
-XTERN bool suppress_common_lines;
-
-/* The half line width and column 2 offset for OUTPUT_SDIFF. */
-XTERN size_t sdiff_half_width;
-XTERN size_t sdiff_column2_offset;
-
-/* String containing all the command options diff received,
- with spaces between and at the beginning but none at the end.
- If there were no options given, this string is empty. */
-XTERN char *switch_string;
-
-/* Use heuristics for better speed with large files with a small
- density of changes. */
-XTERN bool speed_large_files;
-
-/* Patterns that match file names to be excluded. */
-XTERN struct exclude *excluded;
-
-/* Don't discard lines. This makes things slower (sometimes much
- slower) but will find a guaranteed minimal set of changes. */
-XTERN bool minimal;
-
-/* The strftime format to use for time strings. */
-XTERN char const *time_format;
-
-/* The result of comparison is an "edit script": a chain of 'struct change'.
- Each 'struct change' represents one place where some lines are deleted
- and some are inserted.
-
- LINE0 and LINE1 are the first affected lines in the two files (origin 0).
- DELETED is the number of lines deleted here from file 0.
- INSERTED is the number of lines inserted here in file 1.
-
- If DELETED is 0 then LINE0 is the number of the line before
- which the insertion was done; vice versa for INSERTED and LINE1. */
-
-struct change
-{
- struct change *link; /* Previous or next edit command */
- lin inserted; /* # lines of file 1 changed here. */
- lin deleted; /* # lines of file 0 changed here. */
- lin line0; /* Line number of 1st deleted line. */
- lin line1; /* Line number of 1st inserted line. */
- bool ignore; /* Flag used in context.c. */
-};
-
-/* Structures that describe the input files. */
-
-/* Data on one input file being compared. */
-
-struct file_data {
- int desc; /* File descriptor */
- char const *name; /* File name */
- struct stat stat; /* File status */
-
- /* Buffer in which text of file is read. */
- word *buffer;
-
- /* Allocated size of buffer, in bytes. Always a multiple of
- sizeof *buffer. */
- size_t bufsize;
-
- /* Number of valid bytes now in the buffer. */
- size_t buffered;
-
- /* Array of pointers to lines in the file. */
- char const **linbuf;
-
- /* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
- linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
- linebuf[linbuf_base ... valid_lines - 1] contain valid data.
- linebuf[linbuf_base ... alloc_lines - 1] are allocated. */
- lin linbuf_base, buffered_lines, valid_lines, alloc_lines;
-
- /* Pointer to end of prefix of this file to ignore when hashing. */
- char const *prefix_end;
-
- /* Count of lines in the prefix.
- There are this many lines in the file before linbuf[0]. */
- lin prefix_lines;
-
- /* Pointer to start of suffix of this file to ignore when hashing. */
- char const *suffix_begin;
-
- /* Vector, indexed by line number, containing an equivalence code for
- each line. It is this vector that is actually compared with that
- of another file to generate differences. */
- lin *equivs;
-
- /* Vector, like the previous one except that
- the elements for discarded lines have been squeezed out. */
- lin *undiscarded;
-
- /* Vector mapping virtual line numbers (not counting discarded lines)
- to real ones (counting those lines). Both are origin-0. */
- lin *realindexes;
-
- /* Total number of nondiscarded lines. */
- lin nondiscarded_lines;
-
- /* Vector, indexed by real origin-0 line number,
- containing 1 for a line that is an insertion or a deletion.
- The results of comparison are stored here. */
- char *changed;
-
- /* 1 if file ends in a line with no final newline. */
- bool missing_newline;
-
- /* 1 if at end of file. */
- bool eof;
-
- /* 1 more than the maximum equivalence value used for this or its
- sibling file. */
- lin equiv_max;
-};
-
-/* The file buffer, considered as an array of bytes rather than
- as an array of words. */
-#define FILE_BUFFER(f) ((char *) (f)->buffer)
-
-/* Data on two input files being compared. */
-
-struct comparison
- {
- struct file_data file[2];
- struct comparison const *parent; /* parent, if a recursive comparison */
- };
-
-/* Describe the two files currently being compared. */
-
-XTERN struct file_data files[2];
-
-/* Stdio stream to output diffs to. */
-
-XTERN FILE *outfile;
-
-/* Declare various functions. */
-
-/* analyze.c */
-extern int diff_2_files (struct comparison *);
-
-/* context.c */
-extern void print_context_header (struct file_data[], char const * const *, bool);
-extern void print_context_script (struct change *, bool);
-
-/* dir.c */
-extern int diff_dirs (struct comparison const *,
- int (*) (struct comparison const *,
- char const *, char const *));
-extern char *find_dir_file_pathname (char const *, char const *);
-
-/* ed.c */
-extern void print_ed_script (struct change *);
-extern void pr_forward_ed_script (struct change *);
-
-/* ifdef.c */
-extern void print_ifdef_script (struct change *);
-
-/* io.c */
-extern void file_block_read (struct file_data *, size_t);
-extern bool read_files (struct file_data[], bool);
-
-/* normal.c */
-extern void print_normal_script (struct change *);
-
-/* rcs.c */
-extern void print_rcs_script (struct change *);
-
-/* side.c */
-extern void print_sdiff_script (struct change *);
-
-/* util.c */
-extern char const change_letter[4];
-extern char const pr_program[];
-extern char *concat (char const *, char const *, char const *);
-extern bool (*lines_differ) (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
-extern bool lines_differ_singlebyte (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
-#ifdef HANDLE_MULTIBYTE
-extern bool lines_differ_multibyte (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
-#endif
-extern lin translate_line_number (struct file_data const *, lin);
-extern struct change *find_change (struct change *);
-extern struct change *find_reverse_change (struct change *);
-extern void *zalloc (size_t);
-extern enum changes analyze_hunk (struct change *, lin *, lin *, lin *, lin *);
-extern void begin_output (void);
-extern void debug_script (struct change *);
-extern void fatal (char const *) __attribute__((noreturn));
-extern void finish_output (void);
-extern void message (char const *, char const *, char const *);
-extern void message5 (char const *, char const *, char const *,
- char const *, char const *);
-extern void output_1_line (char const *, char const *, char const *,
- char const *);
-extern void perror_with_name (char const *);
-extern void pfatal_with_name (char const *) __attribute__((noreturn));
-extern void print_1_line (char const *, char const * const *);
-extern void print_1_line_nl (char const *, char const * const *, bool);
-extern void print_message_queue (void);
-extern void print_number_range (char, struct file_data *, lin, lin);
-extern void print_script (struct change *, struct change * (*) (struct change *),
- void (*) (struct change *));
-extern void setup_output (char const *, char const *, bool);
-extern void translate_range (struct file_data const *, lin, lin,
- printint *, printint *);
-
-enum color_context
-{
- HEADER_CONTEXT,
- ADD_CONTEXT,
- DELETE_CONTEXT,
- RESET_CONTEXT,
- LINE_NUMBER_CONTEXT,
-};
-
-XTERN bool presume_output_tty;
-
-extern void set_color_context (enum color_context color_context);
-extern void set_color_palette (char const *palette);
diff --git a/src/diff.h.i18n b/src/diff.h.i18n
deleted file mode 100644
index c8cf436..0000000
--- a/src/diff.h.i18n
+++ /dev/null
@@ -1,423 +0,0 @@
-/* Shared definitions for GNU DIFF
-
- Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2009-2013,
- 2015-2017 Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "system.h"
-#include
-#include
-#include
-
-/* What kind of changes a hunk contains. */
-enum changes
-{
- /* No changes: lines common to both files. */
- UNCHANGED,
-
- /* Deletes only: lines taken from just the first file. */
- OLD,
-
- /* Inserts only: lines taken from just the second file. */
- NEW,
-
- /* Both deletes and inserts: a hunk containing both old and new lines. */
- CHANGED
-};
-
-/* When colors should be used in the output. */
-enum colors_style
-{
- /* Never output colors. */
- NEVER,
-
- /* Output colors if the output is a terminal. */
- AUTO,
-
- /* Always output colors. */
- ALWAYS,
-};
-
-/* Variables for command line options */
-
-#ifndef GDIFF_MAIN
-# define XTERN extern
-#else
-# define XTERN
-#endif
-
-enum output_style
-{
- /* No output style specified. */
- OUTPUT_UNSPECIFIED,
-
- /* Default output style. */
- OUTPUT_NORMAL,
-
- /* Output the differences with lines of context before and after (-c). */
- OUTPUT_CONTEXT,
-
- /* Output the differences in a unified context diff format (-u). */
- OUTPUT_UNIFIED,
-
- /* Output the differences as commands suitable for 'ed' (-e). */
- OUTPUT_ED,
-
- /* Output the diff as a forward ed script (-f). */
- OUTPUT_FORWARD_ED,
-
- /* Like -f, but output a count of changed lines in each "command" (-n). */
- OUTPUT_RCS,
-
- /* Output merged #ifdef'd file (-D). */
- OUTPUT_IFDEF,
-
- /* Output sdiff style (-y). */
- OUTPUT_SDIFF
-};
-
-/* True for output styles that are robust,
- i.e. can handle a file that ends in a non-newline. */
-#define ROBUST_OUTPUT_STYLE(S) ((S) != OUTPUT_ED && (S) != OUTPUT_FORWARD_ED)
-
-XTERN enum output_style output_style;
-
-/* Define the current color context used to print a line. */
-XTERN enum colors_style colors_style;
-
-/* Nonzero if output cannot be generated for identical files. */
-XTERN bool no_diff_means_no_output;
-
-/* Number of lines of context to show in each set of diffs.
- This is zero when context is not to be shown. */
-XTERN lin context;
-
-/* Consider all files as text files (-a).
- Don't interpret codes over 0177 as implying a "binary file". */
-XTERN bool text;
-
-/* Number of lines to keep in identical prefix and suffix. */
-XTERN lin horizon_lines;
-
-/* The significance of white space during comparisons. */
-enum DIFF_white_space
-{
- /* All white space is significant (the default). */
- IGNORE_NO_WHITE_SPACE,
-
- /* Ignore changes due to tab expansion (-E). */
- IGNORE_TAB_EXPANSION,
-
- /* Ignore changes in trailing horizontal white space (-Z). */
- IGNORE_TRAILING_SPACE,
-
- /* IGNORE_TAB_EXPANSION and IGNORE_TRAILING_SPACE are a special case
- because they are independent and can be ORed together, yielding
- IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE. */
- IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE,
-
- /* Ignore changes in horizontal white space (-b). */
- IGNORE_SPACE_CHANGE,
-
- /* Ignore all horizontal white space (-w). */
- IGNORE_ALL_SPACE
-};
-XTERN enum DIFF_white_space ignore_white_space;
-
-/* Ignore changes that affect only blank lines (-B). */
-XTERN bool ignore_blank_lines;
-
-/* Files can be compared byte-by-byte, as if they were binary.
- This depends on various options. */
-XTERN bool files_can_be_treated_as_binary;
-
-/* Ignore differences in case of letters (-i). */
-XTERN bool ignore_case;
-
-/* Ignore differences in case of letters in file names. */
-XTERN bool ignore_file_name_case;
-
-/* Act on symbolic links themselves rather than on their target
- (--no-dereference). */
-XTERN bool no_dereference_symlinks;
-
-/* File labels for '-c' output headers (--label). */
-XTERN char *file_label[2];
-
-/* Regexp to identify function-header lines (-F). */
-XTERN struct re_pattern_buffer function_regexp;
-
-/* Ignore changes that affect only lines matching this regexp (-I). */
-XTERN struct re_pattern_buffer ignore_regexp;
-
-/* Say only whether files differ, not how (-q). */
-XTERN bool brief;
-
-/* Expand tabs in the output so the text lines up properly
- despite the characters added to the front of each line (-t). */
-XTERN bool expand_tabs;
-
-/* Number of columns between tab stops. */
-XTERN size_t tabsize;
-
-/* Use a tab in the output, rather than a space, before the text of an
- input line, so as to keep the proper alignment in the input line
- without changing the characters in it (-T). */
-XTERN bool initial_tab;
-
-/* Do not output an initial space or tab before the text of an empty line. */
-XTERN bool suppress_blank_empty;
-
-/* Remove trailing carriage returns from input. */
-XTERN bool strip_trailing_cr;
-
-/* In directory comparison, specify file to start with (-S).
- This is used for resuming an aborted comparison.
- All file names less than this name are ignored. */
-XTERN char const *starting_file;
-
-/* Pipe each file's output through pr (-l). */
-XTERN bool paginate;
-
-/* Line group formats for unchanged, old, new, and changed groups. */
-XTERN char const *group_format[CHANGED + 1];
-
-/* Line formats for unchanged, old, and new lines. */
-XTERN char const *line_format[NEW + 1];
-
-/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */
-XTERN bool sdiff_merge_assist;
-
-/* Tell OUTPUT_SDIFF to show only the left version of common lines. */
-XTERN bool left_column;
-
-/* Tell OUTPUT_SDIFF to not show common lines. */
-XTERN bool suppress_common_lines;
-
-/* The half line width and column 2 offset for OUTPUT_SDIFF. */
-XTERN size_t sdiff_half_width;
-XTERN size_t sdiff_column2_offset;
-
-/* String containing all the command options diff received,
- with spaces between and at the beginning but none at the end.
- If there were no options given, this string is empty. */
-XTERN char *switch_string;
-
-/* Use heuristics for better speed with large files with a small
- density of changes. */
-XTERN bool speed_large_files;
-
-/* Patterns that match file names to be excluded. */
-XTERN struct exclude *excluded;
-
-/* Don't discard lines. This makes things slower (sometimes much
- slower) but will find a guaranteed minimal set of changes. */
-XTERN bool minimal;
-
-/* The strftime format to use for time strings. */
-XTERN char const *time_format;
-
-/* The result of comparison is an "edit script": a chain of 'struct change'.
- Each 'struct change' represents one place where some lines are deleted
- and some are inserted.
-
- LINE0 and LINE1 are the first affected lines in the two files (origin 0).
- DELETED is the number of lines deleted here from file 0.
- INSERTED is the number of lines inserted here in file 1.
-
- If DELETED is 0 then LINE0 is the number of the line before
- which the insertion was done; vice versa for INSERTED and LINE1. */
-
-struct change
-{
- struct change *link; /* Previous or next edit command */
- lin inserted; /* # lines of file 1 changed here. */
- lin deleted; /* # lines of file 0 changed here. */
- lin line0; /* Line number of 1st deleted line. */
- lin line1; /* Line number of 1st inserted line. */
- bool ignore; /* Flag used in context.c. */
-};
-
-/* Structures that describe the input files. */
-
-/* Data on one input file being compared. */
-
-struct file_data {
- int desc; /* File descriptor */
- char const *name; /* File name */
- struct stat stat; /* File status */
-
- /* Buffer in which text of file is read. */
- word *buffer;
-
- /* Allocated size of buffer, in bytes. Always a multiple of
- sizeof *buffer. */
- size_t bufsize;
-
- /* Number of valid bytes now in the buffer. */
- size_t buffered;
-
- /* Array of pointers to lines in the file. */
- char const **linbuf;
-
- /* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
- linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
- linebuf[linbuf_base ... valid_lines - 1] contain valid data.
- linebuf[linbuf_base ... alloc_lines - 1] are allocated. */
- lin linbuf_base, buffered_lines, valid_lines, alloc_lines;
-
- /* Pointer to end of prefix of this file to ignore when hashing. */
- char const *prefix_end;
-
- /* Count of lines in the prefix.
- There are this many lines in the file before linbuf[0]. */
- lin prefix_lines;
-
- /* Pointer to start of suffix of this file to ignore when hashing. */
- char const *suffix_begin;
-
- /* Vector, indexed by line number, containing an equivalence code for
- each line. It is this vector that is actually compared with that
- of another file to generate differences. */
- lin *equivs;
-
- /* Vector, like the previous one except that
- the elements for discarded lines have been squeezed out. */
- lin *undiscarded;
-
- /* Vector mapping virtual line numbers (not counting discarded lines)
- to real ones (counting those lines). Both are origin-0. */
- lin *realindexes;
-
- /* Total number of nondiscarded lines. */
- lin nondiscarded_lines;
-
- /* Vector, indexed by real origin-0 line number,
- containing 1 for a line that is an insertion or a deletion.
- The results of comparison are stored here. */
- char *changed;
-
- /* 1 if file ends in a line with no final newline. */
- bool missing_newline;
-
- /* 1 if at end of file. */
- bool eof;
-
- /* 1 more than the maximum equivalence value used for this or its
- sibling file. */
- lin equiv_max;
-};
-
-/* The file buffer, considered as an array of bytes rather than
- as an array of words. */
-#define FILE_BUFFER(f) ((char *) (f)->buffer)
-
-/* Data on two input files being compared. */
-
-struct comparison
- {
- struct file_data file[2];
- struct comparison const *parent; /* parent, if a recursive comparison */
- };
-
-/* Describe the two files currently being compared. */
-
-XTERN struct file_data files[2];
-
-/* Stdio stream to output diffs to. */
-
-XTERN FILE *outfile;
-
-/* Declare various functions. */
-
-/* analyze.c */
-extern int diff_2_files (struct comparison *);
-
-/* context.c */
-extern void print_context_header (struct file_data[], char const * const *, bool);
-extern void print_context_script (struct change *, bool);
-
-/* dir.c */
-extern int diff_dirs (struct comparison const *,
- int (*) (struct comparison const *,
- char const *, char const *));
-extern char *find_dir_file_pathname (char const *, char const *);
-
-/* ed.c */
-extern void print_ed_script (struct change *);
-extern void pr_forward_ed_script (struct change *);
-
-/* ifdef.c */
-extern void print_ifdef_script (struct change *);
-
-/* io.c */
-extern void file_block_read (struct file_data *, size_t);
-extern bool read_files (struct file_data[], bool);
-
-/* normal.c */
-extern void print_normal_script (struct change *);
-
-/* rcs.c */
-extern void print_rcs_script (struct change *);
-
-/* side.c */
-extern void print_sdiff_script (struct change *);
-
-/* util.c */
-extern char const change_letter[4];
-extern char const pr_program[];
-extern char *concat (char const *, char const *, char const *);
-extern bool lines_differ (char const *, char const *) _GL_ATTRIBUTE_PURE;
-extern lin translate_line_number (struct file_data const *, lin);
-extern struct change *find_change (struct change *);
-extern struct change *find_reverse_change (struct change *);
-extern void *zalloc (size_t);
-extern enum changes analyze_hunk (struct change *, lin *, lin *, lin *, lin *);
-extern void begin_output (void);
-extern void debug_script (struct change *);
-extern void fatal (char const *) __attribute__((noreturn));
-extern void finish_output (void);
-extern void message (char const *, char const *, char const *);
-extern void message5 (char const *, char const *, char const *,
- char const *, char const *);
-extern void output_1_line (char const *, char const *, char const *,
- char const *);
-extern void perror_with_name (char const *);
-extern void pfatal_with_name (char const *) __attribute__((noreturn));
-extern void print_1_line (char const *, char const * const *);
-extern void print_1_line_nl (char const *, char const * const *, bool);
-extern void print_message_queue (void);
-extern void print_number_range (char, struct file_data *, lin, lin);
-extern void print_script (struct change *, struct change * (*) (struct change *),
- void (*) (struct change *));
-extern void setup_output (char const *, char const *, bool);
-extern void translate_range (struct file_data const *, lin, lin,
- printint *, printint *);
-
-enum color_context
-{
- HEADER_CONTEXT,
- ADD_CONTEXT,
- DELETE_CONTEXT,
- RESET_CONTEXT,
- LINE_NUMBER_CONTEXT,
-};
-
-XTERN bool presume_output_tty;
-
-extern void set_color_context (enum color_context color_context);
-extern void set_color_palette (char const *palette);
diff --git a/src/ifdef.c b/src/ifdef.c
index 648b378..c7dae8d 100644
--- a/src/ifdef.c
+++ b/src/ifdef.c
@@ -362,14 +362,20 @@ do_printf_spec (FILE *out, char const *spec,
printint print_value = value;
size_t spec_prefix_len = f - spec - 2;
size_t pI_len = sizeof pI - 1;
+#if 0
+ char format[spec_prefix_len + pI_len + 2];
+#else
char *format = xmalloc (spec_prefix_len + pI_len + 2);
+#endif
char *p = format + spec_prefix_len + pI_len;
memcpy (format, spec, spec_prefix_len);
memcpy (format + spec_prefix_len, pI, pI_len);
*p++ = c;
*p = '\0';
fprintf (out, format, print_value);
+#if ! HAVE_C_VARARRAYS
free (format);
+#endif
}
}
break;
diff --git a/src/ifdef.c.covscan b/src/ifdef.c.covscan
deleted file mode 100644
index c7dae8d..0000000
--- a/src/ifdef.c.covscan
+++ /dev/null
@@ -1,431 +0,0 @@
-/* #ifdef-format output routines for GNU DIFF.
-
- Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2017
- Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- GNU DIFF is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY. No author or distributor
- accepts responsibility to anyone for the consequences of using it
- or for whether it serves any particular purpose or works at all,
- unless he says so in writing. Refer to the GNU General Public
- License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- GNU DIFF, but only under the conditions described in the
- GNU General Public License. A copy of this license is
- supposed to have been given to you along with GNU DIFF so you
- can know your rights and responsibilities. It should be in a
- file named COPYING. Among other things, the copyright notice
- and this notice must be preserved on all copies. */
-
-#include "diff.h"
-
-#include
-
-struct group
-{
- struct file_data const *file;
- lin from, upto; /* start and limit lines for this group of lines */
-};
-
-static char const *format_group (FILE *, char const *, char,
- struct group const *);
-static char const *do_printf_spec (FILE *, char const *,
- struct file_data const *, lin,
- struct group const *);
-static char const *scan_char_literal (char const *, char *);
-static lin groups_letter_value (struct group const *, char);
-static void format_ifdef (char const *, lin, lin, lin, lin);
-static void print_ifdef_hunk (struct change *);
-static void print_ifdef_lines (FILE *, char const *, struct group const *);
-
-static lin next_line0;
-static lin next_line1;
-
-/* Print the edit-script SCRIPT as a merged #ifdef file. */
-
-void
-print_ifdef_script (struct change *script)
-{
- next_line0 = next_line1 = - files[0].prefix_lines;
- print_script (script, find_change, print_ifdef_hunk);
- if (next_line0 < files[0].valid_lines
- || next_line1 < files[1].valid_lines)
- {
- begin_output ();
- format_ifdef (group_format[UNCHANGED],
- next_line0, files[0].valid_lines,
- next_line1, files[1].valid_lines);
- }
-}
-
-/* Print a hunk of an ifdef diff.
- This is a contiguous portion of a complete edit script,
- describing changes in consecutive lines. */
-
-static void
-print_ifdef_hunk (struct change *hunk)
-{
- lin first0, last0, first1, last1;
-
- /* Determine range of line numbers involved in each file. */
- enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
- if (!changes)
- return;
-
- begin_output ();
-
- /* Print lines up to this change. */
- if (next_line0 < first0 || next_line1 < first1)
- format_ifdef (group_format[UNCHANGED],
- next_line0, first0,
- next_line1, first1);
-
- /* Print this change. */
- next_line0 = last0 + 1;
- next_line1 = last1 + 1;
- format_ifdef (group_format[changes],
- first0, next_line0,
- first1, next_line1);
-}
-
-/* Print a set of lines according to FORMAT.
- Lines BEG0 up to END0 are from the first file;
- lines BEG1 up to END1 are from the second file. */
-
-static void
-format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
-{
- struct group groups[2];
-
- groups[0].file = &files[0];
- groups[0].from = beg0;
- groups[0].upto = end0;
- groups[1].file = &files[1];
- groups[1].from = beg1;
- groups[1].upto = end1;
- format_group (outfile, format, 0, groups);
-}
-
-/* Print to file OUT a set of lines according to FORMAT.
- The format ends at the first free instance of ENDCHAR.
- Yield the address of the terminating character.
- GROUPS specifies which lines to print.
- If OUT is zero, do not actually print anything; just scan the format. */
-
-static char const *
-format_group (register FILE *out, char const *format, char endchar,
- struct group const *groups)
-{
- register char c;
- register char const *f = format;
-
- while ((c = *f) != endchar && c != 0)
- {
- char const *f1 = ++f;
- if (c == '%')
- switch ((c = *f++))
- {
- case '%':
- break;
-
- case '(':
- /* Print if-then-else format e.g. '%(n=1?thenpart:elsepart)'. */
- {
- int i;
- uintmax_t value[2];
- FILE *thenout, *elseout;
-
- for (i = 0; i < 2; i++)
- {
- if (ISDIGIT (*f))
- {
- char *fend;
- errno = 0;
- value[i] = strtoumax (f, &fend, 10);
- if (errno)
- goto bad_format;
- f = fend;
- }
- else
- {
- value[i] = groups_letter_value (groups, *f);
- if (value[i] == -1)
- goto bad_format;
- f++;
- }
- if (*f++ != "=?"[i])
- goto bad_format;
- }
- if (value[0] == value[1])
- thenout = out, elseout = 0;
- else
- thenout = 0, elseout = out;
- f = format_group (thenout, f, ':', groups);
- if (*f)
- {
- f = format_group (elseout, f + 1, ')', groups);
- if (*f)
- f++;
- }
- }
- continue;
-
- case '<':
- /* Print lines deleted from first file. */
- print_ifdef_lines (out, line_format[OLD], &groups[0]);
- continue;
-
- case '=':
- /* Print common lines. */
- print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
- continue;
-
- case '>':
- /* Print lines inserted from second file. */
- print_ifdef_lines (out, line_format[NEW], &groups[1]);
- continue;
-
- default:
- f = do_printf_spec (out, f - 2, 0, 0, groups);
- if (f)
- continue;
- /* Fall through. */
- bad_format:
- c = '%';
- f = f1;
- break;
- }
-
- if (out)
- putc (c, out);
- }
-
- return f;
-}
-
-/* For the line group pair G, return the number corresponding to LETTER.
- Return -1 if LETTER is not a group format letter. */
-static lin
-groups_letter_value (struct group const *g, char letter)
-{
- switch (letter)
- {
- case 'E': letter = 'e'; g++; break;
- case 'F': letter = 'f'; g++; break;
- case 'L': letter = 'l'; g++; break;
- case 'M': letter = 'm'; g++; break;
- case 'N': letter = 'n'; g++; break;
- }
-
- switch (letter)
- {
- case 'e': return translate_line_number (g->file, g->from) - 1;
- case 'f': return translate_line_number (g->file, g->from);
- case 'l': return translate_line_number (g->file, g->upto) - 1;
- case 'm': return translate_line_number (g->file, g->upto);
- case 'n': return g->upto - g->from;
- default: return -1;
- }
-}
-
-/* Print to file OUT, using FORMAT to print the line group GROUP.
- But do nothing if OUT is zero. */
-static void
-print_ifdef_lines (register FILE *out, char const *format,
- struct group const *group)
-{
- struct file_data const *file = group->file;
- char const * const *linbuf = file->linbuf;
- lin from = group->from, upto = group->upto;
-
- if (!out)
- return;
-
- /* If possible, use a single fwrite; it's faster. */
- if (!expand_tabs && format[0] == '%')
- {
- if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
- {
- fwrite (linbuf[from], sizeof (char),
- linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
- out);
- return;
- }
- if (format[1] == 'L' && !format[2])
- {
- fwrite (linbuf[from], sizeof (char),
- linbuf[upto] - linbuf[from], out);
- return;
- }
- }
-
- for (; from < upto; from++)
- {
- register char c;
- register char const *f = format;
-
- while ((c = *f++) != 0)
- {
- char const *f1 = f;
- if (c == '%')
- switch ((c = *f++))
- {
- case '%':
- break;
-
- case 'l':
- output_1_line (linbuf[from],
- (linbuf[from + 1]
- - (linbuf[from + 1][-1] == '\n')),
- 0, 0);
- continue;
-
- case 'L':
- output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
- continue;
-
- default:
- f = do_printf_spec (out, f - 2, file, from, 0);
- if (f)
- continue;
- c = '%';
- f = f1;
- break;
- }
-
- putc (c, out);
- }
- }
-}
-
-static char const *
-do_printf_spec (FILE *out, char const *spec,
- struct file_data const *file, lin n,
- struct group const *groups)
-{
- char const *f = spec;
- char c;
- char c1;
-
- /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
- /* assert (*f == '%'); */
- f++;
- while ((c = *f++) == '-' || c == '\'' || c == '0')
- continue;
- while (ISDIGIT (c))
- c = *f++;
- if (c == '.')
- while (ISDIGIT (c = *f++))
- continue;
- c1 = *f++;
-
- switch (c)
- {
- case 'c':
- if (c1 != '\'')
- return 0;
- else
- {
- char value IF_LINT (= 0);
- f = scan_char_literal (f, &value);
- if (!f)
- return 0;
- if (out)
- putc (value, out);
- }
- break;
-
- case 'd': case 'o': case 'x': case 'X':
- {
- lin value;
-
- if (file)
- {
- if (c1 != 'n')
- return 0;
- value = translate_line_number (file, n);
- }
- else
- {
- value = groups_letter_value (groups, c1);
- if (value < 0)
- return 0;
- }
-
- if (out)
- {
- /* For example, if the spec is "%3xn" and pI is "l", use the printf
- format spec "%3lx". Here the spec prefix is "%3". */
- printint print_value = value;
- size_t spec_prefix_len = f - spec - 2;
- size_t pI_len = sizeof pI - 1;
-#if 0
- char format[spec_prefix_len + pI_len + 2];
-#else
- char *format = xmalloc (spec_prefix_len + pI_len + 2);
-#endif
- char *p = format + spec_prefix_len + pI_len;
- memcpy (format, spec, spec_prefix_len);
- memcpy (format + spec_prefix_len, pI, pI_len);
- *p++ = c;
- *p = '\0';
- fprintf (out, format, print_value);
-#if ! HAVE_C_VARARRAYS
- free (format);
-#endif
- }
- }
- break;
-
- default:
- return 0;
- }
-
- return f;
-}
-
-/* Scan the character literal represented in the string LIT; LIT points just
- after the initial apostrophe. Put the literal's value into *VALPTR.
- Yield the address of the first character after the closing apostrophe,
- or a null pointer if the literal is ill-formed. */
-static char const *
-scan_char_literal (char const *lit, char *valptr)
-{
- register char const *p = lit;
- char value;
- ptrdiff_t digits;
- char c = *p++;
-
- switch (c)
- {
- case 0:
- case '\'':
- return NULL;
-
- case '\\':
- value = 0;
- while ((c = *p++) != '\'')
- {
- unsigned int digit = c - '0';
- if (8 <= digit)
- return NULL;
- value = 8 * value + digit;
- }
- digits = p - lit - 2;
- if (! (1 <= digits && digits <= 3))
- return NULL;
- break;
-
- default:
- value = c;
- if (*p++ != '\'')
- return NULL;
- break;
- }
-
- *valptr = value;
- return p;
-}
diff --git a/src/io.c b/src/io.c
index fce16ab..b4ef5dc 100644
--- a/src/io.c
+++ b/src/io.c
@@ -23,7 +23,6 @@
#include
#include
#include
-#include
/* Rotate an unsigned value to the left. */
#define ROL(v, n) ((v) << (n) | (v) >> (sizeof (v) * CHAR_BIT - (n)))
@@ -216,28 +215,6 @@ slurp (struct file_data *current)
/* Split the file into lines, simultaneously computing the equivalence
class for each line. */
-#ifdef HANDLE_MULTIBYTE
-# define MBC2WC(P, END, MBLENGTH, WC, STATE, CONVFAIL) \
-do \
- { \
- mbstate_t state_bak = STATE; \
- \
- CONVFAIL = 0; \
- MBLENGTH = mbrtowc (&WC, P, END - (char const *)P, &STATE); \
- \
- switch (MBLENGTH) \
- { \
- case (size_t)-2: \
- case (size_t)-1: \
- STATE = state_bak; \
- ++CONVFAIL; \
- /* Fall through. */ \
- case 0: \
- MBLENGTH = 1; \
- } \
- } \
- while (0)
-#endif
static void
find_and_hash_each_line (struct file_data *current)
@@ -264,300 +241,12 @@ find_and_hash_each_line (struct file_data *current)
bool same_length_diff_contents_compare_anyway =
diff_length_compare_anyway | ig_case;
-#ifdef HANDLE_MULTIBYTE
- wchar_t wc;
- size_t mblength;
- mbstate_t state;
- int convfail;
-
- memset (&state, '\0', sizeof (mbstate_t));
-#endif
-
while (p < suffix_begin)
{
char const *ip = p;
hash_value h = 0;
unsigned char c;
-#ifdef HANDLE_MULTIBYTE
- if (MB_CUR_MAX > 1)
- {
- wchar_t lo_wc;
- char mbc[MB_LEN_MAX];
- mbstate_t state_wc;
-
- /* Hash this line until we find a newline. */
- switch (ig_white_space)
- {
- case IGNORE_ALL_SPACE:
- while (1)
- {
- if (*p == '\n')
- {
- ++p;
- break;
- }
-
- MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
-
- if (convfail)
- mbc[0] = *p++;
- else if (!iswspace (wc))
- {
- bool flag = 0;
-
- if (ig_case)
- {
- lo_wc = towlower (wc);
- if (lo_wc != wc)
- {
- flag = 1;
-
- p += mblength;
- memset (&state_wc, '\0', sizeof(mbstate_t));
- mblength = wcrtomb (mbc, lo_wc, &state_wc);
-
- assert (mblength != (size_t)-1 &&
- mblength != (size_t)-2);
-
- mblength = (mblength < 1) ? 1 : mblength;
- }
- }
-
- if (!flag)
- {
- for (i = 0; i < mblength; i++)
- mbc[i] = *p++;
- }
- }
- else
- {
- p += mblength;
- continue;
- }
-
- for (i = 0; i < mblength; i++)
- h = HASH (h, mbc[i]);
- }
- break;
-
- case IGNORE_SPACE_CHANGE:
- while (1)
- {
- if (*p == '\n')
- {
- ++p;
- break;
- }
-
- MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
-
- if (!convfail && iswspace (wc))
- {
- while (1)
- {
- if (*p == '\n')
- {
- ++p;
- goto hashing_done;
- }
-
- p += mblength;
- MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
- if (convfail || !iswspace (wc))
- break;
- }
- h = HASH (h, ' ');
- }
-
- /* WC is now the first non-space. */
- if (convfail)
- mbc[0] = *p++;
- else
- {
- bool flag = 0;
-
- if (ignore_case)
- {
- lo_wc = towlower (wc);
- if (lo_wc != wc)
- {
- flag = 1;
-
- p += mblength;
- memset (&state_wc, '\0', sizeof(mbstate_t));
- mblength = wcrtomb (mbc, lo_wc, &state_wc);
-
- assert (mblength != (size_t)-1 &&
- mblength != (size_t)-2);
-
- mblength = (mblength < 1) ? 1 : mblength;
- }
- }
-
- if (!flag)
- {
- for (i = 0; i < mblength; i++)
- mbc[i] = *p++;
- }
- }
-
- for (i = 0; i < mblength; i++)
- h = HASH (h, mbc[i]);
- }
- break;
-
- case IGNORE_TAB_EXPANSION:
- case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
- case IGNORE_TRAILING_SPACE:
- {
- size_t column = 0;
- while (1)
- {
- if (*p == '\n')
- {
- ++p;
- break;
- }
-
- MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
-
- if (!convfail
- && ig_white_space & IGNORE_TRAILING_SPACE
- && iswspace (wc))
- {
- char const *p1 = p;
- while (1)
- {
- if (*p1 == '\n')
- {
- p = p1 + 1;
- goto hashing_done;
- }
-
- p1 += mblength;
- MBC2WC (p1, suffix_begin, mblength, wc, state, convfail);
- if (convfail || !iswspace (wc))
- break;
- }
- }
-
- size_t repetitions = 1;
- bool no_convert = 0;
-
- if (ig_white_space & IGNORE_TAB_EXPANSION)
- {
- if (convfail)
- column++;
- else
- switch (wc)
- {
- case L'\b':
- column -= 0 < column;
- break;
-
- case L'\t':
- mbc[0] = ' ';
- mblength = 1;
- no_convert = 1;
- p++;
- assert(mblength == 1);
- repetitions = tabsize - column % tabsize;
- column = (column + repetitions < column
- ? 0
- : column + repetitions);
- break;
-
- case L'\r':
- column = 0;
- break;
-
- default:
- column += wcwidth (wc);
- break;
- }
- }
-
- if (ig_case)
- {
- lo_wc = towlower (wc);
- if (lo_wc != wc)
- {
- no_convert = 1;
- p += mblength;
- memset (&state_wc, '\0', sizeof(mbstate_t));
- mblength = wcrtomb (mbc, lo_wc, &state_wc);
-
- assert (mblength != (size_t)-1 &&
- mblength != (size_t)-2);
-
- mblength = (mblength < 1) ? 1 : mblength;
- }
- }
-
- if (!no_convert)
- for (i = 0; i < mblength; i++)
- mbc[i] = *p++;
-
- do
- {
- for (i = 0; i < mblength; i++)
- h = HASH (h, mbc[i]);
- }
- while (--repetitions != 0);
- }
- }
- break;
-
- default:
- while (1)
- {
- if (*p == '\n')
- {
- ++p;
- break;
- }
-
- MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
-
- if (convfail)
- mbc[0] = *p++;
- else
- {
- int flag = 0;
-
- if (ig_case)
- {
- lo_wc = towlower (wc);
- if (lo_wc != wc)
- {
- flag = 1;
- p += mblength;
- memset (&state_wc, '\0', sizeof(mbstate_t));
- mblength = wcrtomb (mbc, lo_wc, &state_wc);
-
- assert (mblength != (size_t)-1 &&
- mblength != (size_t)-2);
-
- mblength = (mblength < 1) ? 1 : mblength;
- }
- }
-
- if (!flag)
- {
- for (i = 0; i < mblength; i++)
- mbc[i] = *p++;
- }
- }
-
- for (i = 0; i < mblength; i++)
- h = HASH (h, mbc[i]);
- }
- }
- }
- else
-#endif
-
/* Hash this line until we find a newline. */
switch (ig_white_space)
{
@@ -708,7 +397,7 @@ find_and_hash_each_line (struct file_data *current)
else if (!diff_length_compare_anyway)
continue;
- if (! lines_differ (eqline, eqs[i].length + 1, ip, length + 1))
+ if (! lines_differ (eqline, ip))
break;
}
diff --git a/src/io.c.i18n b/src/io.c.i18n
deleted file mode 100644
index b4ef5dc..0000000
--- a/src/io.c.i18n
+++ /dev/null
@@ -1,830 +0,0 @@
-/* File I/O for GNU DIFF.
-
- Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
- 2015-2017 Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "diff.h"
-#include
-#include
-#include
-#include
-
-/* Rotate an unsigned value to the left. */
-#define ROL(v, n) ((v) << (n) | (v) >> (sizeof (v) * CHAR_BIT - (n)))
-
-/* Given a hash value and a new character, return a new hash value. */
-#define HASH(h, c) ((c) + ROL (h, 7))
-
-/* The type of a hash value. */
-typedef size_t hash_value;
-verify (! TYPE_SIGNED (hash_value));
-
-/* Lines are put into equivalence classes of lines that match in lines_differ.
- Each equivalence class is represented by one of these structures,
- but only while the classes are being computed.
- Afterward, each class is represented by a number. */
-struct equivclass
-{
- lin next; /* Next item in this bucket. */
- hash_value hash; /* Hash of lines in this class. */
- char const *line; /* A line that fits this class. */
- size_t length; /* That line's length, not counting its newline. */
-};
-
-/* Hash-table: array of buckets, each being a chain of equivalence classes.
- buckets[-1] is reserved for incomplete lines. */
-static lin *buckets;
-
-/* Number of buckets in the hash table array, not counting buckets[-1]. */
-static size_t nbuckets;
-
-/* Array in which the equivalence classes are allocated.
- The bucket-chains go through the elements in this array.
- The number of an equivalence class is its index in this array. */
-static struct equivclass *equivs;
-
-/* Index of first free element in the array 'equivs'. */
-static lin equivs_index;
-
-/* Number of elements allocated in the array 'equivs'. */
-static lin equivs_alloc;
-
-/* Read a block of data into a file buffer, checking for EOF and error. */
-
-void
-file_block_read (struct file_data *current, size_t size)
-{
- if (size && ! current->eof)
- {
- size_t s = block_read (current->desc,
- FILE_BUFFER (current) + current->buffered, size);
- if (s == SIZE_MAX)
- pfatal_with_name (current->name);
- current->buffered += s;
- current->eof = s < size;
- }
-}
-
-/* Check for binary files and compare them for exact identity. */
-
-/* Return 1 if BUF contains a non text character.
- SIZE is the number of characters in BUF. */
-
-#define binary_file_p(buf, size) (memchr (buf, 0, size) != 0)
-
-/* Get ready to read the current file.
- Return nonzero if SKIP_TEST is zero,
- and if it appears to be a binary file. */
-
-static bool
-sip (struct file_data *current, bool skip_test)
-{
- /* If we have a nonexistent file at this stage, treat it as empty. */
- if (current->desc < 0)
- {
- /* Leave room for a sentinel. */
- current->bufsize = sizeof (word);
- current->buffer = xmalloc (current->bufsize);
- }
- else
- {
- current->bufsize = buffer_lcm (sizeof (word),
- STAT_BLOCKSIZE (current->stat),
- PTRDIFF_MAX - 2 * sizeof (word));
- current->buffer = xmalloc (current->bufsize);
-
-#ifdef __KLIBC__
- /* Skip test if seek is not possible */
- skip_test = skip_test
- || (lseek (current->desc, 0, SEEK_CUR) < 0
- && errno == ESPIPE);
-#endif
-
- if (! skip_test)
- {
- /* Check first part of file to see if it's a binary file. */
-
- int prev_mode = set_binary_mode (current->desc, O_BINARY);
- off_t buffered;
- file_block_read (current, current->bufsize);
- buffered = current->buffered;
-
- if (prev_mode != O_BINARY)
- {
- /* Revert to text mode and seek back to the start to reread
- the file. Use relative seek, since file descriptors
- like stdin might not start at offset zero. */
- if (lseek (current->desc, - buffered, SEEK_CUR) < 0)
- pfatal_with_name (current->name);
- set_binary_mode (current->desc, prev_mode);
- current->buffered = 0;
- current->eof = false;
- }
-
- return binary_file_p (current->buffer, buffered);
- }
- }
-
- current->buffered = 0;
- current->eof = false;
- return false;
-}
-
-/* Slurp the rest of the current file completely into memory. */
-
-static void
-slurp (struct file_data *current)
-{
- size_t cc;
-
- if (current->desc < 0)
- {
- /* The file is nonexistent. */
- return;
- }
-
- if (S_ISREG (current->stat.st_mode))
- {
- /* It's a regular file; slurp in the rest all at once. */
-
- /* Get the size out of the stat block.
- Allocate just enough room for appended newline plus word sentinel,
- plus word-alignment since we want the buffer word-aligned. */
- size_t file_size = current->stat.st_size;
- cc = file_size + 2 * sizeof (word) - file_size % sizeof (word);
- if (file_size != current->stat.st_size || cc < file_size
- || PTRDIFF_MAX <= cc)
- xalloc_die ();
-
- if (current->bufsize < cc)
- {
- current->bufsize = cc;
- current->buffer = xrealloc (current->buffer, cc);
- }
-
- /* Try to read at least 1 more byte than the size indicates, to
- detect whether the file is growing. This is a nicety for
- users who run 'diff' on files while they are changing. */
-
- if (current->buffered <= file_size)
- {
- file_block_read (current, file_size + 1 - current->buffered);
- if (current->buffered <= file_size)
- return;
- }
- }
-
- /* It's not a regular file, or it's a growing regular file; read it,
- growing the buffer as needed. */
-
- file_block_read (current, current->bufsize - current->buffered);
-
- if (current->buffered)
- {
- while (current->buffered == current->bufsize)
- {
- if (PTRDIFF_MAX / 2 - sizeof (word) < current->bufsize)
- xalloc_die ();
- current->bufsize *= 2;
- current->buffer = xrealloc (current->buffer, current->bufsize);
- file_block_read (current, current->bufsize - current->buffered);
- }
-
- /* Allocate just enough room for appended newline plus word
- sentinel, plus word-alignment. */
- cc = current->buffered + 2 * sizeof (word);
- current->bufsize = cc - cc % sizeof (word);
- current->buffer = xrealloc (current->buffer, current->bufsize);
- }
-}
-
-/* Split the file into lines, simultaneously computing the equivalence
- class for each line. */
-
-static void
-find_and_hash_each_line (struct file_data *current)
-{
- char const *p = current->prefix_end;
- lin i, *bucket;
- size_t length;
-
- /* Cache often-used quantities in local variables to help the compiler. */
- char const **linbuf = current->linbuf;
- lin alloc_lines = current->alloc_lines;
- lin line = 0;
- lin linbuf_base = current->linbuf_base;
- lin *cureqs = xmalloc (alloc_lines * sizeof *cureqs);
- struct equivclass *eqs = equivs;
- lin eqs_index = equivs_index;
- lin eqs_alloc = equivs_alloc;
- char const *suffix_begin = current->suffix_begin;
- char const *bufend = FILE_BUFFER (current) + current->buffered;
- bool ig_case = ignore_case;
- enum DIFF_white_space ig_white_space = ignore_white_space;
- bool diff_length_compare_anyway =
- ig_white_space != IGNORE_NO_WHITE_SPACE;
- bool same_length_diff_contents_compare_anyway =
- diff_length_compare_anyway | ig_case;
-
- while (p < suffix_begin)
- {
- char const *ip = p;
- hash_value h = 0;
- unsigned char c;
-
- /* Hash this line until we find a newline. */
- switch (ig_white_space)
- {
- case IGNORE_ALL_SPACE:
- while ((c = *p++) != '\n')
- if (! isspace (c))
- h = HASH (h, ig_case ? tolower (c) : c);
- break;
-
- case IGNORE_SPACE_CHANGE:
- while ((c = *p++) != '\n')
- {
- if (isspace (c))
- {
- do
- if ((c = *p++) == '\n')
- goto hashing_done;
- while (isspace (c));
-
- h = HASH (h, ' ');
- }
-
- /* C is now the first non-space. */
- h = HASH (h, ig_case ? tolower (c) : c);
- }
- break;
-
- case IGNORE_TAB_EXPANSION:
- case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
- case IGNORE_TRAILING_SPACE:
- {
- size_t column = 0;
- while ((c = *p++) != '\n')
- {
- if (ig_white_space & IGNORE_TRAILING_SPACE
- && isspace (c))
- {
- char const *p1 = p;
- unsigned char c1;
- do
- if ((c1 = *p1++) == '\n')
- {
- p = p1;
- goto hashing_done;
- }
- while (isspace (c1));
- }
-
- size_t repetitions = 1;
-
- if (ig_white_space & IGNORE_TAB_EXPANSION)
- switch (c)
- {
- case '\b':
- column -= 0 < column;
- break;
-
- case '\t':
- c = ' ';
- repetitions = tabsize - column % tabsize;
- column = (column + repetitions < column
- ? 0
- : column + repetitions);
- break;
-
- case '\r':
- column = 0;
- break;
-
- default:
- column++;
- break;
- }
-
- if (ig_case)
- c = tolower (c);
-
- do
- h = HASH (h, c);
- while (--repetitions != 0);
- }
- }
- break;
-
- default:
- if (ig_case)
- while ((c = *p++) != '\n')
- h = HASH (h, tolower (c));
- else
- while ((c = *p++) != '\n')
- h = HASH (h, c);
- break;
- }
-
- hashing_done:;
-
- bucket = &buckets[h % nbuckets];
- length = p - ip - 1;
-
- if (p == bufend
- && current->missing_newline
- && ROBUST_OUTPUT_STYLE (output_style))
- {
- /* The last line is incomplete and we do not silently
- complete lines. If the line cannot compare equal to any
- complete line, put it into buckets[-1] so that it can
- compare equal only to the other file's incomplete line
- (if one exists). */
- if (ig_white_space < IGNORE_TRAILING_SPACE)
- bucket = &buckets[-1];
- }
-
- for (i = *bucket; ; i = eqs[i].next)
- if (!i)
- {
- /* Create a new equivalence class in this bucket. */
- i = eqs_index++;
- if (i == eqs_alloc)
- {
- if (PTRDIFF_MAX / (2 * sizeof *eqs) <= eqs_alloc)
- xalloc_die ();
- eqs_alloc *= 2;
- eqs = xrealloc (eqs, eqs_alloc * sizeof *eqs);
- }
- eqs[i].next = *bucket;
- eqs[i].hash = h;
- eqs[i].line = ip;
- eqs[i].length = length;
- *bucket = i;
- break;
- }
- else if (eqs[i].hash == h)
- {
- char const *eqline = eqs[i].line;
-
- /* Reuse existing class if lines_differ reports the lines
- equal. */
- if (eqs[i].length == length)
- {
- /* Reuse existing equivalence class if the lines are identical.
- This detects the common case of exact identity
- faster than lines_differ would. */
- if (memcmp (eqline, ip, length) == 0)
- break;
- if (!same_length_diff_contents_compare_anyway)
- continue;
- }
- else if (!diff_length_compare_anyway)
- continue;
-
- if (! lines_differ (eqline, ip))
- break;
- }
-
- /* Maybe increase the size of the line table. */
- if (line == alloc_lines)
- {
- /* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */
- if (PTRDIFF_MAX / 3 <= alloc_lines
- || PTRDIFF_MAX / sizeof *cureqs <= 2 * alloc_lines - linbuf_base
- || PTRDIFF_MAX / sizeof *linbuf <= alloc_lines - linbuf_base)
- xalloc_die ();
- alloc_lines = 2 * alloc_lines - linbuf_base;
- cureqs = xrealloc (cureqs, alloc_lines * sizeof *cureqs);
- linbuf += linbuf_base;
- linbuf = xrealloc (linbuf,
- (alloc_lines - linbuf_base) * sizeof *linbuf);
- linbuf -= linbuf_base;
- }
- linbuf[line] = ip;
- cureqs[line] = i;
- ++line;
- }
-
- current->buffered_lines = line;
-
- for (i = 0; ; i++)
- {
- /* Record the line start for lines in the suffix that we care about.
- Record one more line start than lines,
- so that we can compute the length of any buffered line. */
- if (line == alloc_lines)
- {
- /* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */
- if (PTRDIFF_MAX / 3 <= alloc_lines
- || PTRDIFF_MAX / sizeof *cureqs <= 2 * alloc_lines - linbuf_base
- || PTRDIFF_MAX / sizeof *linbuf <= alloc_lines - linbuf_base)
- xalloc_die ();
- alloc_lines = 2 * alloc_lines - linbuf_base;
- linbuf += linbuf_base;
- linbuf = xrealloc (linbuf,
- (alloc_lines - linbuf_base) * sizeof *linbuf);
- linbuf -= linbuf_base;
- }
- linbuf[line] = p;
-
- if (p == bufend)
- {
- /* If the last line is incomplete and we do not silently
- complete lines, don't count its appended newline. */
- if (current->missing_newline && ROBUST_OUTPUT_STYLE (output_style))
- linbuf[line]--;
- break;
- }
-
- if (context <= i && no_diff_means_no_output)
- break;
-
- line++;
-
- while (*p++ != '\n')
- continue;
- }
-
- /* Done with cache in local variables. */
- current->linbuf = linbuf;
- current->valid_lines = line;
- current->alloc_lines = alloc_lines;
- current->equivs = cureqs;
- equivs = eqs;
- equivs_alloc = eqs_alloc;
- equivs_index = eqs_index;
-}
-
-/* Prepare the text. Make sure the text end is initialized.
- Make sure text ends in a newline,
- but remember that we had to add one.
- Strip trailing CRs, if that was requested. */
-
-static void
-prepare_text (struct file_data *current)
-{
- size_t buffered = current->buffered;
- char *p = FILE_BUFFER (current);
-
- if (buffered == 0 || p[buffered - 1] == '\n')
- current->missing_newline = false;
- else
- {
- p[buffered++] = '\n';
- current->missing_newline = true;
- }
-
- if (!p)
- return;
-
- /* Don't use uninitialized storage when planting or using sentinels. */
- memset (p + buffered, 0, sizeof (word));
-
- if (strip_trailing_cr)
- {
- char *dst;
- char *srclim = p + buffered;
- *srclim = '\r';
- dst = rawmemchr (p, '\r');
-
- if (dst != srclim)
- {
- char const *src = dst;
- do
- {
- *dst = *src++;
- dst += ! (*dst == '\r' && *src == '\n');
- }
- while (src < srclim);
-
- buffered -= src - dst;
- }
- }
-
- current->buffered = buffered;
-}
-
-/* We have found N lines in a buffer of size S; guess the
- proportionate number of lines that will be found in a buffer of
- size T. However, do not guess a number of lines so large that the
- resulting line table might cause overflow in size calculations. */
-static lin
-guess_lines (lin n, size_t s, size_t t)
-{
- size_t guessed_bytes_per_line = n < 10 ? 32 : s / (n - 1);
- lin guessed_lines = MAX (1, t / guessed_bytes_per_line);
- return MIN (guessed_lines, PTRDIFF_MAX / (2 * sizeof (char *) + 1) - 5) + 5;
-}
-
-/* Given a vector of two file_data objects, find the identical
- prefixes and suffixes of each object. */
-
-static void
-find_identical_ends (struct file_data filevec[])
-{
- word *w0, *w1;
- char *p0, *p1, *buffer0, *buffer1;
- char const *end0, *beg0;
- char const **linbuf0, **linbuf1;
- lin i, lines;
- size_t n0, n1;
- lin alloc_lines0, alloc_lines1;
- bool prefix_needed;
- lin buffered_prefix, prefix_count, prefix_mask;
- lin middle_guess, suffix_guess;
-
- slurp (&filevec[0]);
- prepare_text (&filevec[0]);
- if (filevec[0].desc != filevec[1].desc)
- {
- slurp (&filevec[1]);
- prepare_text (&filevec[1]);
- }
- else
- {
- filevec[1].buffer = filevec[0].buffer;
- filevec[1].bufsize = filevec[0].bufsize;
- filevec[1].buffered = filevec[0].buffered;
- filevec[1].missing_newline = filevec[0].missing_newline;
- }
-
- /* Find identical prefix. */
-
- w0 = filevec[0].buffer;
- w1 = filevec[1].buffer;
- p0 = buffer0 = (char *) w0;
- p1 = buffer1 = (char *) w1;
- n0 = filevec[0].buffered;
- n1 = filevec[1].buffered;
-
- if (p0 == p1)
- /* The buffers are the same; sentinels won't work. */
- p0 = p1 += n1;
- else
- {
- /* Insert end sentinels, in this case characters that are guaranteed
- to make the equality test false, and thus terminate the loop. */
-
- if (n0 < n1)
- p0[n0] = ~p1[n0];
- else
- p1[n1] = ~p0[n1];
-
- /* Loop until first mismatch, or to the sentinel characters. */
-
- /* Compare a word at a time for speed. */
- while (*w0 == *w1)
- w0++, w1++;
-
- /* Do the last few bytes of comparison a byte at a time. */
- p0 = (char *) w0;
- p1 = (char *) w1;
- while (*p0 == *p1)
- p0++, p1++;
-
- /* Don't mistakenly count missing newline as part of prefix. */
- if (ROBUST_OUTPUT_STYLE (output_style)
- && ((buffer0 + n0 - filevec[0].missing_newline < p0)
- !=
- (buffer1 + n1 - filevec[1].missing_newline < p1)))
- p0--, p1--;
- }
-
- /* Now P0 and P1 point at the first nonmatching characters. */
-
- /* Skip back to last line-beginning in the prefix,
- and then discard up to HORIZON_LINES lines from the prefix. */
- i = horizon_lines;
- while (p0 != buffer0 && (p0[-1] != '\n' || i--))
- p0--, p1--;
-
- /* Record the prefix. */
- filevec[0].prefix_end = p0;
- filevec[1].prefix_end = p1;
-
- /* Find identical suffix. */
-
- /* P0 and P1 point beyond the last chars not yet compared. */
- p0 = buffer0 + n0;
- p1 = buffer1 + n1;
-
- if (! ROBUST_OUTPUT_STYLE (output_style)
- || filevec[0].missing_newline == filevec[1].missing_newline)
- {
- end0 = p0; /* Addr of last char in file 0. */
-
- /* Get value of P0 at which we should stop scanning backward:
- this is when either P0 or P1 points just past the last char
- of the identical prefix. */
- beg0 = filevec[0].prefix_end + (n0 < n1 ? 0 : n0 - n1);
-
- /* Scan back until chars don't match or we reach that point. */
- while (p0 != beg0)
- if (*--p0 != *--p1)
- {
- /* Point at the first char of the matching suffix. */
- ++p0, ++p1;
- beg0 = p0;
- break;
- }
-
- /* Are we at a line-beginning in both files? If not, add the rest of
- this line to the main body. Discard up to HORIZON_LINES lines from
- the identical suffix. Also, discard one extra line,
- because shift_boundaries may need it. */
- i = horizon_lines + !((buffer0 == p0 || p0[-1] == '\n')
- &&
- (buffer1 == p1 || p1[-1] == '\n'));
- while (i-- && p0 != end0)
- while (*p0++ != '\n')
- continue;
-
- p1 += p0 - beg0;
- }
-
- /* Record the suffix. */
- filevec[0].suffix_begin = p0;
- filevec[1].suffix_begin = p1;
-
- /* Calculate number of lines of prefix to save.
-
- prefix_count == 0 means save the whole prefix;
- we need this for options like -D that output the whole file,
- or for enormous contexts (to avoid worrying about arithmetic overflow).
- We also need it for options like -F that output some preceding line;
- at least we will need to find the last few lines,
- but since we don't know how many, it's easiest to find them all.
-
- Otherwise, prefix_count != 0. Save just prefix_count lines at start
- of the line buffer; they'll be moved to the proper location later.
- Handle 1 more line than the context says (because we count 1 too many),
- rounded up to the next power of 2 to speed index computation. */
-
- if (no_diff_means_no_output && ! function_regexp.fastmap
- && context < LIN_MAX / 4 && context < n0)
- {
- middle_guess = guess_lines (0, 0, p0 - filevec[0].prefix_end);
- suffix_guess = guess_lines (0, 0, buffer0 + n0 - p0);
- for (prefix_count = 1; prefix_count <= context; prefix_count *= 2)
- continue;
- alloc_lines0 = (prefix_count + middle_guess
- + MIN (context, suffix_guess));
- }
- else
- {
- prefix_count = 0;
- alloc_lines0 = guess_lines (0, 0, n0);
- }
-
- prefix_mask = prefix_count - 1;
- lines = 0;
- linbuf0 = xmalloc (alloc_lines0 * sizeof *linbuf0);
- prefix_needed = ! (no_diff_means_no_output
- && filevec[0].prefix_end == p0
- && filevec[1].prefix_end == p1);
- p0 = buffer0;
-
- /* If the prefix is needed, find the prefix lines. */
- if (prefix_needed)
- {
- end0 = filevec[0].prefix_end;
- while (p0 != end0)
- {
- lin l = lines++ & prefix_mask;
- if (l == alloc_lines0)
- {
- if (PTRDIFF_MAX / (2 * sizeof *linbuf0) <= alloc_lines0)
- xalloc_die ();
- alloc_lines0 *= 2;
- linbuf0 = xrealloc (linbuf0, alloc_lines0 * sizeof *linbuf0);
- }
- linbuf0[l] = p0;
- while (*p0++ != '\n')
- continue;
- }
- }
- buffered_prefix = prefix_count && context < lines ? context : lines;
-
- /* Allocate line buffer 1. */
-
- middle_guess = guess_lines (lines, p0 - buffer0, p1 - filevec[1].prefix_end);
- suffix_guess = guess_lines (lines, p0 - buffer0, buffer1 + n1 - p1);
- alloc_lines1 = buffered_prefix + middle_guess + MIN (context, suffix_guess);
- if (alloc_lines1 < buffered_prefix
- || PTRDIFF_MAX / sizeof *linbuf1 <= alloc_lines1)
- xalloc_die ();
- linbuf1 = xmalloc (alloc_lines1 * sizeof *linbuf1);
-
- if (buffered_prefix != lines)
- {
- /* Rotate prefix lines to proper location. */
- for (i = 0; i < buffered_prefix; i++)
- linbuf1[i] = linbuf0[(lines - context + i) & prefix_mask];
- for (i = 0; i < buffered_prefix; i++)
- linbuf0[i] = linbuf1[i];
- }
-
- /* Initialize line buffer 1 from line buffer 0. */
- for (i = 0; i < buffered_prefix; i++)
- linbuf1[i] = linbuf0[i] - buffer0 + buffer1;
-
- /* Record the line buffer, adjusted so that
- linbuf[0] points at the first differing line. */
- filevec[0].linbuf = linbuf0 + buffered_prefix;
- filevec[1].linbuf = linbuf1 + buffered_prefix;
- filevec[0].linbuf_base = filevec[1].linbuf_base = - buffered_prefix;
- filevec[0].alloc_lines = alloc_lines0 - buffered_prefix;
- filevec[1].alloc_lines = alloc_lines1 - buffered_prefix;
- filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
-}
-
-/* If 1 < k, then (2**k - prime_offset[k]) is the largest prime less
- than 2**k. This table is derived from Chris K. Caldwell's list
- . */
-
-static unsigned char const prime_offset[] =
-{
- 0, 0, 1, 1, 3, 1, 3, 1, 5, 3, 3, 9, 3, 1, 3, 19, 15, 1, 5, 1, 3, 9, 3,
- 15, 3, 39, 5, 39, 57, 3, 35, 1, 5, 9, 41, 31, 5, 25, 45, 7, 87, 21,
- 11, 57, 17, 55, 21, 115, 59, 81, 27, 129, 47, 111, 33, 55, 5, 13, 27,
- 55, 93, 1, 57, 25
-};
-
-/* Verify that this host's size_t is not too wide for the above table. */
-
-verify (sizeof (size_t) * CHAR_BIT <= sizeof prime_offset);
-
-/* Given a vector of two file_data objects, read the file associated
- with each one, and build the table of equivalence classes.
- Return nonzero if either file appears to be a binary file.
- If PRETEND_BINARY is nonzero, pretend they are binary regardless. */
-
-bool
-read_files (struct file_data filevec[], bool pretend_binary)
-{
- int i;
- bool skip_test = text | pretend_binary;
- bool appears_binary = pretend_binary | sip (&filevec[0], skip_test);
-
- if (filevec[0].desc != filevec[1].desc)
- appears_binary |= sip (&filevec[1], skip_test | appears_binary);
- else
- {
- filevec[1].buffer = filevec[0].buffer;
- filevec[1].bufsize = filevec[0].bufsize;
- filevec[1].buffered = filevec[0].buffered;
- }
- if (appears_binary)
- {
- set_binary_mode (filevec[0].desc, O_BINARY);
- set_binary_mode (filevec[1].desc, O_BINARY);
- return true;
- }
-
- find_identical_ends (filevec);
-
- equivs_alloc = filevec[0].alloc_lines + filevec[1].alloc_lines + 1;
- if (PTRDIFF_MAX / sizeof *equivs <= equivs_alloc)
- xalloc_die ();
- equivs = xmalloc (equivs_alloc * sizeof *equivs);
- /* Equivalence class 0 is permanently safe for lines that were not
- hashed. Real equivalence classes start at 1. */
- equivs_index = 1;
-
- /* Allocate (one plus) a prime number of hash buckets. Use a prime
- number between 1/3 and 2/3 of the value of equiv_allocs,
- approximately. */
- for (i = 9; (size_t) 1 << i < equivs_alloc / 3; i++)
- continue;
- nbuckets = ((size_t) 1 << i) - prime_offset[i];
- if (PTRDIFF_MAX / sizeof *buckets <= nbuckets)
- xalloc_die ();
- buckets = zalloc ((nbuckets + 1) * sizeof *buckets);
- buckets++;
-
- for (i = 0; i < 2; i++)
- find_and_hash_each_line (&filevec[i]);
-
- filevec[0].equiv_max = filevec[1].equiv_max = equivs_index;
-
- free (equivs);
- free (buckets - 1);
-
- return false;
-}
diff --git a/src/sdiff.c b/src/sdiff.c
index 78a44c4..1ae3dcb 100644
--- a/src/sdiff.c
+++ b/src/sdiff.c
@@ -230,10 +230,8 @@ cleanup (int signo __attribute__((unused)))
if (0 < diffpid)
kill (diffpid, SIGPIPE);
#endif
- if (tmpname) {
+ if (tmpname)
unlink (tmpname);
- free (tmpname);
- }
}
static void exiterr (void) __attribute__((noreturn));
@@ -687,7 +685,6 @@ main (int argc, char *argv[])
if (tmpname)
{
unlink (tmpname);
- free (tmpname);
tmpname = 0;
}
diff --git a/src/sdiff.c.covscan b/src/sdiff.c.covscan
deleted file mode 100644
index 1ae3dcb..0000000
--- a/src/sdiff.c.covscan
+++ /dev/null
@@ -1,1173 +0,0 @@
-/* sdiff - side-by-side merge of file differences
-
- Copyright (C) 1992-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
- 2015-2017 Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "system.h"
-#include "paths.h"
-
-#include
-#include
-
-#include
-#include
-#include "die.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* The official name of this program (e.g., no 'g' prefix). */
-#define PROGRAM_NAME "sdiff"
-
-#define AUTHORS \
- proper_name ("Thomas Lord")
-
-/* Size of chunks read from files which must be parsed into lines. */
-#define SDIFF_BUFSIZE ((size_t) 65536)
-
-static char const *editor_program = DEFAULT_EDITOR_PROGRAM;
-static char const **diffargv;
-
-static char * volatile tmpname;
-static FILE *tmp;
-
-#if HAVE_WORKING_FORK
-static pid_t volatile diffpid;
-#endif
-
-struct line_filter;
-
-static void catchsig (int);
-static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);
-static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);
-static void checksigs (void);
-static void diffarg (char const *);
-static void fatal (char const *) __attribute__((noreturn));
-static void perror_fatal (char const *) __attribute__((noreturn));
-static void trapsigs (void);
-static void untrapsig (int);
-
-static int const sigs[] = {
-#ifdef SIGHUP
- SIGHUP,
-#endif
-#ifdef SIGQUIT
- SIGQUIT,
-#endif
-#ifdef SIGTERM
- SIGTERM,
-#endif
-#ifdef SIGXCPU
- SIGXCPU,
-#endif
-#ifdef SIGXFSZ
- SIGXFSZ,
-#endif
-#ifdef SIGPIPE
- SIGPIPE,
-#endif
- SIGINT
-};
-enum
- {
- NUM_SIGS = sizeof sigs / sizeof *sigs,
- handler_index_of_SIGINT = NUM_SIGS - 1
- };
-
-#if HAVE_SIGACTION
- /* Prefer 'sigaction' if available, since 'signal' can lose signals. */
- static struct sigaction initial_action[NUM_SIGS];
-# define initial_handler(i) (initial_action[i].sa_handler)
- static void signal_handler (int, void (*) (int));
-#else
- static void (*initial_action[NUM_SIGS]) ();
-# define initial_handler(i) (initial_action[i])
-# define signal_handler(sig, handler) signal (sig, handler)
-#endif
-
-static bool diraccess (char const *);
-static int temporary_file (void);
-
-/* Options: */
-
-/* Name of output file if -o specified. */
-static char const *output;
-
-/* Do not print common lines. */
-static bool suppress_common_lines;
-
-/* Value for the long option that does not have single-letter equivalents. */
-enum
-{
- DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
- HELP_OPTION,
- STRIP_TRAILING_CR_OPTION,
- TABSIZE_OPTION
-};
-
-static struct option const longopts[] =
-{
- {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
- {"expand-tabs", 0, 0, 't'},
- {"help", 0, 0, HELP_OPTION},
- {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
- {"ignore-blank-lines", 0, 0, 'B'},
- {"ignore-case", 0, 0, 'i'},
- {"ignore-matching-lines", 1, 0, 'I'},
- {"ignore-space-change", 0, 0, 'b'},
- {"ignore-tab-expansion", 0, 0, 'E'},
- {"ignore-trailing-space", 0, 0, 'Z'},
- {"left-column", 0, 0, 'l'},
- {"minimal", 0, 0, 'd'},
- {"output", 1, 0, 'o'},
- {"speed-large-files", 0, 0, 'H'},
- {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
- {"suppress-common-lines", 0, 0, 's'},
- {"tabsize", 1, 0, TABSIZE_OPTION},
- {"text", 0, 0, 'a'},
- {"version", 0, 0, 'v'},
- {"width", 1, 0, 'w'},
- {0, 0, 0, 0}
-};
-
-static void try_help (char const *, char const *) __attribute__((noreturn));
-static void
-try_help (char const *reason_msgid, char const *operand)
-{
- if (reason_msgid)
- error (0, 0, _(reason_msgid), operand);
- die (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
- program_name);
-}
-
-static void
-check_stdout (void)
-{
- if (ferror (stdout))
- fatal ("write failed");
- else if (fclose (stdout) != 0)
- perror_fatal (_("standard output"));
-}
-
-static char const * const option_help_msgid[] = {
- N_("-o, --output=FILE operate interactively, sending output to FILE"),
- "",
- N_("-i, --ignore-case consider upper- and lower-case to be the same"),
- N_("-E, --ignore-tab-expansion ignore changes due to tab expansion"),
- N_("-Z, --ignore-trailing-space ignore white space at line end"),
- N_("-b, --ignore-space-change ignore changes in the amount of white space"),
- N_("-W, --ignore-all-space ignore all white space"),
- N_("-B, --ignore-blank-lines ignore changes whose lines are all blank"),
- N_("-I, --ignore-matching-lines=RE ignore changes all whose lines match RE"),
- N_(" --strip-trailing-cr strip trailing carriage return on input"),
- N_("-a, --text treat all files as text"),
- "",
- N_("-w, --width=NUM output at most NUM (default 130) print columns"),
- N_("-l, --left-column output only the left column of common lines"),
- N_("-s, --suppress-common-lines do not output common lines"),
- "",
- N_("-t, --expand-tabs expand tabs to spaces in output"),
- N_(" --tabsize=NUM tab stops at every NUM (default 8) print columns"),
- "",
- N_("-d, --minimal try hard to find a smaller set of changes"),
- N_("-H, --speed-large-files assume large files, many scattered small changes"),
- N_(" --diff-program=PROGRAM use PROGRAM to compare files"),
- "",
- N_(" --help display this help and exit"),
- N_("-v, --version output version information and exit"),
- 0
-};
-
-static void
-usage (void)
-{
- char const * const *p;
-
- printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
- printf ("%s\n\n",
- _("Side-by-side merge of differences between FILE1 and FILE2."));
-
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
- for (p = option_help_msgid; *p; p++)
- if (**p)
- printf (" %s\n", _(*p));
- else
- putchar ('\n');
- printf ("\n%s\n%s\n",
- _("If a FILE is '-', read standard input."),
- _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."));
- emit_bug_reporting_address ();
-}
-
-/* Clean up after a signal or other failure. This function is
- async-signal-safe. */
-static void
-cleanup (int signo __attribute__((unused)))
-{
-#if HAVE_WORKING_FORK
- if (0 < diffpid)
- kill (diffpid, SIGPIPE);
-#endif
- if (tmpname)
- unlink (tmpname);
-}
-
-static void exiterr (void) __attribute__((noreturn));
-static void
-exiterr (void)
-{
- cleanup (0);
- untrapsig (0);
- checksigs ();
- exit (EXIT_TROUBLE);
-}
-
-static void
-fatal (char const *msgid)
-{
- error (0, 0, "%s", _(msgid));
- exiterr ();
-}
-
-static void
-perror_fatal (char const *msg)
-{
- int e = errno;
- checksigs ();
- error (0, e, "%s", msg);
- exiterr ();
-}
-
-static void
-check_child_status (int werrno, int wstatus, int max_ok_status,
- char const *subsidiary_program)
-{
- int status = (! werrno && WIFEXITED (wstatus)
- ? WEXITSTATUS (wstatus)
- : INT_MAX);
-
- if (max_ok_status < status)
- {
- error (0, werrno,
- _(status == 126
- ? "subsidiary program '%s' could not be invoked"
- : status == 127
- ? "subsidiary program '%s' not found"
- : status == INT_MAX
- ? "subsidiary program '%s' failed"
- : "subsidiary program '%s' failed (exit status %d)"),
- subsidiary_program, status);
- exiterr ();
- }
-}
-
-static FILE *
-ck_fopen (char const *fname, char const *type)
-{
- FILE *r = fopen (fname, type);
- if (! r)
- perror_fatal (fname);
- return r;
-}
-
-static void
-ck_fclose (FILE *f)
-{
- if (fclose (f))
- perror_fatal ("fclose");
-}
-
-static size_t
-ck_fread (char *buf, size_t size, FILE *f)
-{
- size_t r = fread (buf, sizeof (char), size, f);
- if (r == 0 && ferror (f))
- perror_fatal (_("read failed"));
- return r;
-}
-
-static void
-ck_fwrite (char const *buf, size_t size, FILE *f)
-{
- if (fwrite (buf, sizeof (char), size, f) != size)
- perror_fatal (_("write failed"));
-}
-
-static void
-ck_fflush (FILE *f)
-{
- if (fflush (f) != 0)
- perror_fatal (_("write failed"));
-}
-
-static char const *
-expand_name (char *name, bool is_dir, char const *other_name)
-{
- if (STREQ (name, "-"))
- fatal ("cannot interactively merge standard input");
- if (! is_dir)
- return name;
- else
- {
- /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */
- char const *base = last_component (other_name);
- size_t namelen = strlen (name), baselen = base_len (base);
- bool insert_slash = *last_component (name) && name[namelen - 1] != '/';
- char *r = xmalloc (namelen + insert_slash + baselen + 1);
- memcpy (r, name, namelen);
- r[namelen] = '/';
- memcpy (r + namelen + insert_slash, base, baselen);
- r[namelen + insert_slash + baselen] = '\0';
- return r;
- }
-}
-
-struct line_filter {
- FILE *infile;
- char *bufpos;
- char *buffer;
- char *buflim;
-};
-
-static void
-lf_init (struct line_filter *lf, FILE *infile)
-{
- lf->infile = infile;
- lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
- lf->buflim[0] = '\n';
-}
-
-/* Fill an exhausted line_filter buffer from its INFILE */
-static size_t
-lf_refill (struct line_filter *lf)
-{
- size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
- lf->bufpos = lf->buffer;
- lf->buflim = lf->buffer + s;
- lf->buflim[0] = '\n';
- checksigs ();
- return s;
-}
-
-/* Advance LINES on LF's infile, copying lines to OUTFILE */
-static void
-lf_copy (struct line_filter *lf, lin lines, FILE *outfile)
-{
- char *start = lf->bufpos;
-
- while (lines)
- {
- lf->bufpos = rawmemchr (lf->bufpos, '\n');
- if (lf->bufpos == lf->buflim)
- {
- ck_fwrite (start, lf->buflim - start, outfile);
- if (! lf_refill (lf))
- return;
- start = lf->bufpos;
- }
- else
- {
- --lines;
- ++lf->bufpos;
- }
- }
-
- ck_fwrite (start, lf->bufpos - start, outfile);
-}
-
-/* Advance LINES on LF's infile without doing output */
-static void
-lf_skip (struct line_filter *lf, lin lines)
-{
- while (lines)
- {
- lf->bufpos = rawmemchr (lf->bufpos, '\n');
- if (lf->bufpos == lf->buflim)
- {
- if (! lf_refill (lf))
- break;
- }
- else
- {
- --lines;
- ++lf->bufpos;
- }
- }
-}
-
-/* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */
-static int
-lf_snarf (struct line_filter *lf, char *buffer, size_t bufsize)
-{
- for (;;)
- {
- char *start = lf->bufpos;
- char *next = rawmemchr (start, '\n');
- size_t s = next - start;
- if (bufsize <= s)
- return 0;
- memcpy (buffer, start, s);
- if (next < lf->buflim)
- {
- buffer[s] = 0;
- lf->bufpos = next + 1;
- return 1;
- }
- if (! lf_refill (lf))
- return s ? 0 : EOF;
- buffer += s;
- bufsize -= s;
- }
-}
-
-int
-main (int argc, char *argv[])
-{
- int opt;
- char const *prog;
-
- exit_failure = EXIT_TROUBLE;
- initialize_main (&argc, &argv);
- set_program_name (argv[0]);
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
- c_stack_action (cleanup);
-
- prog = getenv ("EDITOR");
- if (prog)
- editor_program = prog;
-
- diffarg (DEFAULT_DIFF_PROGRAM);
-
- /* parse command line args */
- while ((opt = getopt_long (argc, argv, "abBdEHiI:lo:stvw:WZ", longopts, 0))
- != -1)
- {
- switch (opt)
- {
- case 'a':
- diffarg ("-a");
- break;
-
- case 'b':
- diffarg ("-b");
- break;
-
- case 'B':
- diffarg ("-B");
- break;
-
- case 'd':
- diffarg ("-d");
- break;
-
- case 'E':
- diffarg ("-E");
- break;
-
- case 'H':
- diffarg ("-H");
- break;
-
- case 'i':
- diffarg ("-i");
- break;
-
- case 'I':
- diffarg ("-I");
- diffarg (optarg);
- break;
-
- case 'l':
- diffarg ("--left-column");
- break;
-
- case 'o':
- output = optarg;
- break;
-
- case 's':
- suppress_common_lines = true;
- break;
-
- case 't':
- diffarg ("-t");
- break;
-
- case 'v':
- version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
- AUTHORS, (char *) NULL);
- check_stdout ();
- return EXIT_SUCCESS;
-
- case 'w':
- diffarg ("-W");
- diffarg (optarg);
- break;
-
- case 'W':
- diffarg ("-w");
- break;
-
- case 'Z':
- diffarg ("-Z");
- break;
-
- case DIFF_PROGRAM_OPTION:
- diffargv[0] = optarg;
- break;
-
- case HELP_OPTION:
- usage ();
- check_stdout ();
- return EXIT_SUCCESS;
-
- case STRIP_TRAILING_CR_OPTION:
- diffarg ("--strip-trailing-cr");
- break;
-
- case TABSIZE_OPTION:
- diffarg ("--tabsize");
- diffarg (optarg);
- break;
-
- default:
- try_help (0, 0);
- }
- }
-
- if (argc - optind != 2)
- {
- if (argc - optind < 2)
- try_help ("missing operand after '%s'", argv[argc - 1]);
- else
- try_help ("extra operand '%s'", argv[optind + 2]);
- }
-
- if (! output)
- {
- /* easy case: diff does everything for us */
- if (suppress_common_lines)
- diffarg ("--suppress-common-lines");
- diffarg ("-y");
- diffarg ("--");
- diffarg (argv[optind]);
- diffarg (argv[optind + 1]);
- diffarg (0);
- execvp (diffargv[0], (char **) diffargv);
- perror_fatal (diffargv[0]);
- }
- else
- {
- char const *lname, *rname;
- FILE *left, *right, *out, *diffout;
- bool interact_ok;
- struct line_filter lfilt;
- struct line_filter rfilt;
- struct line_filter diff_filt;
- bool leftdir = diraccess (argv[optind]);
- bool rightdir = diraccess (argv[optind + 1]);
-
- if (leftdir & rightdir)
- fatal ("both files to be compared are directories");
-
- lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
- left = ck_fopen (lname, "r");
- rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
- right = ck_fopen (rname, "r");
- out = ck_fopen (output, "w");
-
- diffarg ("--sdiff-merge-assist");
- diffarg ("--");
- diffarg (argv[optind]);
- diffarg (argv[optind + 1]);
- diffarg (0);
-
- trapsigs ();
-
-#if ! HAVE_WORKING_FORK
- {
- char *command = system_quote_argv (SCI_SYSTEM, (char **) diffargv);
- errno = 0;
- diffout = popen (command, "r");
- if (! diffout)
- perror_fatal (command);
- free (command);
- }
-#else
- {
- int diff_fds[2];
-
- if (pipe (diff_fds) != 0)
- perror_fatal ("pipe");
-
- diffpid = fork ();
- if (diffpid < 0)
- perror_fatal ("fork");
- if (! diffpid)
- {
- /* Alter the child's SIGINT and SIGPIPE handlers;
- this may munge the parent.
- The child ignores SIGINT in case the user interrupts the editor.
- The child does not ignore SIGPIPE, even if the parent does. */
- if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
- signal_handler (SIGINT, SIG_IGN);
- signal_handler (SIGPIPE, SIG_DFL);
- close (diff_fds[0]);
- if (diff_fds[1] != STDOUT_FILENO)
- {
- dup2 (diff_fds[1], STDOUT_FILENO);
- close (diff_fds[1]);
- }
-
- execvp (diffargv[0], (char **) diffargv);
- _exit (errno == ENOENT ? 127 : 126);
- }
-
- close (diff_fds[1]);
- diffout = fdopen (diff_fds[0], "r");
- if (! diffout)
- perror_fatal ("fdopen");
- }
-#endif
-
- lf_init (&diff_filt, diffout);
- lf_init (&lfilt, left);
- lf_init (&rfilt, right);
-
- interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
-
- ck_fclose (left);
- ck_fclose (right);
- ck_fclose (out);
-
- {
- int wstatus;
- int werrno = 0;
-
-#if ! HAVE_WORKING_FORK
- wstatus = pclose (diffout);
- if (wstatus == -1)
- werrno = errno;
-#else
- ck_fclose (diffout);
- while (waitpid (diffpid, &wstatus, 0) < 0)
- if (errno == EINTR)
- checksigs ();
- else
- perror_fatal ("waitpid");
- diffpid = 0;
-#endif
-
- if (tmpname)
- {
- unlink (tmpname);
- tmpname = 0;
- }
-
- if (! interact_ok)
- exiterr ();
-
- check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]);
- untrapsig (0);
- checksigs ();
- exit (WEXITSTATUS (wstatus));
- }
- }
- return EXIT_SUCCESS; /* Fool '-Wall'. */
-}
-
-static void
-diffarg (char const *a)
-{
- static size_t diffargs, diffarglim;
-
- if (diffargs == diffarglim)
- {
- if (! diffarglim)
- diffarglim = 16;
- else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
- xalloc_die ();
- else
- diffarglim *= 2;
- diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
- }
- diffargv[diffargs++] = a;
-}
-
-/* Signal handling */
-
-static bool volatile ignore_SIGINT;
-static int volatile signal_received;
-static bool sigs_trapped;
-
-static void
-catchsig (int s)
-{
-#if ! HAVE_SIGACTION
- signal (s, SIG_IGN);
-#endif
- if (! (s == SIGINT && ignore_SIGINT))
- signal_received = s;
-}
-
-#if HAVE_SIGACTION
-static struct sigaction catchaction;
-
-static void
-signal_handler (int sig, void (*handler) (int))
-{
- catchaction.sa_handler = handler;
- sigaction (sig, &catchaction, 0);
-}
-#endif
-
-static void
-trapsigs (void)
-{
- int i;
-
-#if HAVE_SIGACTION
- catchaction.sa_flags = SA_RESTART;
- sigemptyset (&catchaction.sa_mask);
- for (i = 0; i < NUM_SIGS; i++)
- sigaddset (&catchaction.sa_mask, sigs[i]);
-#endif
-
- for (i = 0; i < NUM_SIGS; i++)
- {
-#if HAVE_SIGACTION
- sigaction (sigs[i], 0, &initial_action[i]);
-#else
- initial_action[i] = signal (sigs[i], SIG_IGN);
-#endif
- if (initial_handler (i) != SIG_IGN)
- signal_handler (sigs[i], catchsig);
- }
-
-#ifdef SIGCHLD
- /* System V fork+wait does not work if SIGCHLD is ignored. */
- signal (SIGCHLD, SIG_DFL);
-#endif
-
- sigs_trapped = true;
-}
-
-/* Untrap signal S, or all trapped signals if S is zero. */
-static void
-untrapsig (int s)
-{
- int i;
-
- if (sigs_trapped)
- for (i = 0; i < NUM_SIGS; i++)
- if ((! s || sigs[i] == s) && initial_handler (i) != SIG_IGN)
- {
-#if HAVE_SIGACTION
- sigaction (sigs[i], &initial_action[i], 0);
-#else
- signal (sigs[i], initial_action[i]);
-#endif
- }
-}
-
-/* Exit if a signal has been received. */
-static void
-checksigs (void)
-{
- int s = signal_received;
- if (s)
- {
- cleanup (0);
-
- /* Yield an exit status indicating that a signal was received. */
- untrapsig (s);
- kill (getpid (), s);
-
- /* That didn't work, so exit with error status. */
- exit (EXIT_TROUBLE);
- }
-}
-
-static void
-give_help (void)
-{
- fprintf (stderr, "%s", _("\
-ed:\tEdit then use both versions, each decorated with a header.\n\
-eb:\tEdit then use both versions.\n\
-el or e1:\tEdit then use the left version.\n\
-er or e2:\tEdit then use the right version.\n\
-e:\tDiscard both versions then edit a new one.\n\
-l or 1:\tUse the left version.\n\
-r or 2:\tUse the right version.\n\
-s:\tSilently include common lines.\n\
-v:\tVerbosely include common lines.\n\
-q:\tQuit.\n\
-"));
-}
-
-static int
-skip_white (void)
-{
- int c;
- for (;;)
- {
- c = getchar ();
- if (! isspace (c) || c == '\n')
- break;
- checksigs ();
- }
- if (ferror (stdin))
- perror_fatal (_("read failed"));
- return c;
-}
-
-static void
-flush_line (void)
-{
- int c;
- while ((c = getchar ()) != '\n' && c != EOF)
- continue;
- if (ferror (stdin))
- perror_fatal (_("read failed"));
-}
-
-
-/* interpret an edit command */
-static bool
-edit (struct line_filter *left, char const *lname, lin lline, lin llen,
- struct line_filter *right, char const *rname, lin rline, lin rlen,
- FILE *outfile)
-{
- for (;;)
- {
- int cmd0 IF_LINT (= 0);
- int cmd1 IF_LINT (= 0);
- bool gotcmd = false;
-
- while (! gotcmd)
- {
- if (putchar ('%') != '%')
- perror_fatal (_("write failed"));
- ck_fflush (stdout);
-
- cmd0 = skip_white ();
- switch (cmd0)
- {
- case '1': case '2': case 'l': case 'r':
- case 's': case 'v': case 'q':
- if (skip_white () != '\n')
- {
- give_help ();
- flush_line ();
- continue;
- }
- gotcmd = true;
- break;
-
- case 'e':
- cmd1 = skip_white ();
- switch (cmd1)
- {
- case '1': case '2': case 'b': case 'd': case 'l': case 'r':
- if (skip_white () != '\n')
- {
- give_help ();
- flush_line ();
- continue;
- }
- gotcmd = true;
- break;
- case '\n':
- gotcmd = true;
- break;
- default:
- give_help ();
- flush_line ();
- continue;
- }
- break;
-
- case EOF:
- if (feof (stdin))
- {
- gotcmd = true;
- cmd0 = 'q';
- break;
- }
- FALLTHROUGH;
- default:
- flush_line ();
- FALLTHROUGH;
- case '\n':
- give_help ();
- continue;
- }
- }
-
- switch (cmd0)
- {
- case '1': case 'l':
- lf_copy (left, llen, outfile);
- lf_skip (right, rlen);
- return true;
- case '2': case 'r':
- lf_copy (right, rlen, outfile);
- lf_skip (left, llen);
- return true;
- case 's':
- suppress_common_lines = true;
- break;
- case 'v':
- suppress_common_lines = false;
- break;
- case 'q':
- return false;
- case 'e':
- {
- int fd;
-
- if (tmpname)
- tmp = fopen (tmpname, "w");
- else
- {
- if ((fd = temporary_file ()) < 0)
- perror_fatal ("mkstemp");
- tmp = fdopen (fd, "w");
- }
-
- if (! tmp)
- perror_fatal (tmpname);
-
- switch (cmd1)
- {
- case 'd':
- if (llen)
- {
- printint l1 = lline;
- printint l2 = lline + llen - 1;
- if (llen == 1)
- fprintf (tmp, "--- %s %"pI"d\n", lname, l1);
- else
- fprintf (tmp, "--- %s %"pI"d,%"pI"d\n", lname, l1, l2);
- }
- FALLTHROUGH;
- case '1': case 'b': case 'l':
- lf_copy (left, llen, tmp);
- break;
-
- default:
- lf_skip (left, llen);
- break;
- }
-
- switch (cmd1)
- {
- case 'd':
- if (rlen)
- {
- printint l1 = rline;
- printint l2 = rline + rlen - 1;
- if (rlen == 1)
- fprintf (tmp, "+++ %s %"pI"d\n", rname, l1);
- else
- fprintf (tmp, "+++ %s %"pI"d,%"pI"d\n", rname, l1, l2);
- }
- FALLTHROUGH;
- case '2': case 'b': case 'r':
- lf_copy (right, rlen, tmp);
- break;
-
- default:
- lf_skip (right, rlen);
- break;
- }
-
- ck_fclose (tmp);
-
- {
- int wstatus;
- int werrno = 0;
- char const *argv[3];
-
- ignore_SIGINT = true;
- checksigs ();
- argv[0] = editor_program;
- argv[1] = tmpname;
- argv[2] = 0;
-
- {
-#if ! HAVE_WORKING_FORK
- char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
- wstatus = system (command);
- if (wstatus == -1)
- werrno = errno;
- free (command);
-#else
- pid_t pid;
-
- pid = fork ();
- if (pid == 0)
- {
- execvp (editor_program, (char **) argv);
- _exit (errno == ENOENT ? 127 : 126);
- }
-
- if (pid < 0)
- perror_fatal ("fork");
-
- while (waitpid (pid, &wstatus, 0) < 0)
- if (errno == EINTR)
- checksigs ();
- else
- perror_fatal ("waitpid");
-#endif
- }
-
- ignore_SIGINT = false;
- check_child_status (werrno, wstatus, EXIT_SUCCESS,
- editor_program);
- }
-
- {
- char buf[SDIFF_BUFSIZE];
- size_t size;
- tmp = ck_fopen (tmpname, "r");
- while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
- {
- checksigs ();
- ck_fwrite (buf, size, outfile);
- }
- ck_fclose (tmp);
- }
- return true;
- }
- default:
- give_help ();
- break;
- }
- }
-}
-
-/* Alternately reveal bursts of diff output and handle user commands. */
-static bool
-interact (struct line_filter *diff,
- struct line_filter *left, char const *lname,
- struct line_filter *right, char const *rname,
- FILE *outfile)
-{
- lin lline = 1, rline = 1;
-
- for (;;)
- {
- char diff_help[256];
- int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
-
- if (snarfed <= 0)
- return snarfed != 0;
-
- checksigs ();
-
- if (diff_help[0] == ' ')
- puts (diff_help + 1);
- else
- {
- char *numend;
- uintmax_t val;
- lin llen, rlen, lenmax;
- errno = 0;
- val = strtoumax (diff_help + 1, &numend, 10);
- if (LIN_MAX < val || errno || *numend != ',')
- fatal (diff_help);
- llen = val;
- val = strtoumax (numend + 1, &numend, 10);
- if (LIN_MAX < val || errno || *numend)
- fatal (diff_help);
- rlen = val;
-
- lenmax = MAX (llen, rlen);
-
- switch (diff_help[0])
- {
- case 'i':
- if (suppress_common_lines)
- lf_skip (diff, lenmax);
- else
- lf_copy (diff, lenmax, stdout);
-
- lf_copy (left, llen, outfile);
- lf_skip (right, rlen);
- break;
-
- case 'c':
- lf_copy (diff, lenmax, stdout);
- if (! edit (left, lname, lline, llen,
- right, rname, rline, rlen,
- outfile))
- return false;
- break;
-
- default:
- fatal (diff_help);
- }
-
- lline += llen;
- rline += rlen;
- }
- }
-}
-
-/* Return true if DIR is an existing directory. */
-static bool
-diraccess (char const *dir)
-{
- struct stat buf;
- return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
-}
-
-#ifndef P_tmpdir
-# define P_tmpdir "/tmp"
-#endif
-#ifndef TMPDIR_ENV
-# define TMPDIR_ENV "TMPDIR"
-#endif
-
-/* Open a temporary file and return its file descriptor. Put into
- tmpname the address of a newly allocated buffer that holds the
- file's name. Use the prefix "sdiff". */
-static int
-temporary_file (void)
-{
- char const *tmpdir = getenv (TMPDIR_ENV);
- char const *dir = tmpdir ? tmpdir : P_tmpdir;
- char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
- int fd;
- sprintf (buf, "%s/sdiffXXXXXX", dir);
- fd = mkstemp (buf);
- if (0 <= fd)
- tmpname = buf;
- return fd;
-}
diff --git a/src/util.c b/src/util.c
index 3de19f8..88955da 100644
--- a/src/util.c
+++ b/src/util.c
@@ -985,8 +985,7 @@ finish_output (void)
Return nonzero if the lines differ. */
bool
-lines_differ_singlebyte (char const *s1, size_t s1len,
- char const *s2, size_t s2len)
+lines_differ (char const *s1, char const *s2)
{
register char const *t1 = s1;
register char const *t2 = s2;
@@ -1142,354 +1141,6 @@ lines_differ_singlebyte (char const *s1, size_t s1len,
return true;
}
-
-#ifdef HANDLE_MULTIBYTE
-# define MBC2WC(T, END, MBLENGTH, WC, STATE, CONVFAIL) \
-do \
- { \
- mbstate_t bak = STATE; \
- \
- CONVFAIL = 0; \
- MBLENGTH = mbrtowc (&WC, T, END - T, &STATE); \
- \
- switch (MBLENGTH) \
- { \
- case (size_t)-2: \
- case (size_t)-1: \
- STATE = bak; \
- ++CONVFAIL; \
- /* Fall through. */ \
- case 0: \
- MBLENGTH = 1; \
- } \
- } \
- while (0)
-
-bool
-lines_differ_multibyte (char const *s1, size_t s1len,
- char const *s2, size_t s2len)
-{
- char const *end1, *end2;
- char c1, c2;
- wchar_t wc1, wc2, wc1_bak, wc2_bak;
- size_t mblen1, mblen2;
- mbstate_t state1, state2, state1_bak, state2_bak;
- int convfail1, convfail2, convfail1_bak, convfail2_bak;
-
- char const *t1 = s1;
- char const *t2 = s2;
- char const *t1_bak, *t2_bak;
- size_t column = 0;
-
- if (ignore_white_space == IGNORE_NO_WHITE_SPACE && !ignore_case)
- {
- while (*t1 != '\n')
- if (*t1++ != *t2++)
- return 1;
- return 0;
- }
-
- end1 = t1 + s1len;
- end2 = t2 + s2len;
-
- memset (&state1, '\0', sizeof (mbstate_t));
- memset (&state2, '\0', sizeof (mbstate_t));
-
- while (1)
- {
- c1 = *t1;
- c2 = *t2;
- MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
- MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
-
- /* Test for exact char equality first, since it's a common case. */
- if (convfail1 ^ convfail2)
- break;
- else if (convfail1 && convfail2 && c1 != c2)
- break;
- else if (!convfail1 && !convfail2 && wc1 != wc2)
- {
- switch (ignore_white_space)
- {
- case IGNORE_ALL_SPACE:
- /* For -w, just skip past any white space. */
- while (1)
- {
- if (convfail1)
- break;
- else if (wc1 == L'\n' || !iswspace (wc1))
- break;
-
- t1 += mblen1;
- c1 = *t1;
- MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
- }
-
- while (1)
- {
- if (convfail2)
- break;
- else if (wc2 == L'\n' || !iswspace (wc2))
- break;
-
- t2 += mblen2;
- c2 = *t2;
- MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
- }
- t1 += mblen1;
- t2 += mblen2;
- break;
-
- case IGNORE_SPACE_CHANGE:
- /* For -b, advance past any sequence of white space in
- line 1 and consider it just one space, or nothing at
- all if it is at the end of the line. */
- if (wc1 != L'\n' && iswspace (wc1))
- {
- size_t mblen_bak;
- mbstate_t state_bak;
-
- do
- {
- t1 += mblen1;
- mblen_bak = mblen1;
- state_bak = state1;
- MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
- }
- while (!convfail1 && (wc1 != L'\n' && iswspace (wc1)));
-
- state1 = state_bak;
- mblen1 = mblen_bak;
- t1 -= mblen1;
- convfail1 = 0;
- wc1 = L' ';
- }
-
- /* Likewise for line 2. */
- if (wc2 != L'\n' && iswspace (wc2))
- {
- size_t mblen_bak;
- mbstate_t state_bak;
-
- do
- {
- t2 += mblen2;
- mblen_bak = mblen2;
- state_bak = state2;
- MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
- }
- while (!convfail2 && (wc2 != L'\n' && iswspace (wc2)));
-
- state2 = state_bak;
- mblen2 = mblen_bak;
- t2 -= mblen2;
- convfail2 = 0;
- wc2 = L' ';
- }
-
- if (wc1 != wc2)
- {
- /* If we went too far when doing the simple test for
- equality, go back to the first non-whitespace
- character in both sides and try again. */
- if (wc2 == L' ' && wc1 != L'\n' &&
- t1 > s1 &&
- !convfail1_bak && iswspace (wc1_bak))
- {
- t1 = t1_bak;
- wc1 = wc1_bak;
- state1 = state1_bak;
- convfail1 = convfail1_bak;
- continue;
- }
- if (wc1 == L' ' && wc2 != L'\n'
- && t2 > s2
- && !convfail2_bak && iswspace (wc2_bak))
- {
- t2 = t2_bak;
- wc2 = wc2_bak;
- state2 = state2_bak;
- convfail2 = convfail2_bak;
- continue;
- }
- }
-
- t1_bak = t1; t2_bak = t2;
- wc1_bak = wc1; wc2_bak = wc2;
- state1_bak = state1; state2_bak = state2;
- convfail1_bak = convfail1; convfail2_bak = convfail2;
-
- if (wc1 == L'\n')
- wc1 = L' ';
- else
- t1 += mblen1;
-
- if (wc2 == L'\n')
- wc2 = L' ';
- else
- t2 += mblen2;
-
- break;
-
- case IGNORE_TRAILING_SPACE:
- case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
- if (iswspace (wc1) && iswspace (wc2))
- {
- char const *p;
- wchar_t wc;
- size_t mblength;
- int convfail;
- mbstate_t state;
- bool just_whitespace_left = 1;
- if (wc1 != L'\n')
- {
- mblength = mblen1;
- p = t1;
- memset (&state, '\0', sizeof(mbstate_t));
- while (p < end1)
- {
- if (*p == '\n')
- break;
-
- p += mblength;
- MBC2WC (p, end1, mblength, wc, state, convfail);
- if (convfail || !iswspace (wc))
- {
- just_whitespace_left = 0;
- break;
- }
- }
- }
- if (just_whitespace_left && wc2 != L'\n')
- {
- mblength = mblen2;
- p = t2;
- memset (&state, '\0', sizeof(mbstate_t));
- while (p < end2)
- {
- if (*p == '\n')
- break;
-
- p += mblength;
- MBC2WC (p, end2, mblength, wc, state, convfail);
- if (convfail || !iswspace (wc))
- {
- just_whitespace_left = 0;
- break;
- }
- }
- }
-
- if (just_whitespace_left)
- /* Both lines have nothing but whitespace left. */
- return false;
- }
-
- if (ignore_white_space == IGNORE_TRAILING_SPACE)
- break;
- /* Fall through. */
- case IGNORE_TAB_EXPANSION:
- if ((wc1 == L' ' && wc2 == L'\t')
- || (wc1 == L'\t' && wc2 == L' '))
- {
- size_t column2 = column;
-
- while (1)
- {
- if (convfail1)
- {
- ++t1;
- break;
- }
- else if (wc1 == L' ')
- column++;
- else if (wc1 == L'\t')
- column += tabsize - column % tabsize;
- else
- {
- t1 += mblen1;
- break;
- }
-
- t1 += mblen1;
- c1 = *t1;
- MBC2WC (t1, end1, mblen1, wc1, state1, convfail1);
- }
-
- while (1)
- {
- if (convfail2)
- {
- ++t2;
- break;
- }
- else if (wc2 == L' ')
- column2++;
- else if (wc2 == L'\t')
- column2 += tabsize - column2 % tabsize;
- else
- {
- t2 += mblen2;
- break;
- }
-
- t2 += mblen2;
- c2 = *t2;
- MBC2WC (t2, end2, mblen2, wc2, state2, convfail2);
- }
-
- if (column != column2)
- return 1;
- }
- else
- {
- t1 += mblen1;
- t2 += mblen2;
- }
- break;
-
- case IGNORE_NO_WHITE_SPACE:
- t1 += mblen1;
- t2 += mblen2;
- break;
- }
-
- /* Lowercase all letters if -i is specified. */
- if (ignore_case)
- {
- if (!convfail1)
- wc1 = towlower (wc1);
- if (!convfail2)
- wc2 = towlower (wc2);
- }
-
- if (convfail1 ^ convfail2)
- break;
- else if (convfail1 && convfail2 && c1 != c2)
- break;
- else if (!convfail1 && !convfail2 && wc1 != wc2)
- break;
- }
- else
- {
- t1_bak = t1; t2_bak = t2;
- wc1_bak = wc1; wc2_bak = wc2;
- state1_bak = state1; state2_bak = state2;
- convfail1_bak = convfail1; convfail2_bak = convfail2;
-
- t1 += mblen1; t2 += mblen2;
- }
-
- if (!convfail1 && wc1 == L'\n')
- return 0;
-
- column += convfail1 ? 1 :
- (wc1 == L'\t') ? tabsize - column % tabsize : wcwidth (wc1);
- }
-
- return 1;
-}
-#endif
/* Find the consecutive changes at the start of the script START.
Return the last link before the first gap. */
diff --git a/src/util.c.i18n b/src/util.c.i18n
deleted file mode 100644
index 88955da..0000000
--- a/src/util.c.i18n
+++ /dev/null
@@ -1,1576 +0,0 @@
-/* Support routines for GNU DIFF.
-
- Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
- 2015-2017 Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-#include "diff.h"
-#include "argmatch.h"
-#include "die.h"
-#include
-#include
-#include
-#include
-#include "xvasprintf.h"
-#include
-
-/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
- present. */
-#ifndef SA_NOCLDSTOP
-# define SA_NOCLDSTOP 0
-# define sigprocmask(How, Set, Oset) /* empty */
-# define sigset_t int
-# if ! HAVE_SIGINTERRUPT
-# define siginterrupt(sig, flag) /* empty */
-# endif
-#endif
-
-#ifndef SA_RESTART
-# define SA_RESTART 0
-#endif
-
-char const pr_program[] = PR_PROGRAM;
-
-/* Queue up one-line messages to be printed at the end,
- when -l is specified. Each message is recorded with a 'struct msg'. */
-
-struct msg
-{
- struct msg *next;
- char args[1]; /* Format + 4 args, each '\0' terminated, concatenated. */
-};
-
-/* Head of the chain of queues messages. */
-
-static struct msg *msg_chain;
-
-/* Tail of the chain of queues messages. */
-
-static struct msg **msg_chain_end = &msg_chain;
-
-/* Use when a system call returns non-zero status.
- NAME should normally be the file name. */
-
-void
-perror_with_name (char const *name)
-{
- error (0, errno, "%s", name);
-}
-
-/* Use when a system call returns non-zero status and that is fatal. */
-
-void
-pfatal_with_name (char const *name)
-{
- int e = errno;
- print_message_queue ();
- die (EXIT_TROUBLE, e, "%s", name);
-}
-
-/* Print an error message containing MSGID, then exit. */
-
-void
-fatal (char const *msgid)
-{
- print_message_queue ();
- die (EXIT_TROUBLE, 0, "%s", _(msgid));
-}
-
-/* Like printf, except if -l in effect then save the message and print later.
- This is used for things like "Only in ...". */
-
-void
-message (char const *format_msgid, char const *arg1, char const *arg2)
-{
- message5 (format_msgid, arg1, arg2, 0, 0);
-}
-
-void
-message5 (char const *format_msgid, char const *arg1, char const *arg2,
- char const *arg3, char const *arg4)
-{
- if (paginate)
- {
- char *p;
- char const *arg[5];
- int i;
- size_t size[5];
- size_t total_size = offsetof (struct msg, args);
- struct msg *new;
-
- arg[0] = format_msgid;
- arg[1] = arg1;
- arg[2] = arg2;
- arg[3] = arg3 ? arg3 : "";
- arg[4] = arg4 ? arg4 : "";
-
- for (i = 0; i < 5; i++)
- total_size += size[i] = strlen (arg[i]) + 1;
-
- new = xmalloc (total_size);
-
- for (i = 0, p = new->args; i < 5; p += size[i++])
- memcpy (p, arg[i], size[i]);
-
- *msg_chain_end = new;
- new->next = 0;
- msg_chain_end = &new->next;
- }
- else
- {
- if (sdiff_merge_assist)
- putchar (' ');
- printf (_(format_msgid), arg1, arg2, arg3, arg4);
- }
-}
-
-/* Output all the messages that were saved up by calls to 'message'. */
-
-void
-print_message_queue (void)
-{
- char const *arg[5];
- int i;
- struct msg *m = msg_chain;
-
- while (m)
- {
- struct msg *next = m->next;
- arg[0] = m->args;
- for (i = 0; i < 4; i++)
- arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
- printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
- free (m);
- m = next;
- }
-}
-
-/* The set of signals that are caught. */
-
-static sigset_t caught_signals;
-
-/* If nonzero, the value of the pending fatal signal. */
-
-static sig_atomic_t volatile interrupt_signal;
-
-/* A count of the number of pending stop signals that have been received. */
-
-static sig_atomic_t volatile stop_signal_count;
-
-/* An ordinary signal was received; arrange for the program to exit. */
-
-static void
-sighandler (int sig)
-{
- if (! SA_NOCLDSTOP)
- signal (sig, SIG_IGN);
- if (! interrupt_signal)
- interrupt_signal = sig;
-}
-
-/* A SIGTSTP was received; arrange for the program to suspend itself. */
-
-static void
-stophandler (int sig)
-{
- if (! SA_NOCLDSTOP)
- signal (sig, stophandler);
- if (! interrupt_signal)
- stop_signal_count++;
-}
-/* Process any pending signals. If signals are caught, this function
- should be called periodically. Ideally there should never be an
- unbounded amount of time when signals are not being processed.
- Signal handling can restore the default colors, so callers must
- immediately change colors after invoking this function. */
-
-static void
-process_signals (void)
-{
- while (interrupt_signal || stop_signal_count)
- {
- int sig;
- int stops;
- sigset_t oldset;
-
- set_color_context (RESET_CONTEXT);
- fflush (stdout);
-
- sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
-
- /* Reload interrupt_signal and stop_signal_count, in case a new
- signal was handled before sigprocmask took effect. */
- sig = interrupt_signal;
- stops = stop_signal_count;
-
- /* SIGTSTP is special, since the application can receive that signal
- more than once. In this case, don't set the signal handler to the
- default. Instead, just raise the uncatchable SIGSTOP. */
- if (stops)
- {
- stop_signal_count = stops - 1;
- sig = SIGSTOP;
- }
- else
- signal (sig, SIG_DFL);
-
- /* Exit or suspend the program. */
- raise (sig);
- sigprocmask (SIG_SETMASK, &oldset, NULL);
-
- /* If execution reaches here, then the program has been
- continued (after being suspended). */
- }
-}
-
-static void
-install_signal_handlers (void)
-{
- /* The signals that are trapped, and the number of such signals. */
- static int const sig[] =
- {
- /* This one is handled specially. */
- SIGTSTP,
-
- /* The usual suspects. */
- SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
-#ifdef SIGPOLL
- SIGPOLL,
-#endif
-#ifdef SIGPROF
- SIGPROF,
-#endif
-#ifdef SIGVTALRM
- SIGVTALRM,
-#endif
-#ifdef SIGXCPU
- SIGXCPU,
-#endif
-#ifdef SIGXFSZ
- SIGXFSZ,
-#endif
- };
- enum { nsigs = sizeof (sig) / sizeof *(sig) };
-
-#if ! SA_NOCLDSTOP
- bool caught_sig[nsigs];
-#endif
- {
- int j;
-#if SA_NOCLDSTOP
- struct sigaction act;
-
- sigemptyset (&caught_signals);
- for (j = 0; j < nsigs; j++)
- {
- sigaction (sig[j], NULL, &act);
- if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[j]);
- }
-
- act.sa_mask = caught_signals;
- act.sa_flags = SA_RESTART;
-
- for (j = 0; j < nsigs; j++)
- if (sigismember (&caught_signals, sig[j]))
- {
- act.sa_handler = sig[j] == SIGTSTP ? stophandler : sighandler;
- sigaction (sig[j], &act, NULL);
- }
-#else
- for (j = 0; j < nsigs; j++)
- {
- caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
- if (caught_sig[j])
- {
- signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
- siginterrupt (sig[j], 0);
- }
- }
-#endif
- }
-}
-
-static char const *current_name0;
-static char const *current_name1;
-static bool currently_recursive;
-static bool colors_enabled;
-
-static struct color_ext_type *color_ext_list = NULL;
-
-struct bin_str
- {
- size_t len; /* Number of bytes */
- const char *string; /* Pointer to the same */
- };
-
-struct color_ext_type
- {
- struct bin_str ext; /* The extension we're looking for */
- struct bin_str seq; /* The sequence to output when we do */
- struct color_ext_type *next; /* Next in list */
- };
-
-/* Parse a string as part of the --palette argument; this may involve
- decoding all kinds of escape characters. If equals_end is set an
- unescaped equal sign ends the string, otherwise only a : or \0
- does. Set *OUTPUT_COUNT to the number of bytes output. Return
- true if successful.
-
- The resulting string is *not* null-terminated, but may contain
- embedded nulls.
-
- Note that both dest and src are char **; on return they point to
- the first free byte after the array and the character that ended
- the input string, respectively. */
-
-static bool
-get_funky_string (char **dest, const char **src, bool equals_end,
- size_t *output_count)
-{
- char num; /* For numerical codes */
- size_t count; /* Something to count with */
- enum {
- ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
- } state;
- const char *p;
- char *q;
-
- p = *src; /* We don't want to double-indirect */
- q = *dest; /* the whole darn time. */
-
- count = 0; /* No characters counted in yet. */
- num = 0;
-
- state = ST_GND; /* Start in ground state. */
- while (state < ST_END)
- {
- switch (state)
- {
- case ST_GND: /* Ground state (no escapes) */
- switch (*p)
- {
- case ':':
- case '\0':
- state = ST_END; /* End of string */
- break;
- case '\\':
- state = ST_BACKSLASH; /* Backslash scape sequence */
- ++p;
- break;
- case '^':
- state = ST_CARET; /* Caret escape */
- ++p;
- break;
- case '=':
- if (equals_end)
- {
- state = ST_END; /* End */
- break;
- }
- FALLTHROUGH;
- default:
- *(q++) = *(p++);
- ++count;
- break;
- }
- break;
-
- case ST_BACKSLASH: /* Backslash escaped character */
- switch (*p)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- state = ST_OCTAL; /* Octal sequence */
- num = *p - '0';
- break;
- case 'x':
- case 'X':
- state = ST_HEX; /* Hex sequence */
- num = 0;
- break;
- case 'a': /* Bell */
- num = '\a';
- break;
- case 'b': /* Backspace */
- num = '\b';
- break;
- case 'e': /* Escape */
- num = 27;
- break;
- case 'f': /* Form feed */
- num = '\f';
- break;
- case 'n': /* Newline */
- num = '\n';
- break;
- case 'r': /* Carriage return */
- num = '\r';
- break;
- case 't': /* Tab */
- num = '\t';
- break;
- case 'v': /* Vtab */
- num = '\v';
- break;
- case '?': /* Delete */
- num = 127;
- break;
- case '_': /* Space */
- num = ' ';
- break;
- case '\0': /* End of string */
- state = ST_ERROR; /* Error! */
- break;
- default: /* Escaped character like \ ^ : = */
- num = *p;
- break;
- }
- if (state == ST_BACKSLASH)
- {
- *(q++) = num;
- ++count;
- state = ST_GND;
- }
- ++p;
- break;
-
- case ST_OCTAL: /* Octal sequence */
- if (*p < '0' || *p > '7')
- {
- *(q++) = num;
- ++count;
- state = ST_GND;
- }
- else
- num = (num << 3) + (*(p++) - '0');
- break;
-
- case ST_HEX: /* Hex sequence */
- switch (*p)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- num = (num << 4) + (*(p++) - '0');
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- num = (num << 4) + (*(p++) - 'a') + 10;
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- num = (num << 4) + (*(p++) - 'A') + 10;
- break;
- default:
- *(q++) = num;
- ++count;
- state = ST_GND;
- break;
- }
- break;
-
- case ST_CARET: /* Caret escape */
- state = ST_GND; /* Should be the next state... */
- if (*p >= '@' && *p <= '~')
- {
- *(q++) = *(p++) & 037;
- ++count;
- }
- else if (*p == '?')
- {
- *(q++) = 127;
- ++count;
- }
- else
- state = ST_ERROR;
- break;
-
- default:
- abort ();
- }
- }
-
- *dest = q;
- *src = p;
- *output_count = count;
-
- return state != ST_ERROR;
-}
-
-enum parse_state
- {
- PS_START = 1,
- PS_2,
- PS_3,
- PS_4,
- PS_DONE,
- PS_FAIL
- };
-
-#define LEN_STR_PAIR(s) sizeof (s) - 1, s
-
-static struct bin_str color_indicator[] =
- {
- { LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
- { LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
- { 0, NULL }, /* ec: End color (replaces lc+rs+rc) */
- { LEN_STR_PAIR ("0") }, /* rs: Reset to ordinary colors */
- { LEN_STR_PAIR ("1") }, /* hd: Header */
- { LEN_STR_PAIR ("32") }, /* ad: Add line */
- { LEN_STR_PAIR ("31") }, /* de: Delete line */
- { LEN_STR_PAIR ("36") }, /* ln: Line number */
- };
-
-static const char *const indicator_name[] =
- {
- "lc", "rc", "ec", "rs", "hd", "ad", "de", "ln", NULL
- };
-ARGMATCH_VERIFY (indicator_name, color_indicator);
-
-static char const *color_palette;
-
-void
-set_color_palette (char const *palette)
-{
- color_palette = palette;
-}
-
-static void
-parse_diff_color (void)
-{
- char *color_buf;
- const char *p; /* Pointer to character being parsed */
- char *buf; /* color_buf buffer pointer */
- int ind_no; /* Indicator number */
- char label[3]; /* Indicator label */
- struct color_ext_type *ext; /* Extension we are working on */
-
- if ((p = color_palette) == NULL || *p == '\0')
- return;
-
- ext = NULL;
- strcpy (label, "??");
-
- /* This is an overly conservative estimate, but any possible
- --palette string will *not* generate a color_buf longer than
- itself, so it is a safe way of allocating a buffer in
- advance. */
- buf = color_buf = xstrdup (p);
-
- enum parse_state state = PS_START;
- while (true)
- {
- switch (state)
- {
- case PS_START: /* First label character */
- switch (*p)
- {
- case ':':
- ++p;
- break;
-
- case '*':
- /* Allocate new extension block and add to head of
- linked list (this way a later definition will
- override an earlier one, which can be useful for
- having terminal-specific defs override global). */
-
- ext = xmalloc (sizeof *ext);
- ext->next = color_ext_list;
- color_ext_list = ext;
-
- ++p;
- ext->ext.string = buf;
-
- state = (get_funky_string (&buf, &p, true, &ext->ext.len)
- ? PS_4 : PS_FAIL);
- break;
-
- case '\0':
- state = PS_DONE; /* Done! */
- goto done;
-
- default: /* Assume it is file type label */
- label[0] = *(p++);
- state = PS_2;
- break;
- }
- break;
-
- case PS_2: /* Second label character */
- if (*p)
- {
- label[1] = *(p++);
- state = PS_3;
- }
- else
- state = PS_FAIL; /* Error */
- break;
-
- case PS_3: /* Equal sign after indicator label */
- state = PS_FAIL; /* Assume failure... */
- if (*(p++) == '=')/* It *should* be... */
- {
- for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
- {
- if (STREQ (label, indicator_name[ind_no]))
- {
- color_indicator[ind_no].string = buf;
- state = (get_funky_string (&buf, &p, false,
- &color_indicator[ind_no].len)
- ? PS_START : PS_FAIL);
- break;
- }
- }
- if (state == PS_FAIL)
- error (0, 0, _("unrecognized prefix: %s"), label);
- }
- break;
-
- case PS_4: /* Equal sign after *.ext */
- if (*(p++) == '=')
- {
- ext->seq.string = buf;
- state = (get_funky_string (&buf, &p, false, &ext->seq.len)
- ? PS_START : PS_FAIL);
- }
- else
- state = PS_FAIL;
- break;
-
- case PS_FAIL:
- goto done;
-
- default:
- abort ();
- }
- }
- done:
-
- if (state == PS_FAIL)
- {
- struct color_ext_type *e;
- struct color_ext_type *e2;
-
- error (0, 0,
- _("unparsable value for --palette"));
- free (color_buf);
- for (e = color_ext_list; e != NULL; /* empty */)
- {
- e2 = e;
- e = e->next;
- free (e2);
- }
- colors_enabled = false;
- }
-}
-
-static void
-check_color_output (bool is_pipe)
-{
- bool output_is_tty;
-
- if (! outfile || colors_style == NEVER)
- return;
-
- output_is_tty = presume_output_tty || (!is_pipe && isatty (fileno (outfile)));
-
- colors_enabled = (colors_style == ALWAYS
- || (colors_style == AUTO && output_is_tty));
-
- if (colors_enabled)
- parse_diff_color ();
-
- if (output_is_tty)
- install_signal_handlers ();
-}
-
-/* Call before outputting the results of comparing files NAME0 and NAME1
- to set up OUTFILE, the stdio stream for the output to go to.
-
- Usually, OUTFILE is just stdout. But when -l was specified
- we fork off a 'pr' and make OUTFILE a pipe to it.
- 'pr' then outputs to our stdout. */
-
-void
-setup_output (char const *name0, char const *name1, bool recursive)
-{
- current_name0 = name0;
- current_name1 = name1;
- currently_recursive = recursive;
- outfile = 0;
-}
-
-#if HAVE_WORKING_FORK
-static pid_t pr_pid;
-#endif
-
-static char c_escape_char (char c)
-{
- switch (c) {
- case '\a': return 'a';
- case '\b': return 'b';
- case '\t': return 't';
- case '\n': return 'n';
- case '\v': return 'v';
- case '\f': return 'f';
- case '\r': return 'r';
- case '"': return '"';
- case '\\': return '\\';
- default:
- return c < 32;
- }
-}
-
-static char *
-c_escape (char const *str)
-{
- char const *s;
- size_t plus = 0;
- bool must_quote = false;
-
- for (s = str; *s; s++)
- {
- char c = *s;
-
- if (c == ' ')
- {
- must_quote = true;
- continue;
- }
- switch (c_escape_char (*s))
- {
- case 1:
- plus += 3;
- /* fall through */
- case 0:
- break;
- default:
- plus++;
- break;
- }
- }
-
- if (must_quote || plus)
- {
- size_t s_len = s - str;
- char *buffer = xmalloc (s_len + plus + 3);
- char *b = buffer;
-
- *b++ = '"';
- for (s = str; *s; s++)
- {
- char c = *s;
- char escape = c_escape_char (c);
-
- switch (escape)
- {
- case 0:
- *b++ = c;
- break;
- case 1:
- *b++ = '\\';
- *b++ = ((c >> 6) & 03) + '0';
- *b++ = ((c >> 3) & 07) + '0';
- *b++ = ((c >> 0) & 07) + '0';
- break;
- default:
- *b++ = '\\';
- *b++ = escape;
- break;
- }
- }
- *b++ = '"';
- *b = 0;
- return buffer;
- }
-
- return (char *) str;
-}
-
-void
-begin_output (void)
-{
- char *names[2];
- char *name;
-
- if (outfile != 0)
- return;
-
- names[0] = c_escape (current_name0);
- names[1] = c_escape (current_name1);
-
- /* Construct the header of this piece of diff. */
- /* POSIX 1003.1-2001 specifies this format. But there are some bugs in
- the standard: it says that we must print only the last component
- of the pathnames, and it requires two spaces after "diff" if
- there are no options. These requirements are silly and do not
- match historical practice. */
- name = xasprintf ("diff%s %s %s", switch_string, names[0], names[1]);
-
- if (paginate)
- {
- char const *argv[4];
-
- if (fflush (stdout) != 0)
- pfatal_with_name (_("write failed"));
-
- argv[0] = pr_program;
- argv[1] = "-h";
- argv[2] = name;
- argv[3] = 0;
-
- /* Make OUTFILE a pipe to a subsidiary 'pr'. */
- {
-#if HAVE_WORKING_FORK
- int pipes[2];
-
- if (pipe (pipes) != 0)
- pfatal_with_name ("pipe");
-
- pr_pid = fork ();
- if (pr_pid < 0)
- pfatal_with_name ("fork");
-
- if (pr_pid == 0)
- {
- close (pipes[1]);
- if (pipes[0] != STDIN_FILENO)
- {
- if (dup2 (pipes[0], STDIN_FILENO) < 0)
- pfatal_with_name ("dup2");
- close (pipes[0]);
- }
-
- execv (pr_program, (char **) argv);
- _exit (errno == ENOENT ? 127 : 126);
- }
- else
- {
- close (pipes[0]);
- outfile = fdopen (pipes[1], "w");
- if (!outfile)
- pfatal_with_name ("fdopen");
- check_color_output (true);
- }
-#else
- char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
- errno = 0;
- outfile = popen (command, "w");
- if (!outfile)
- pfatal_with_name (command);
- check_color_output (true);
- free (command);
-#endif
- }
- }
- else
- {
-
- /* If -l was not specified, output the diff straight to 'stdout'. */
-
- outfile = stdout;
- check_color_output (false);
-
- /* If handling multiple files (because scanning a directory),
- print which files the following output is about. */
- if (currently_recursive)
- printf ("%s\n", name);
- }
-
- free (name);
-
- /* A special header is needed at the beginning of context output. */
- switch (output_style)
- {
- case OUTPUT_CONTEXT:
- print_context_header (files, (char const *const *)names, false);
- break;
-
- case OUTPUT_UNIFIED:
- print_context_header (files, (char const *const *)names, true);
- break;
-
- default:
- break;
- }
-
- if (names[0] != current_name0)
- free (names[0]);
- if (names[1] != current_name1)
- free (names[1]);
-}
-
-/* Call after the end of output of diffs for one file.
- Close OUTFILE and get rid of the 'pr' subfork. */
-
-void
-finish_output (void)
-{
- if (outfile != 0 && outfile != stdout)
- {
- int status;
- int wstatus;
- int werrno = 0;
- if (ferror (outfile))
- fatal ("write failed");
-#if ! HAVE_WORKING_FORK
- wstatus = pclose (outfile);
- if (wstatus == -1)
- werrno = errno;
-#else
- if (fclose (outfile) != 0)
- pfatal_with_name (_("write failed"));
- if (waitpid (pr_pid, &wstatus, 0) < 0)
- pfatal_with_name ("waitpid");
-#endif
- status = (! werrno && WIFEXITED (wstatus)
- ? WEXITSTATUS (wstatus)
- : INT_MAX);
- if (status)
- die (EXIT_TROUBLE, werrno,
- _(status == 126
- ? "subsidiary program '%s' could not be invoked"
- : status == 127
- ? "subsidiary program '%s' not found"
- : status == INT_MAX
- ? "subsidiary program '%s' failed"
- : "subsidiary program '%s' failed (exit status %d)"),
- pr_program, status);
- }
-
- outfile = 0;
-}
-
-/* Compare two lines (typically one from each input file)
- according to the command line options.
- For efficiency, this is invoked only when the lines do not match exactly
- but an option like -i might cause us to ignore the difference.
- Return nonzero if the lines differ. */
-
-bool
-lines_differ (char const *s1, char const *s2)
-{
- register char const *t1 = s1;
- register char const *t2 = s2;
- size_t column = 0;
-
- while (1)
- {
- register unsigned char c1 = *t1++;
- register unsigned char c2 = *t2++;
-
- /* Test for exact char equality first, since it's a common case. */
- if (c1 != c2)
- {
- switch (ignore_white_space)
- {
- case IGNORE_ALL_SPACE:
- /* For -w, just skip past any white space. */
- while (isspace (c1) && c1 != '\n') c1 = *t1++;
- while (isspace (c2) && c2 != '\n') c2 = *t2++;
- break;
-
- case IGNORE_SPACE_CHANGE:
- /* For -b, advance past any sequence of white space in
- line 1 and consider it just one space, or nothing at
- all if it is at the end of the line. */
- if (isspace (c1))
- {
- while (c1 != '\n')
- {
- c1 = *t1++;
- if (! isspace (c1))
- {
- --t1;
- c1 = ' ';
- break;
- }
- }
- }
-
- /* Likewise for line 2. */
- if (isspace (c2))
- {
- while (c2 != '\n')
- {
- c2 = *t2++;
- if (! isspace (c2))
- {
- --t2;
- c2 = ' ';
- break;
- }
- }
- }
-
- if (c1 != c2)
- {
- /* If we went too far when doing the simple test
- for equality, go back to the first non-white-space
- character in both sides and try again. */
- if (c2 == ' ' && c1 != '\n'
- && s1 + 1 < t1
- && isspace ((unsigned char) t1[-2]))
- {
- --t1;
- continue;
- }
- if (c1 == ' ' && c2 != '\n'
- && s2 + 1 < t2
- && isspace ((unsigned char) t2[-2]))
- {
- --t2;
- continue;
- }
- }
-
- break;
-
- case IGNORE_TRAILING_SPACE:
- case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
- if (isspace (c1) && isspace (c2))
- {
- unsigned char c;
- if (c1 != '\n')
- {
- char const *p = t1;
- while ((c = *p) != '\n' && isspace (c))
- ++p;
- if (c != '\n')
- break;
- }
- if (c2 != '\n')
- {
- char const *p = t2;
- while ((c = *p) != '\n' && isspace (c))
- ++p;
- if (c != '\n')
- break;
- }
- /* Both lines have nothing but whitespace left. */
- return false;
- }
- if (ignore_white_space == IGNORE_TRAILING_SPACE)
- break;
- FALLTHROUGH;
- case IGNORE_TAB_EXPANSION:
- if ((c1 == ' ' && c2 == '\t')
- || (c1 == '\t' && c2 == ' '))
- {
- size_t column2 = column;
- for (;; c1 = *t1++)
- {
- if (c1 == ' ')
- column++;
- else if (c1 == '\t')
- column += tabsize - column % tabsize;
- else
- break;
- }
- for (;; c2 = *t2++)
- {
- if (c2 == ' ')
- column2++;
- else if (c2 == '\t')
- column2 += tabsize - column2 % tabsize;
- else
- break;
- }
- if (column != column2)
- return true;
- }
- break;
-
- case IGNORE_NO_WHITE_SPACE:
- break;
- }
-
- /* Lowercase all letters if -i is specified. */
-
- if (ignore_case)
- {
- c1 = tolower (c1);
- c2 = tolower (c2);
- }
-
- if (c1 != c2)
- break;
- }
- if (c1 == '\n')
- return false;
-
- column += c1 == '\t' ? tabsize - column % tabsize : 1;
- }
-
- return true;
-}
-
-/* Find the consecutive changes at the start of the script START.
- Return the last link before the first gap. */
-
-struct change * _GL_ATTRIBUTE_CONST
-find_change (struct change *start)
-{
- return start;
-}
-
-struct change * _GL_ATTRIBUTE_CONST
-find_reverse_change (struct change *start)
-{
- return start;
-}
-
-/* Divide SCRIPT into pieces by calling HUNKFUN and
- print each piece with PRINTFUN.
- Both functions take one arg, an edit script.
-
- HUNKFUN is called with the tail of the script
- and returns the last link that belongs together with the start
- of the tail.
-
- PRINTFUN takes a subscript which belongs together (with a null
- link at the end) and prints it. */
-
-void
-print_script (struct change *script,
- struct change * (*hunkfun) (struct change *),
- void (*printfun) (struct change *))
-{
- struct change *next = script;
-
- while (next)
- {
- struct change *this, *end;
-
- /* Find a set of changes that belong together. */
- this = next;
- end = (*hunkfun) (next);
-
- /* Disconnect them from the rest of the changes,
- making them a hunk, and remember the rest for next iteration. */
- next = end->link;
- end->link = 0;
-#ifdef DEBUG
- debug_script (this);
-#endif
-
- /* Print this hunk. */
- (*printfun) (this);
-
- /* Reconnect the script so it will all be freed properly. */
- end->link = next;
- }
-}
-
-/* Print the text of a single line LINE,
- flagging it with the characters in LINE_FLAG (which say whether
- the line is inserted, deleted, changed, etc.). LINE_FLAG must not
- end in a blank, unless it is a single blank. */
-
-void
-print_1_line (char const *line_flag, char const *const *line)
-{
- print_1_line_nl (line_flag, line, false);
-}
-
-/* Print the text of a single line LINE,
- flagging it with the characters in LINE_FLAG (which say whether
- the line is inserted, deleted, changed, etc.). LINE_FLAG must not
- end in a blank, unless it is a single blank. If SKIP_NL is set, then
- the final '\n' is not printed. */
-
-void
-print_1_line_nl (char const *line_flag, char const *const *line, bool skip_nl)
-{
- char const *base = line[0], *limit = line[1]; /* Help the compiler. */
- FILE *out = outfile; /* Help the compiler some more. */
- char const *flag_format = 0;
-
- /* If -T was specified, use a Tab between the line-flag and the text.
- Otherwise use a Space (as Unix diff does).
- Print neither space nor tab if line-flags are empty.
- But omit trailing blanks if requested. */
-
- if (line_flag && *line_flag)
- {
- char const *flag_format_1 = flag_format = initial_tab ? "%s\t" : "%s ";
- char const *line_flag_1 = line_flag;
-
- if (suppress_blank_empty && **line == '\n')
- {
- flag_format_1 = "%s";
-
- /* This hack to omit trailing blanks takes advantage of the
- fact that the only way that LINE_FLAG can end in a blank
- is when LINE_FLAG consists of a single blank. */
- line_flag_1 += *line_flag_1 == ' ';
- }
-
- fprintf (out, flag_format_1, line_flag_1);
- }
-
- output_1_line (base, limit - (skip_nl && limit[-1] == '\n'), flag_format, line_flag);
-
- if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
- {
- set_color_context (RESET_CONTEXT);
- fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
- }
-}
-
-/* Output a line from BASE up to LIMIT.
- With -t, expand white space characters to spaces, and if FLAG_FORMAT
- is nonzero, output it with argument LINE_FLAG after every
- internal carriage return, so that tab stops continue to line up. */
-
-void
-output_1_line (char const *base, char const *limit, char const *flag_format,
- char const *line_flag)
-{
- const size_t MAX_CHUNK = 1024;
- if (!expand_tabs)
- {
- size_t left = limit - base;
- while (left)
- {
- size_t to_write = MIN (left, MAX_CHUNK);
- size_t written = fwrite (base, sizeof (char), to_write, outfile);
- if (written < to_write)
- return;
- base += written;
- left -= written;
- process_signals ();
- }
- }
- else
- {
- register FILE *out = outfile;
- register unsigned char c;
- register char const *t = base;
- register size_t column = 0;
- size_t tab_size = tabsize;
- size_t counter_proc_signals = 0;
-
- while (t < limit)
- {
- counter_proc_signals++;
- if (counter_proc_signals == MAX_CHUNK)
- {
- process_signals ();
- counter_proc_signals = 0;
- }
-
- switch ((c = *t++))
- {
- case '\t':
- {
- size_t spaces = tab_size - column % tab_size;
- column += spaces;
- do
- putc (' ', out);
- while (--spaces);
- }
- break;
-
- case '\r':
- putc (c, out);
- if (flag_format && t < limit && *t != '\n')
- fprintf (out, flag_format, line_flag);
- column = 0;
- break;
-
- case '\b':
- if (column == 0)
- continue;
- column--;
- putc (c, out);
- break;
-
- default:
- column += isprint (c) != 0;
- putc (c, out);
- break;
- }
- }
- }
-}
-
-enum indicator_no
- {
- C_LEFT, C_RIGHT, C_END, C_RESET, C_HEADER, C_ADD, C_DELETE, C_LINE
- };
-
-static void
-put_indicator (const struct bin_str *ind)
-{
- fwrite (ind->string, ind->len, 1, outfile);
-}
-
-static enum color_context last_context = RESET_CONTEXT;
-
-void
-set_color_context (enum color_context color_context)
-{
- if (color_context != RESET_CONTEXT)
- process_signals ();
- if (colors_enabled && last_context != color_context)
- {
- put_indicator (&color_indicator[C_LEFT]);
- switch (color_context)
- {
- case HEADER_CONTEXT:
- put_indicator (&color_indicator[C_HEADER]);
- break;
-
- case LINE_NUMBER_CONTEXT:
- put_indicator (&color_indicator[C_LINE]);
- break;
-
- case ADD_CONTEXT:
- put_indicator (&color_indicator[C_ADD]);
- break;
-
- case DELETE_CONTEXT:
- put_indicator (&color_indicator[C_DELETE]);
- break;
-
- case RESET_CONTEXT:
- put_indicator (&color_indicator[C_RESET]);
- break;
-
- default:
- abort ();
- }
- put_indicator (&color_indicator[C_RIGHT]);
- last_context = color_context;
- }
-}
-
-
-char const change_letter[] = { 0, 'd', 'a', 'c' };
-
-/* Translate an internal line number (an index into diff's table of lines)
- into an actual line number in the input file.
- The internal line number is I. FILE points to the data on the file.
-
- Internal line numbers count from 0 starting after the prefix.
- Actual line numbers count from 1 within the entire file. */
-
-lin _GL_ATTRIBUTE_PURE
-translate_line_number (struct file_data const *file, lin i)
-{
- return i + file->prefix_lines + 1;
-}
-
-/* Translate a line number range. This is always done for printing,
- so for convenience translate to printint rather than lin, so that the
- caller can use printf with "%"pI"d" without casting. */
-
-void
-translate_range (struct file_data const *file,
- lin a, lin b,
- printint *aptr, printint *bptr)
-{
- *aptr = translate_line_number (file, a - 1) + 1;
- *bptr = translate_line_number (file, b + 1) - 1;
-}
-
-/* Print a pair of line numbers with SEPCHAR, translated for file FILE.
- If the two numbers are identical, print just one number.
-
- Args A and B are internal line numbers.
- We print the translated (real) line numbers. */
-
-void
-print_number_range (char sepchar, struct file_data *file, lin a, lin b)
-{
- printint trans_a, trans_b;
- translate_range (file, a, b, &trans_a, &trans_b);
-
- /* Note: we can have B < A in the case of a range of no lines.
- In this case, we should print the line number before the range,
- which is B. */
- if (trans_b > trans_a)
- fprintf (outfile, "%"pI"d%c%"pI"d", trans_a, sepchar, trans_b);
- else
- fprintf (outfile, "%"pI"d", trans_b);
-}
-
-/* Look at a hunk of edit script and report the range of lines in each file
- that it applies to. HUNK is the start of the hunk, which is a chain
- of 'struct change'. The first and last line numbers of file 0 are stored in
- *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
- Note that these are internal line numbers that count from 0.
-
- If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
-
- Return UNCHANGED if only ignorable lines are inserted or deleted,
- OLD if lines of file 0 are deleted,
- NEW if lines of file 1 are inserted,
- and CHANGED if both kinds of changes are found. */
-
-enum changes
-analyze_hunk (struct change *hunk,
- lin *first0, lin *last0,
- lin *first1, lin *last1)
-{
- struct change *next;
- lin l0, l1;
- lin show_from, show_to;
- lin i;
- bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
- size_t trivial_length = ignore_blank_lines - 1;
- /* If 0, ignore zero-length lines;
- if SIZE_MAX, do not ignore lines just because of their length. */
-
- bool skip_white_space =
- ignore_blank_lines && IGNORE_TRAILING_SPACE <= ignore_white_space;
- bool skip_leading_white_space =
- skip_white_space && IGNORE_SPACE_CHANGE <= ignore_white_space;
-
- char const * const *linbuf0 = files[0].linbuf; /* Help the compiler. */
- char const * const *linbuf1 = files[1].linbuf;
-
- show_from = show_to = 0;
-
- *first0 = hunk->line0;
- *first1 = hunk->line1;
-
- next = hunk;
- do
- {
- l0 = next->line0 + next->deleted - 1;
- l1 = next->line1 + next->inserted - 1;
- show_from += next->deleted;
- show_to += next->inserted;
-
- for (i = next->line0; i <= l0 && trivial; i++)
- {
- char const *line = linbuf0[i];
- char const *lastbyte = linbuf0[i + 1] - 1;
- char const *newline = lastbyte + (*lastbyte != '\n');
- size_t len = newline - line;
- char const *p = line;
- if (skip_white_space)
- for (; *p != '\n'; p++)
- if (! isspace ((unsigned char) *p))
- {
- if (! skip_leading_white_space)
- p = line;
- break;
- }
- if (newline - p != trivial_length
- && (! ignore_regexp.fastmap
- || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
- trivial = 0;
- }
-
- for (i = next->line1; i <= l1 && trivial; i++)
- {
- char const *line = linbuf1[i];
- char const *lastbyte = linbuf1[i + 1] - 1;
- char const *newline = lastbyte + (*lastbyte != '\n');
- size_t len = newline - line;
- char const *p = line;
- if (skip_white_space)
- for (; *p != '\n'; p++)
- if (! isspace ((unsigned char) *p))
- {
- if (! skip_leading_white_space)
- p = line;
- break;
- }
- if (newline - p != trivial_length
- && (! ignore_regexp.fastmap
- || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
- trivial = 0;
- }
- }
- while ((next = next->link) != 0);
-
- *last0 = l0;
- *last1 = l1;
-
- /* If all inserted or deleted lines are ignorable,
- tell the caller to ignore this hunk. */
-
- if (trivial)
- return UNCHANGED;
-
- return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
-}
-
-/* Concatenate three strings, returning a newly malloc'd string. */
-
-char *
-concat (char const *s1, char const *s2, char const *s3)
-{
- char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
- sprintf (new, "%s%s%s", s1, s2, s3);
- return new;
-}
-
-/* Yield a new block of SIZE bytes, initialized to zero. */
-
-void *
-zalloc (size_t size)
-{
- void *p = xmalloc (size);
- memset (p, 0, size);
- return p;
-}
-
-void
-debug_script (struct change *sp)
-{
- fflush (stdout);
-
- for (; sp; sp = sp->link)
- {
- printint line0 = sp->line0;
- printint line1 = sp->line1;
- printint deleted = sp->deleted;
- printint inserted = sp->inserted;
- fprintf (stderr, "%3"pI"d %3"pI"d delete %"pI"d insert %"pI"d\n",
- line0, line1, deleted, inserted);
- }
-
- fflush (stderr);
-}
diff --git a/tests/cmp b/tests/cmp
index 56946de..082a931 100755
--- a/tests/cmp
+++ b/tests/cmp
@@ -208,14 +208,4 @@ done >out1
compare exp1 out1 || fail=1
-printf 'bad\n' >bad
-printf 'bug\n' >bug
-echo LC_ALL=C cmp -b bad bug
-LC_ALL=C cmp -b bad bug
-test $? -eq 1 || fail=1
-case `LC_ALL=C cmp -b bad bug` in
- 'bad bug differ: byte 2, line 1 is '*' a '*' u') ;;
- *) echo 'expected cmp -b to report a and u'; fail=1;;
-esac
-
Exit $fail
diff --git a/tests/cmp.shows_incorrect_data b/tests/cmp.shows_incorrect_data
deleted file mode 100755
index 082a931..0000000
--- a/tests/cmp.shows_incorrect_data
+++ /dev/null
@@ -1,211 +0,0 @@
-#!/bin/sh
-# Test 'cmp'.
-
-# Copyright 2017 Free Software Foundation, Inc.
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-. "${srcdir=.}/init.sh"; path_prepend_ ../src
-
-fail=0
-
-cat <<'EOF' > exp || fail=1
-cmp a a
-0
-cmp a b
-a b differ: char 1, line 1
-1
-cmp a c
-cmp: EOF on c which is empty
-1
-cmp a d
-cmp: d: No such file or directory
-2
-cmp b a
-b a differ: char 1, line 1
-1
-cmp b b
-0
-cmp b c
-cmp: EOF on c which is empty
-1
-cmp b d
-cmp: d: No such file or directory
-2
-cmp c a
-cmp: EOF on c which is empty
-1
-cmp c b
-cmp: EOF on c which is empty
-1
-cmp c c
-0
-cmp c d
-cmp: d: No such file or directory
-2
-cmp d a
-cmp: d: No such file or directory
-2
-cmp d b
-cmp: d: No such file or directory
-2
-cmp d c
-cmp: d: No such file or directory
-2
-cmp d d
-cmp: d: No such file or directory
-2
-cmp -l a a
-0
-cmp -l a b
-1 141 142
-1
-cmp -l a c
-cmp: EOF on c which is empty
-1
-cmp -l a d
-cmp: d: No such file or directory
-2
-cmp -l b a
-1 142 141
-1
-cmp -l b b
-0
-cmp -l b c
-cmp: EOF on c which is empty
-1
-cmp -l b d
-cmp: d: No such file or directory
-2
-cmp -l c a
-cmp: EOF on c which is empty
-1
-cmp -l c b
-cmp: EOF on c which is empty
-1
-cmp -l c c
-0
-cmp -l c d
-cmp: d: No such file or directory
-2
-cmp -l d a
-cmp: d: No such file or directory
-2
-cmp -l d b
-cmp: d: No such file or directory
-2
-cmp -l d c
-cmp: d: No such file or directory
-2
-cmp -l d d
-cmp: d: No such file or directory
-2
-cmp -s a a
-0
-cmp -s a b
-1
-cmp -s a c
-1
-cmp -s a d
-2
-cmp -s b a
-1
-cmp -s b b
-0
-cmp -s b c
-1
-cmp -s b d
-2
-cmp -s c a
-1
-cmp -s c b
-1
-cmp -s c c
-0
-cmp -s c d
-2
-cmp -s d a
-2
-cmp -s d b
-2
-cmp -s d c
-2
-cmp -s d d
-2
-EOF
-
-echo a >a
-echo b >b
-: >c
-rm -f d
-
-for option in '' -l -s; do
- for i in a b c d; do
- for j in a b c d; do
- echo cmp $option $i $j
- cmp $option $i $j >stdout 2>stderr
- status=$?
- cat stderr stdout
- echo $status
- done
- done
-done >out
-
-compare exp out || fail=1
-
-cat <<'EOF' > exp1 || fail=1
-cmp a0 a1
-cmp: EOF on a0 which is empty
-1
-cmp a1 a2
-cmp: EOF on a1 after byte 2, line 1
-1
-cmp a2 a3
-cmp: EOF on a2 after byte 5, in line 2
-1
-cmp -l a0 a1
-cmp: EOF on a0 which is empty
-1
-cmp -l a1 a2
-cmp: EOF on a1 after byte 2
-1
-cmp -l a2 a3
-cmp: EOF on a2 after byte 5
-1
-cmp -s a0 a1
-1
-cmp -s a1 a2
-1
-cmp -s a2 a3
-1
-EOF
-
-printf '' >a0
-printf '1\n' >a1
-printf '1\nfoo' >a2
-printf '1\nfoolery\n' >a3
-
-for option in '' -l -s; do
- for files in 'a0 a1' 'a1 a2' 'a2 a3'; do
- echo cmp $option $files
- cmp $option $files >stdout 2>stderr
- status=$?
- cat stderr stdout
- echo $status
- done
-done >out1
-
-compare exp1 out1 || fail=1
-
-Exit $fail