From 3a92f25a7ebdec0cdc9d9e0d996ca5abca576621 Mon Sep 17 00:00:00 2001 From: Packit Service Date: Dec 10 2020 11:41:08 +0000 Subject: Prepare for a new update Reverting patches so we can apply the latest update and changes can be seen in the spec file and sources. --- 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