Blame src/util.c.i18n

Packit d91b79
/* Support routines for GNU DIFF.
Packit d91b79
Packit d91b79
   Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
Packit d91b79
   2015-2017 Free Software Foundation, Inc.
Packit d91b79
Packit d91b79
   This file is part of GNU DIFF.
Packit d91b79
Packit d91b79
   This program is free software: you can redistribute it and/or modify
Packit d91b79
   it under the terms of the GNU General Public License as published by
Packit d91b79
   the Free Software Foundation, either version 3 of the License, or
Packit d91b79
   (at your option) any later version.
Packit d91b79
Packit d91b79
   This program is distributed in the hope that it will be useful,
Packit d91b79
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d91b79
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit d91b79
   GNU General Public License for more details.
Packit d91b79
Packit d91b79
   You should have received a copy of the GNU General Public License
Packit d91b79
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit d91b79
Packit d91b79
#include "diff.h"
Packit d91b79
#include "argmatch.h"
Packit d91b79
#include "die.h"
Packit d91b79
#include <dirname.h>
Packit d91b79
#include <error.h>
Packit d91b79
#include <system-quote.h>
Packit d91b79
#include <xalloc.h>
Packit d91b79
#include "xvasprintf.h"
Packit d91b79
#include <signal.h>
Packit d91b79
Packit d91b79
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
Packit d91b79
   present.  */
Packit d91b79
#ifndef SA_NOCLDSTOP
Packit d91b79
# define SA_NOCLDSTOP 0
Packit d91b79
# define sigprocmask(How, Set, Oset) /* empty */
Packit d91b79
# define sigset_t int
Packit d91b79
# if ! HAVE_SIGINTERRUPT
Packit d91b79
#  define siginterrupt(sig, flag) /* empty */
Packit d91b79
# endif
Packit d91b79
#endif
Packit d91b79
Packit d91b79
#ifndef SA_RESTART
Packit d91b79
# define SA_RESTART 0
Packit d91b79
#endif
Packit d91b79
Packit d91b79
char const pr_program[] = PR_PROGRAM;
Packit d91b79
Packit d91b79
/* Queue up one-line messages to be printed at the end,
Packit d91b79
   when -l is specified.  Each message is recorded with a 'struct msg'.  */
Packit d91b79
Packit d91b79
struct msg
Packit d91b79
{
Packit d91b79
  struct msg *next;
Packit d91b79
  char args[1]; /* Format + 4 args, each '\0' terminated, concatenated.  */
Packit d91b79
};
Packit d91b79
Packit d91b79
/* Head of the chain of queues messages.  */
Packit d91b79
Packit d91b79
static struct msg *msg_chain;
Packit d91b79
Packit d91b79
/* Tail of the chain of queues messages.  */
Packit d91b79
Packit d91b79
static struct msg **msg_chain_end = &msg_chain;
Packit d91b79

Packit d91b79
/* Use when a system call returns non-zero status.
Packit d91b79
   NAME should normally be the file name.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
perror_with_name (char const *name)
Packit d91b79
{
Packit d91b79
  error (0, errno, "%s", name);
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Use when a system call returns non-zero status and that is fatal.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
pfatal_with_name (char const *name)
Packit d91b79
{
Packit d91b79
  int e = errno;
Packit d91b79
  print_message_queue ();
Packit d91b79
  die (EXIT_TROUBLE, e, "%s", name);
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Print an error message containing MSGID, then exit.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
fatal (char const *msgid)
Packit d91b79
{
Packit d91b79
  print_message_queue ();
Packit d91b79
  die (EXIT_TROUBLE, 0, "%s", _(msgid));
Packit d91b79
}
Packit d91b79

Packit d91b79
/* Like printf, except if -l in effect then save the message and print later.
Packit d91b79
   This is used for things like "Only in ...".  */
Packit d91b79
Packit d91b79
void
Packit d91b79
message (char const *format_msgid, char const *arg1, char const *arg2)
Packit d91b79
{
Packit d91b79
  message5 (format_msgid, arg1, arg2, 0, 0);
Packit d91b79
}
Packit d91b79
Packit d91b79
void
Packit d91b79
message5 (char const *format_msgid, char const *arg1, char const *arg2,
Packit d91b79
	  char const *arg3, char const *arg4)
Packit d91b79
{
Packit d91b79
  if (paginate)
Packit d91b79
    {
Packit d91b79
      char *p;
Packit d91b79
      char const *arg[5];
Packit d91b79
      int i;
Packit d91b79
      size_t size[5];
Packit d91b79
      size_t total_size = offsetof (struct msg, args);
Packit d91b79
      struct msg *new;
Packit d91b79
Packit d91b79
      arg[0] = format_msgid;
Packit d91b79
      arg[1] = arg1;
Packit d91b79
      arg[2] = arg2;
Packit d91b79
      arg[3] = arg3 ? arg3 : "";
Packit d91b79
      arg[4] = arg4 ? arg4 : "";
Packit d91b79
Packit d91b79
      for (i = 0;  i < 5;  i++)
Packit d91b79
	total_size += size[i] = strlen (arg[i]) + 1;
Packit d91b79
Packit d91b79
      new = xmalloc (total_size);
Packit d91b79
Packit d91b79
      for (i = 0, p = new->args;  i < 5;  p += size[i++])
Packit d91b79
	memcpy (p, arg[i], size[i]);
Packit d91b79
Packit d91b79
      *msg_chain_end = new;
Packit d91b79
      new->next = 0;
Packit d91b79
      msg_chain_end = &new->next;
Packit d91b79
    }
Packit d91b79
  else
Packit d91b79
    {
Packit d91b79
      if (sdiff_merge_assist)
Packit d91b79
	putchar (' ');
Packit d91b79
      printf (_(format_msgid), arg1, arg2, arg3, arg4);
Packit d91b79
    }
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Output all the messages that were saved up by calls to 'message'.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
print_message_queue (void)
Packit d91b79
{
Packit d91b79
  char const *arg[5];
Packit d91b79
  int i;
Packit d91b79
  struct msg *m = msg_chain;
Packit d91b79
Packit d91b79
  while (m)
Packit d91b79
    {
Packit d91b79
      struct msg *next = m->next;
Packit d91b79
      arg[0] = m->args;
Packit d91b79
      for (i = 0;  i < 4;  i++)
Packit d91b79
	arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
Packit d91b79
      printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
Packit d91b79
      free (m);
Packit d91b79
      m = next;
Packit d91b79
    }
Packit d91b79
}
Packit d91b79

Packit d91b79
/* The set of signals that are caught.  */
Packit d91b79
Packit d91b79
static sigset_t caught_signals;
Packit d91b79
Packit d91b79
/* If nonzero, the value of the pending fatal signal.  */
Packit d91b79
Packit d91b79
static sig_atomic_t volatile interrupt_signal;
Packit d91b79
Packit d91b79
/* A count of the number of pending stop signals that have been received.  */
Packit d91b79
Packit d91b79
static sig_atomic_t volatile stop_signal_count;
Packit d91b79
Packit d91b79
/* An ordinary signal was received; arrange for the program to exit.  */
Packit d91b79
Packit d91b79
static void
Packit d91b79
sighandler (int sig)
Packit d91b79
{
Packit d91b79
  if (! SA_NOCLDSTOP)
Packit d91b79
    signal (sig, SIG_IGN);
Packit d91b79
  if (! interrupt_signal)
Packit d91b79
    interrupt_signal = sig;
Packit d91b79
}
Packit d91b79
Packit d91b79
/* A SIGTSTP was received; arrange for the program to suspend itself.  */
Packit d91b79
Packit d91b79
static void
Packit d91b79
stophandler (int sig)
Packit d91b79
{
Packit d91b79
  if (! SA_NOCLDSTOP)
Packit d91b79
    signal (sig, stophandler);
Packit d91b79
  if (! interrupt_signal)
Packit d91b79
    stop_signal_count++;
Packit d91b79
}
Packit d91b79
/* Process any pending signals.  If signals are caught, this function
Packit d91b79
   should be called periodically.  Ideally there should never be an
Packit d91b79
   unbounded amount of time when signals are not being processed.
Packit d91b79
   Signal handling can restore the default colors, so callers must
Packit d91b79
   immediately change colors after invoking this function.  */
Packit d91b79
Packit d91b79
static void
Packit d91b79
process_signals (void)
Packit d91b79
{
Packit d91b79
  while (interrupt_signal || stop_signal_count)
Packit d91b79
    {
Packit d91b79
      int sig;
Packit d91b79
      int stops;
Packit d91b79
      sigset_t oldset;
Packit d91b79
Packit d91b79
      set_color_context (RESET_CONTEXT);
Packit d91b79
      fflush (stdout);
Packit d91b79
Packit d91b79
      sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
Packit d91b79
Packit d91b79
      /* Reload interrupt_signal and stop_signal_count, in case a new
Packit d91b79
         signal was handled before sigprocmask took effect.  */
Packit d91b79
      sig = interrupt_signal;
Packit d91b79
      stops = stop_signal_count;
Packit d91b79
Packit d91b79
      /* SIGTSTP is special, since the application can receive that signal
Packit d91b79
         more than once.  In this case, don't set the signal handler to the
Packit d91b79
         default.  Instead, just raise the uncatchable SIGSTOP.  */
Packit d91b79
      if (stops)
Packit d91b79
        {
Packit d91b79
          stop_signal_count = stops - 1;
Packit d91b79
          sig = SIGSTOP;
Packit d91b79
        }
Packit d91b79
      else
Packit d91b79
        signal (sig, SIG_DFL);
Packit d91b79
Packit d91b79
      /* Exit or suspend the program.  */
Packit d91b79
      raise (sig);
Packit d91b79
      sigprocmask (SIG_SETMASK, &oldset, NULL);
Packit d91b79
Packit d91b79
      /* If execution reaches here, then the program has been
Packit d91b79
         continued (after being suspended).  */
Packit d91b79
    }
Packit d91b79
}
Packit d91b79
Packit d91b79
static void
Packit d91b79
install_signal_handlers (void)
Packit d91b79
{
Packit d91b79
  /* The signals that are trapped, and the number of such signals.  */
Packit d91b79
  static int const sig[] =
Packit d91b79
    {
Packit d91b79
      /* This one is handled specially.  */
Packit d91b79
      SIGTSTP,
Packit d91b79
Packit d91b79
      /* The usual suspects.  */
Packit d91b79
      SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
Packit d91b79
#ifdef SIGPOLL
Packit d91b79
      SIGPOLL,
Packit d91b79
#endif
Packit d91b79
#ifdef SIGPROF
Packit d91b79
      SIGPROF,
Packit d91b79
#endif
Packit d91b79
#ifdef SIGVTALRM
Packit d91b79
      SIGVTALRM,
Packit d91b79
#endif
Packit d91b79
#ifdef SIGXCPU
Packit d91b79
      SIGXCPU,
Packit d91b79
#endif
Packit d91b79
#ifdef SIGXFSZ
Packit d91b79
      SIGXFSZ,
Packit d91b79
#endif
Packit d91b79
    };
Packit d91b79
  enum { nsigs = sizeof (sig) / sizeof *(sig) };
Packit d91b79
Packit d91b79
#if ! SA_NOCLDSTOP
Packit d91b79
  bool caught_sig[nsigs];
Packit d91b79
#endif
Packit d91b79
  {
Packit d91b79
    int j;
Packit d91b79
#if SA_NOCLDSTOP
Packit d91b79
    struct sigaction act;
Packit d91b79
Packit d91b79
    sigemptyset (&caught_signals);
Packit d91b79
    for (j = 0; j < nsigs; j++)
Packit d91b79
      {
Packit d91b79
        sigaction (sig[j], NULL, &act;;
Packit d91b79
        if (act.sa_handler != SIG_IGN)
Packit d91b79
          sigaddset (&caught_signals, sig[j]);
Packit d91b79
      }
Packit d91b79
Packit d91b79
    act.sa_mask = caught_signals;
Packit d91b79
    act.sa_flags = SA_RESTART;
Packit d91b79
Packit d91b79
    for (j = 0; j < nsigs; j++)
Packit d91b79
      if (sigismember (&caught_signals, sig[j]))
Packit d91b79
        {
Packit d91b79
          act.sa_handler = sig[j] == SIGTSTP ? stophandler : sighandler;
Packit d91b79
          sigaction (sig[j], &act, NULL);
Packit d91b79
        }
Packit d91b79
#else
Packit d91b79
    for (j = 0; j < nsigs; j++)
Packit d91b79
      {
Packit d91b79
        caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
Packit d91b79
        if (caught_sig[j])
Packit d91b79
          {
Packit d91b79
            signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
Packit d91b79
            siginterrupt (sig[j], 0);
Packit d91b79
          }
Packit d91b79
      }
Packit d91b79
#endif
Packit d91b79
    }
Packit d91b79
}
Packit d91b79
Packit d91b79
static char const *current_name0;
Packit d91b79
static char const *current_name1;
Packit d91b79
static bool currently_recursive;
Packit d91b79
static bool colors_enabled;
Packit d91b79
Packit d91b79
static struct color_ext_type *color_ext_list = NULL;
Packit d91b79
Packit d91b79
struct bin_str
Packit d91b79
  {
Packit d91b79
    size_t len;			/* Number of bytes */
Packit d91b79
    const char *string;		/* Pointer to the same */
Packit d91b79
  };
Packit d91b79
Packit d91b79
struct color_ext_type
Packit d91b79
  {
Packit d91b79
    struct bin_str ext;		/* The extension we're looking for */
Packit d91b79
    struct bin_str seq;		/* The sequence to output when we do */
Packit d91b79
    struct color_ext_type *next;	/* Next in list */
Packit d91b79
  };
Packit d91b79
Packit d91b79
/* Parse a string as part of the --palette argument; this may involve
Packit d91b79
   decoding all kinds of escape characters.  If equals_end is set an
Packit d91b79
   unescaped equal sign ends the string, otherwise only a : or \0
Packit d91b79
   does.  Set *OUTPUT_COUNT to the number of bytes output.  Return
Packit d91b79
   true if successful.
Packit d91b79
Packit d91b79
   The resulting string is *not* null-terminated, but may contain
Packit d91b79
   embedded nulls.
Packit d91b79
Packit d91b79
   Note that both dest and src are char **; on return they point to
Packit d91b79
   the first free byte after the array and the character that ended
Packit d91b79
   the input string, respectively.  */
Packit d91b79
Packit d91b79
static bool
Packit d91b79
get_funky_string (char **dest, const char **src, bool equals_end,
Packit d91b79
                  size_t *output_count)
Packit d91b79
{
Packit d91b79
  char num;			/* For numerical codes */
Packit d91b79
  size_t count;			/* Something to count with */
Packit d91b79
  enum {
Packit d91b79
    ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
Packit d91b79
  } state;
Packit d91b79
  const char *p;
Packit d91b79
  char *q;
Packit d91b79
Packit d91b79
  p = *src;			/* We don't want to double-indirect */
Packit d91b79
  q = *dest;			/* the whole darn time.  */
Packit d91b79
Packit d91b79
  count = 0;			/* No characters counted in yet.  */
Packit d91b79
  num = 0;
Packit d91b79
Packit d91b79
  state = ST_GND;		/* Start in ground state.  */
Packit d91b79
  while (state < ST_END)
Packit d91b79
    {
Packit d91b79
      switch (state)
Packit d91b79
        {
Packit d91b79
        case ST_GND:		/* Ground state (no escapes) */
Packit d91b79
          switch (*p)
Packit d91b79
            {
Packit d91b79
            case ':':
Packit d91b79
            case '\0':
Packit d91b79
              state = ST_END;	/* End of string */
Packit d91b79
              break;
Packit d91b79
            case '\\':
Packit d91b79
              state = ST_BACKSLASH; /* Backslash scape sequence */
Packit d91b79
              ++p;
Packit d91b79
              break;
Packit d91b79
            case '^':
Packit d91b79
              state = ST_CARET; /* Caret escape */
Packit d91b79
              ++p;
Packit d91b79
              break;
Packit d91b79
            case '=':
Packit d91b79
              if (equals_end)
Packit d91b79
                {
Packit d91b79
                  state = ST_END; /* End */
Packit d91b79
                  break;
Packit d91b79
                }
Packit d91b79
              FALLTHROUGH;
Packit d91b79
            default:
Packit d91b79
              *(q++) = *(p++);
Packit d91b79
              ++count;
Packit d91b79
              break;
Packit d91b79
            }
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case ST_BACKSLASH:	/* Backslash escaped character */
Packit d91b79
          switch (*p)
Packit d91b79
            {
Packit d91b79
            case '0':
Packit d91b79
            case '1':
Packit d91b79
            case '2':
Packit d91b79
            case '3':
Packit d91b79
            case '4':
Packit d91b79
            case '5':
Packit d91b79
            case '6':
Packit d91b79
            case '7':
Packit d91b79
              state = ST_OCTAL;	/* Octal sequence */
Packit d91b79
              num = *p - '0';
Packit d91b79
              break;
Packit d91b79
            case 'x':
Packit d91b79
            case 'X':
Packit d91b79
              state = ST_HEX;	/* Hex sequence */
Packit d91b79
              num = 0;
Packit d91b79
              break;
Packit d91b79
            case 'a':		/* Bell */
Packit d91b79
              num = '\a';
Packit d91b79
              break;
Packit d91b79
            case 'b':		/* Backspace */
Packit d91b79
              num = '\b';
Packit d91b79
              break;
Packit d91b79
            case 'e':		/* Escape */
Packit d91b79
              num = 27;
Packit d91b79
              break;
Packit d91b79
            case 'f':		/* Form feed */
Packit d91b79
              num = '\f';
Packit d91b79
              break;
Packit d91b79
            case 'n':		/* Newline */
Packit d91b79
              num = '\n';
Packit d91b79
              break;
Packit d91b79
            case 'r':		/* Carriage return */
Packit d91b79
              num = '\r';
Packit d91b79
              break;
Packit d91b79
            case 't':		/* Tab */
Packit d91b79
              num = '\t';
Packit d91b79
              break;
Packit d91b79
            case 'v':		/* Vtab */
Packit d91b79
              num = '\v';
Packit d91b79
              break;
Packit d91b79
            case '?':		/* Delete */
Packit d91b79
              num = 127;
Packit d91b79
              break;
Packit d91b79
            case '_':		/* Space */
Packit d91b79
              num = ' ';
Packit d91b79
              break;
Packit d91b79
            case '\0':		/* End of string */
Packit d91b79
              state = ST_ERROR;	/* Error! */
Packit d91b79
              break;
Packit d91b79
            default:		/* Escaped character like \ ^ : = */
Packit d91b79
              num = *p;
Packit d91b79
              break;
Packit d91b79
            }
Packit d91b79
          if (state == ST_BACKSLASH)
Packit d91b79
            {
Packit d91b79
              *(q++) = num;
Packit d91b79
              ++count;
Packit d91b79
              state = ST_GND;
Packit d91b79
            }
Packit d91b79
          ++p;
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case ST_OCTAL:		/* Octal sequence */
Packit d91b79
          if (*p < '0' || *p > '7')
Packit d91b79
            {
Packit d91b79
              *(q++) = num;
Packit d91b79
              ++count;
Packit d91b79
              state = ST_GND;
Packit d91b79
            }
Packit d91b79
          else
Packit d91b79
            num = (num << 3) + (*(p++) - '0');
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case ST_HEX:		/* Hex sequence */
Packit d91b79
          switch (*p)
Packit d91b79
            {
Packit d91b79
            case '0':
Packit d91b79
            case '1':
Packit d91b79
            case '2':
Packit d91b79
            case '3':
Packit d91b79
            case '4':
Packit d91b79
            case '5':
Packit d91b79
            case '6':
Packit d91b79
            case '7':
Packit d91b79
            case '8':
Packit d91b79
            case '9':
Packit d91b79
              num = (num << 4) + (*(p++) - '0');
Packit d91b79
              break;
Packit d91b79
            case 'a':
Packit d91b79
            case 'b':
Packit d91b79
            case 'c':
Packit d91b79
            case 'd':
Packit d91b79
            case 'e':
Packit d91b79
            case 'f':
Packit d91b79
              num = (num << 4) + (*(p++) - 'a') + 10;
Packit d91b79
              break;
Packit d91b79
            case 'A':
Packit d91b79
            case 'B':
Packit d91b79
            case 'C':
Packit d91b79
            case 'D':
Packit d91b79
            case 'E':
Packit d91b79
            case 'F':
Packit d91b79
              num = (num << 4) + (*(p++) - 'A') + 10;
Packit d91b79
              break;
Packit d91b79
            default:
Packit d91b79
              *(q++) = num;
Packit d91b79
              ++count;
Packit d91b79
              state = ST_GND;
Packit d91b79
              break;
Packit d91b79
            }
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case ST_CARET:		/* Caret escape */
Packit d91b79
          state = ST_GND;	/* Should be the next state... */
Packit d91b79
          if (*p >= '@' && *p <= '~')
Packit d91b79
            {
Packit d91b79
              *(q++) = *(p++) & 037;
Packit d91b79
              ++count;
Packit d91b79
            }
Packit d91b79
          else if (*p == '?')
Packit d91b79
            {
Packit d91b79
              *(q++) = 127;
Packit d91b79
              ++count;
Packit d91b79
            }
Packit d91b79
          else
Packit d91b79
            state = ST_ERROR;
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        default:
Packit d91b79
          abort ();
Packit d91b79
        }
Packit d91b79
    }
Packit d91b79
Packit d91b79
  *dest = q;
Packit d91b79
  *src = p;
Packit d91b79
  *output_count = count;
Packit d91b79
Packit d91b79
  return state != ST_ERROR;
Packit d91b79
}
Packit d91b79
Packit d91b79
enum parse_state
Packit d91b79
  {
Packit d91b79
    PS_START = 1,
Packit d91b79
    PS_2,
Packit d91b79
    PS_3,
Packit d91b79
    PS_4,
Packit d91b79
    PS_DONE,
Packit d91b79
    PS_FAIL
Packit d91b79
  };
Packit d91b79
Packit d91b79
#define LEN_STR_PAIR(s) sizeof (s) - 1, s
Packit d91b79
Packit d91b79
static struct bin_str color_indicator[] =
Packit d91b79
  {
Packit d91b79
    { LEN_STR_PAIR ("\033[") },		/* lc: Left of color sequence */
Packit d91b79
    { LEN_STR_PAIR ("m") },		/* rc: Right of color sequence */
Packit d91b79
    { 0, NULL },			/* ec: End color (replaces lc+rs+rc) */
Packit d91b79
    { LEN_STR_PAIR ("0") },		/* rs: Reset to ordinary colors */
Packit d91b79
    { LEN_STR_PAIR ("1") },		/* hd: Header */
Packit d91b79
    { LEN_STR_PAIR ("32") },		/* ad: Add line */
Packit d91b79
    { LEN_STR_PAIR ("31") },		/* de: Delete line */
Packit d91b79
    { LEN_STR_PAIR ("36") },		/* ln: Line number */
Packit d91b79
  };
Packit d91b79
Packit d91b79
static const char *const indicator_name[] =
Packit d91b79
  {
Packit d91b79
    "lc", "rc", "ec", "rs", "hd", "ad", "de", "ln", NULL
Packit d91b79
  };
Packit d91b79
ARGMATCH_VERIFY (indicator_name, color_indicator);
Packit d91b79
Packit d91b79
static char const *color_palette;
Packit d91b79
Packit d91b79
void
Packit d91b79
set_color_palette (char const *palette)
Packit d91b79
{
Packit d91b79
  color_palette = palette;
Packit d91b79
}
Packit d91b79
Packit d91b79
static void
Packit d91b79
parse_diff_color (void)
Packit d91b79
{
Packit d91b79
  char *color_buf;
Packit d91b79
  const char *p;		/* Pointer to character being parsed */
Packit d91b79
  char *buf;			/* color_buf buffer pointer */
Packit d91b79
  int ind_no;			/* Indicator number */
Packit d91b79
  char label[3];		/* Indicator label */
Packit d91b79
  struct color_ext_type *ext;	/* Extension we are working on */
Packit d91b79
Packit d91b79
  if ((p = color_palette) == NULL || *p == '\0')
Packit d91b79
    return;
Packit d91b79
Packit d91b79
  ext = NULL;
Packit d91b79
  strcpy (label, "??");
Packit d91b79
Packit d91b79
  /* This is an overly conservative estimate, but any possible
Packit d91b79
     --palette string will *not* generate a color_buf longer than
Packit d91b79
     itself, so it is a safe way of allocating a buffer in
Packit d91b79
     advance.  */
Packit d91b79
  buf = color_buf = xstrdup (p);
Packit d91b79
Packit d91b79
  enum parse_state state = PS_START;
Packit d91b79
  while (true)
Packit d91b79
    {
Packit d91b79
      switch (state)
Packit d91b79
        {
Packit d91b79
        case PS_START:		/* First label character */
Packit d91b79
          switch (*p)
Packit d91b79
            {
Packit d91b79
            case ':':
Packit d91b79
              ++p;
Packit d91b79
              break;
Packit d91b79
Packit d91b79
            case '*':
Packit d91b79
              /* Allocate new extension block and add to head of
Packit d91b79
                 linked list (this way a later definition will
Packit d91b79
                 override an earlier one, which can be useful for
Packit d91b79
                 having terminal-specific defs override global).  */
Packit d91b79
Packit d91b79
              ext = xmalloc (sizeof *ext);
Packit d91b79
              ext->next = color_ext_list;
Packit d91b79
              color_ext_list = ext;
Packit d91b79
Packit d91b79
              ++p;
Packit d91b79
              ext->ext.string = buf;
Packit d91b79
Packit d91b79
              state = (get_funky_string (&buf, &p, true, &ext->ext.len)
Packit d91b79
                       ? PS_4 : PS_FAIL);
Packit d91b79
              break;
Packit d91b79
Packit d91b79
            case '\0':
Packit d91b79
              state = PS_DONE;	/* Done! */
Packit d91b79
              goto done;
Packit d91b79
Packit d91b79
            default:	/* Assume it is file type label */
Packit d91b79
              label[0] = *(p++);
Packit d91b79
              state = PS_2;
Packit d91b79
              break;
Packit d91b79
            }
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case PS_2:		/* Second label character */
Packit d91b79
          if (*p)
Packit d91b79
            {
Packit d91b79
              label[1] = *(p++);
Packit d91b79
              state = PS_3;
Packit d91b79
            }
Packit d91b79
          else
Packit d91b79
            state = PS_FAIL;	/* Error */
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case PS_3:		/* Equal sign after indicator label */
Packit d91b79
          state = PS_FAIL;	/* Assume failure...  */
Packit d91b79
          if (*(p++) == '=')/* It *should* be...  */
Packit d91b79
            {
Packit d91b79
              for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
Packit d91b79
                {
Packit d91b79
                  if (STREQ (label, indicator_name[ind_no]))
Packit d91b79
                    {
Packit d91b79
                      color_indicator[ind_no].string = buf;
Packit d91b79
                      state = (get_funky_string (&buf, &p, false,
Packit d91b79
                                                 &color_indicator[ind_no].len)
Packit d91b79
                               ? PS_START : PS_FAIL);
Packit d91b79
                      break;
Packit d91b79
                    }
Packit d91b79
                }
Packit d91b79
              if (state == PS_FAIL)
Packit d91b79
                error (0, 0, _("unrecognized prefix: %s"), label);
Packit d91b79
            }
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case PS_4:		/* Equal sign after *.ext */
Packit d91b79
          if (*(p++) == '=')
Packit d91b79
            {
Packit d91b79
              ext->seq.string = buf;
Packit d91b79
              state = (get_funky_string (&buf, &p, false, &ext->seq.len)
Packit d91b79
                       ? PS_START : PS_FAIL);
Packit d91b79
            }
Packit d91b79
          else
Packit d91b79
            state = PS_FAIL;
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case PS_FAIL:
Packit d91b79
          goto done;
Packit d91b79
Packit d91b79
        default:
Packit d91b79
          abort ();
Packit d91b79
        }
Packit d91b79
    }
Packit d91b79
 done:
Packit d91b79
Packit d91b79
  if (state == PS_FAIL)
Packit d91b79
    {
Packit d91b79
      struct color_ext_type *e;
Packit d91b79
      struct color_ext_type *e2;
Packit d91b79
Packit d91b79
      error (0, 0,
Packit d91b79
             _("unparsable value for --palette"));
Packit d91b79
      free (color_buf);
Packit d91b79
      for (e = color_ext_list; e != NULL; /* empty */)
Packit d91b79
        {
Packit d91b79
          e2 = e;
Packit d91b79
          e = e->next;
Packit d91b79
          free (e2);
Packit d91b79
        }
Packit d91b79
      colors_enabled = false;
Packit d91b79
    }
Packit d91b79
}
Packit d91b79
Packit d91b79
static void
Packit d91b79
check_color_output (bool is_pipe)
Packit d91b79
{
Packit d91b79
  bool output_is_tty;
Packit d91b79
Packit d91b79
  if (! outfile || colors_style == NEVER)
Packit d91b79
    return;
Packit d91b79
Packit d91b79
  output_is_tty = presume_output_tty || (!is_pipe && isatty (fileno (outfile)));
Packit d91b79
Packit d91b79
  colors_enabled = (colors_style == ALWAYS
Packit d91b79
                    || (colors_style == AUTO && output_is_tty));
Packit d91b79
Packit d91b79
  if (colors_enabled)
Packit d91b79
    parse_diff_color ();
Packit d91b79
Packit d91b79
  if (output_is_tty)
Packit d91b79
    install_signal_handlers ();
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Call before outputting the results of comparing files NAME0 and NAME1
Packit d91b79
   to set up OUTFILE, the stdio stream for the output to go to.
Packit d91b79
Packit d91b79
   Usually, OUTFILE is just stdout.  But when -l was specified
Packit d91b79
   we fork off a 'pr' and make OUTFILE a pipe to it.
Packit d91b79
   'pr' then outputs to our stdout.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
setup_output (char const *name0, char const *name1, bool recursive)
Packit d91b79
{
Packit d91b79
  current_name0 = name0;
Packit d91b79
  current_name1 = name1;
Packit d91b79
  currently_recursive = recursive;
Packit d91b79
  outfile = 0;
Packit d91b79
}
Packit d91b79
Packit d91b79
#if HAVE_WORKING_FORK
Packit d91b79
static pid_t pr_pid;
Packit d91b79
#endif
Packit d91b79
Packit d91b79
static char c_escape_char (char c)
Packit d91b79
{
Packit d91b79
  switch (c) {
Packit d91b79
    case '\a': return 'a';
Packit d91b79
    case '\b': return 'b';
Packit d91b79
    case '\t': return 't';
Packit d91b79
    case '\n': return 'n';
Packit d91b79
    case '\v': return 'v';
Packit d91b79
    case '\f': return 'f';
Packit d91b79
    case '\r': return 'r';
Packit d91b79
    case '"': return '"';
Packit d91b79
    case '\\': return '\\';
Packit d91b79
    default:
Packit d91b79
      return c < 32;
Packit d91b79
  }
Packit d91b79
}
Packit d91b79
Packit d91b79
static char *
Packit d91b79
c_escape (char const *str)
Packit d91b79
{
Packit d91b79
  char const *s;
Packit d91b79
  size_t plus = 0;
Packit d91b79
  bool must_quote = false;
Packit d91b79
Packit d91b79
  for (s = str; *s; s++)
Packit d91b79
    {
Packit d91b79
      char c = *s;
Packit d91b79
Packit d91b79
      if (c == ' ')
Packit d91b79
	{
Packit d91b79
	  must_quote = true;
Packit d91b79
	  continue;
Packit d91b79
	}
Packit d91b79
      switch (c_escape_char (*s))
Packit d91b79
	{
Packit d91b79
	  case 1:
Packit d91b79
	    plus += 3;
Packit d91b79
	    /* fall through */
Packit d91b79
	  case 0:
Packit d91b79
	    break;
Packit d91b79
	  default:
Packit d91b79
	    plus++;
Packit d91b79
	    break;
Packit d91b79
	}
Packit d91b79
    }
Packit d91b79
Packit d91b79
  if (must_quote || plus)
Packit d91b79
    {
Packit d91b79
      size_t s_len = s - str;
Packit d91b79
      char *buffer = xmalloc (s_len + plus + 3);
Packit d91b79
      char *b = buffer;
Packit d91b79
Packit d91b79
      *b++ = '"';
Packit d91b79
      for (s = str; *s; s++)
Packit d91b79
	{
Packit d91b79
	  char c = *s;
Packit d91b79
	  char escape = c_escape_char (c);
Packit d91b79
Packit d91b79
	  switch (escape)
Packit d91b79
	    {
Packit d91b79
	      case 0:
Packit d91b79
		*b++ = c;
Packit d91b79
		break;
Packit d91b79
	      case 1:
Packit d91b79
		*b++ = '\\';
Packit d91b79
		*b++ = ((c >> 6) & 03) + '0';
Packit d91b79
		*b++ = ((c >> 3) & 07) + '0';
Packit d91b79
		*b++ = ((c >> 0) & 07) + '0';
Packit d91b79
		break;
Packit d91b79
	      default:
Packit d91b79
		*b++ = '\\';
Packit d91b79
		*b++ = escape;
Packit d91b79
		break;
Packit d91b79
	    }
Packit d91b79
	}
Packit d91b79
      *b++ = '"';
Packit d91b79
      *b = 0;
Packit d91b79
      return buffer;
Packit d91b79
    }
Packit d91b79
Packit d91b79
  return (char *) str;
Packit d91b79
}
Packit d91b79
Packit d91b79
void
Packit d91b79
begin_output (void)
Packit d91b79
{
Packit d91b79
  char *names[2];
Packit d91b79
  char *name;
Packit d91b79
Packit d91b79
  if (outfile != 0)
Packit d91b79
    return;
Packit d91b79
Packit d91b79
  names[0] = c_escape (current_name0);
Packit d91b79
  names[1] = c_escape (current_name1);
Packit d91b79
Packit d91b79
  /* Construct the header of this piece of diff.  */
Packit d91b79
  /* POSIX 1003.1-2001 specifies this format.  But there are some bugs in
Packit d91b79
     the standard: it says that we must print only the last component
Packit d91b79
     of the pathnames, and it requires two spaces after "diff" if
Packit d91b79
     there are no options.  These requirements are silly and do not
Packit d91b79
     match historical practice.  */
Packit d91b79
  name = xasprintf ("diff%s %s %s", switch_string, names[0], names[1]);
Packit d91b79
Packit d91b79
  if (paginate)
Packit d91b79
    {
Packit d91b79
      char const *argv[4];
Packit d91b79
Packit d91b79
      if (fflush (stdout) != 0)
Packit d91b79
	pfatal_with_name (_("write failed"));
Packit d91b79
Packit d91b79
      argv[0] = pr_program;
Packit d91b79
      argv[1] = "-h";
Packit d91b79
      argv[2] = name;
Packit d91b79
      argv[3] = 0;
Packit d91b79
Packit d91b79
      /* Make OUTFILE a pipe to a subsidiary 'pr'.  */
Packit d91b79
      {
Packit d91b79
#if HAVE_WORKING_FORK
Packit d91b79
	int pipes[2];
Packit d91b79
Packit d91b79
	if (pipe (pipes) != 0)
Packit d91b79
	  pfatal_with_name ("pipe");
Packit d91b79
Packit d91b79
	pr_pid = fork ();
Packit d91b79
	if (pr_pid < 0)
Packit d91b79
	  pfatal_with_name ("fork");
Packit d91b79
Packit d91b79
	if (pr_pid == 0)
Packit d91b79
	  {
Packit d91b79
	    close (pipes[1]);
Packit d91b79
	    if (pipes[0] != STDIN_FILENO)
Packit d91b79
	      {
Packit d91b79
		if (dup2 (pipes[0], STDIN_FILENO) < 0)
Packit d91b79
		  pfatal_with_name ("dup2");
Packit d91b79
		close (pipes[0]);
Packit d91b79
	      }
Packit d91b79
Packit d91b79
	    execv (pr_program, (char **) argv);
Packit d91b79
	    _exit (errno == ENOENT ? 127 : 126);
Packit d91b79
	  }
Packit d91b79
	else
Packit d91b79
	  {
Packit d91b79
	    close (pipes[0]);
Packit d91b79
	    outfile = fdopen (pipes[1], "w");
Packit d91b79
	    if (!outfile)
Packit d91b79
	      pfatal_with_name ("fdopen");
Packit d91b79
	    check_color_output (true);
Packit d91b79
	  }
Packit d91b79
#else
Packit d91b79
	char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
Packit d91b79
	errno = 0;
Packit d91b79
	outfile = popen (command, "w");
Packit d91b79
	if (!outfile)
Packit d91b79
	  pfatal_with_name (command);
Packit d91b79
	check_color_output (true);
Packit d91b79
	free (command);
Packit d91b79
#endif
Packit d91b79
      }
Packit d91b79
    }
Packit d91b79
  else
Packit d91b79
    {
Packit d91b79
Packit d91b79
      /* If -l was not specified, output the diff straight to 'stdout'.  */
Packit d91b79
Packit d91b79
      outfile = stdout;
Packit d91b79
      check_color_output (false);
Packit d91b79
Packit d91b79
      /* If handling multiple files (because scanning a directory),
Packit d91b79
	 print which files the following output is about.  */
Packit d91b79
      if (currently_recursive)
Packit d91b79
	printf ("%s\n", name);
Packit d91b79
    }
Packit d91b79
Packit d91b79
  free (name);
Packit d91b79
Packit d91b79
  /* A special header is needed at the beginning of context output.  */
Packit d91b79
  switch (output_style)
Packit d91b79
    {
Packit d91b79
    case OUTPUT_CONTEXT:
Packit d91b79
      print_context_header (files, (char const *const *)names, false);
Packit d91b79
      break;
Packit d91b79
Packit d91b79
    case OUTPUT_UNIFIED:
Packit d91b79
      print_context_header (files, (char const *const *)names, true);
Packit d91b79
      break;
Packit d91b79
Packit d91b79
    default:
Packit d91b79
      break;
Packit d91b79
    }
Packit d91b79
Packit d91b79
  if (names[0] != current_name0)
Packit d91b79
    free (names[0]);
Packit d91b79
  if (names[1] != current_name1)
Packit d91b79
    free (names[1]);
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Call after the end of output of diffs for one file.
Packit d91b79
   Close OUTFILE and get rid of the 'pr' subfork.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
finish_output (void)
Packit d91b79
{
Packit d91b79
  if (outfile != 0 && outfile != stdout)
Packit d91b79
    {
Packit d91b79
      int status;
Packit d91b79
      int wstatus;
Packit d91b79
      int werrno = 0;
Packit d91b79
      if (ferror (outfile))
Packit d91b79
	fatal ("write failed");
Packit d91b79
#if ! HAVE_WORKING_FORK
Packit d91b79
      wstatus = pclose (outfile);
Packit d91b79
      if (wstatus == -1)
Packit d91b79
	werrno = errno;
Packit d91b79
#else
Packit d91b79
      if (fclose (outfile) != 0)
Packit d91b79
	pfatal_with_name (_("write failed"));
Packit d91b79
      if (waitpid (pr_pid, &wstatus, 0) < 0)
Packit d91b79
	pfatal_with_name ("waitpid");
Packit d91b79
#endif
Packit d91b79
      status = (! werrno && WIFEXITED (wstatus)
Packit d91b79
		? WEXITSTATUS (wstatus)
Packit d91b79
		: INT_MAX);
Packit d91b79
      if (status)
Packit d91b79
	die (EXIT_TROUBLE, werrno,
Packit d91b79
	       _(status == 126
Packit d91b79
		 ? "subsidiary program '%s' could not be invoked"
Packit d91b79
		 : status == 127
Packit d91b79
		 ? "subsidiary program '%s' not found"
Packit d91b79
		 : status == INT_MAX
Packit d91b79
		 ? "subsidiary program '%s' failed"
Packit d91b79
		 : "subsidiary program '%s' failed (exit status %d)"),
Packit d91b79
	       pr_program, status);
Packit d91b79
    }
Packit d91b79
Packit d91b79
  outfile = 0;
Packit d91b79
}
Packit d91b79

Packit d91b79
/* Compare two lines (typically one from each input file)
Packit d91b79
   according to the command line options.
Packit d91b79
   For efficiency, this is invoked only when the lines do not match exactly
Packit d91b79
   but an option like -i might cause us to ignore the difference.
Packit d91b79
   Return nonzero if the lines differ.  */
Packit d91b79
Packit d91b79
bool
Packit d91b79
lines_differ (char const *s1, char const *s2)
Packit d91b79
{
Packit d91b79
  register char const *t1 = s1;
Packit d91b79
  register char const *t2 = s2;
Packit d91b79
  size_t column = 0;
Packit d91b79
Packit d91b79
  while (1)
Packit d91b79
    {
Packit d91b79
      register unsigned char c1 = *t1++;
Packit d91b79
      register unsigned char c2 = *t2++;
Packit d91b79
Packit d91b79
      /* Test for exact char equality first, since it's a common case.  */
Packit d91b79
      if (c1 != c2)
Packit d91b79
	{
Packit d91b79
	  switch (ignore_white_space)
Packit d91b79
	    {
Packit d91b79
	    case IGNORE_ALL_SPACE:
Packit d91b79
	      /* For -w, just skip past any white space.  */
Packit d91b79
	      while (isspace (c1) && c1 != '\n') c1 = *t1++;
Packit d91b79
	      while (isspace (c2) && c2 != '\n') c2 = *t2++;
Packit d91b79
	      break;
Packit d91b79
Packit d91b79
	    case IGNORE_SPACE_CHANGE:
Packit d91b79
	      /* For -b, advance past any sequence of white space in
Packit d91b79
		 line 1 and consider it just one space, or nothing at
Packit d91b79
		 all if it is at the end of the line.  */
Packit d91b79
	      if (isspace (c1))
Packit d91b79
		{
Packit d91b79
		  while (c1 != '\n')
Packit d91b79
		    {
Packit d91b79
		      c1 = *t1++;
Packit d91b79
		      if (! isspace (c1))
Packit d91b79
			{
Packit d91b79
			  --t1;
Packit d91b79
			  c1 = ' ';
Packit d91b79
			  break;
Packit d91b79
			}
Packit d91b79
		    }
Packit d91b79
		}
Packit d91b79
Packit d91b79
	      /* Likewise for line 2.  */
Packit d91b79
	      if (isspace (c2))
Packit d91b79
		{
Packit d91b79
		  while (c2 != '\n')
Packit d91b79
		    {
Packit d91b79
		      c2 = *t2++;
Packit d91b79
		      if (! isspace (c2))
Packit d91b79
			{
Packit d91b79
			  --t2;
Packit d91b79
			  c2 = ' ';
Packit d91b79
			  break;
Packit d91b79
			}
Packit d91b79
		    }
Packit d91b79
		}
Packit d91b79
Packit d91b79
	      if (c1 != c2)
Packit d91b79
		{
Packit d91b79
		  /* If we went too far when doing the simple test
Packit d91b79
		     for equality, go back to the first non-white-space
Packit d91b79
		     character in both sides and try again.  */
Packit d91b79
		  if (c2 == ' ' && c1 != '\n'
Packit d91b79
		      && s1 + 1 < t1
Packit d91b79
		      && isspace ((unsigned char) t1[-2]))
Packit d91b79
		    {
Packit d91b79
		      --t1;
Packit d91b79
		      continue;
Packit d91b79
		    }
Packit d91b79
		  if (c1 == ' ' && c2 != '\n'
Packit d91b79
		      && s2 + 1 < t2
Packit d91b79
		      && isspace ((unsigned char) t2[-2]))
Packit d91b79
		    {
Packit d91b79
		      --t2;
Packit d91b79
		      continue;
Packit d91b79
		    }
Packit d91b79
		}
Packit d91b79
Packit d91b79
	      break;
Packit d91b79
Packit d91b79
	    case IGNORE_TRAILING_SPACE:
Packit d91b79
	    case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
Packit d91b79
	      if (isspace (c1) && isspace (c2))
Packit d91b79
		{
Packit d91b79
		  unsigned char c;
Packit d91b79
		  if (c1 != '\n')
Packit d91b79
		    {
Packit d91b79
		      char const *p = t1;
Packit d91b79
		      while ((c = *p) != '\n' && isspace (c))
Packit d91b79
			++p;
Packit d91b79
		      if (c != '\n')
Packit d91b79
			break;
Packit d91b79
		    }
Packit d91b79
		  if (c2 != '\n')
Packit d91b79
		    {
Packit d91b79
		      char const *p = t2;
Packit d91b79
		      while ((c = *p) != '\n' && isspace (c))
Packit d91b79
			++p;
Packit d91b79
		      if (c != '\n')
Packit d91b79
			break;
Packit d91b79
		    }
Packit d91b79
		  /* Both lines have nothing but whitespace left.  */
Packit d91b79
		  return false;
Packit d91b79
		}
Packit d91b79
	      if (ignore_white_space == IGNORE_TRAILING_SPACE)
Packit d91b79
		break;
Packit d91b79
	      FALLTHROUGH;
Packit d91b79
	    case IGNORE_TAB_EXPANSION:
Packit d91b79
	      if ((c1 == ' ' && c2 == '\t')
Packit d91b79
		  || (c1 == '\t' && c2 == ' '))
Packit d91b79
		{
Packit d91b79
		  size_t column2 = column;
Packit d91b79
		  for (;; c1 = *t1++)
Packit d91b79
		    {
Packit d91b79
		      if (c1 == ' ')
Packit d91b79
			column++;
Packit d91b79
		      else if (c1 == '\t')
Packit d91b79
			column += tabsize - column % tabsize;
Packit d91b79
		      else
Packit d91b79
			break;
Packit d91b79
		    }
Packit d91b79
		  for (;; c2 = *t2++)
Packit d91b79
		    {
Packit d91b79
		      if (c2 == ' ')
Packit d91b79
			column2++;
Packit d91b79
		      else if (c2 == '\t')
Packit d91b79
			column2 += tabsize - column2 % tabsize;
Packit d91b79
		      else
Packit d91b79
			break;
Packit d91b79
		    }
Packit d91b79
		  if (column != column2)
Packit d91b79
		    return true;
Packit d91b79
		}
Packit d91b79
	      break;
Packit d91b79
Packit d91b79
	    case IGNORE_NO_WHITE_SPACE:
Packit d91b79
	      break;
Packit d91b79
	    }
Packit d91b79
Packit d91b79
	  /* Lowercase all letters if -i is specified.  */
Packit d91b79
Packit d91b79
	  if (ignore_case)
Packit d91b79
	    {
Packit d91b79
	      c1 = tolower (c1);
Packit d91b79
	      c2 = tolower (c2);
Packit d91b79
	    }
Packit d91b79
Packit d91b79
	  if (c1 != c2)
Packit d91b79
	    break;
Packit d91b79
	}
Packit d91b79
      if (c1 == '\n')
Packit d91b79
	return false;
Packit d91b79
Packit d91b79
      column += c1 == '\t' ? tabsize - column % tabsize : 1;
Packit d91b79
    }
Packit d91b79
Packit d91b79
  return true;
Packit d91b79
}
Packit d91b79

Packit d91b79
/* Find the consecutive changes at the start of the script START.
Packit d91b79
   Return the last link before the first gap.  */
Packit d91b79
Packit d91b79
struct change * _GL_ATTRIBUTE_CONST
Packit d91b79
find_change (struct change *start)
Packit d91b79
{
Packit d91b79
  return start;
Packit d91b79
}
Packit d91b79
Packit d91b79
struct change * _GL_ATTRIBUTE_CONST
Packit d91b79
find_reverse_change (struct change *start)
Packit d91b79
{
Packit d91b79
  return start;
Packit d91b79
}
Packit d91b79

Packit d91b79
/* Divide SCRIPT into pieces by calling HUNKFUN and
Packit d91b79
   print each piece with PRINTFUN.
Packit d91b79
   Both functions take one arg, an edit script.
Packit d91b79
Packit d91b79
   HUNKFUN is called with the tail of the script
Packit d91b79
   and returns the last link that belongs together with the start
Packit d91b79
   of the tail.
Packit d91b79
Packit d91b79
   PRINTFUN takes a subscript which belongs together (with a null
Packit d91b79
   link at the end) and prints it.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
print_script (struct change *script,
Packit d91b79
	      struct change * (*hunkfun) (struct change *),
Packit d91b79
	      void (*printfun) (struct change *))
Packit d91b79
{
Packit d91b79
  struct change *next = script;
Packit d91b79
Packit d91b79
  while (next)
Packit d91b79
    {
Packit d91b79
      struct change *this, *end;
Packit d91b79
Packit d91b79
      /* Find a set of changes that belong together.  */
Packit d91b79
      this = next;
Packit d91b79
      end = (*hunkfun) (next);
Packit d91b79
Packit d91b79
      /* Disconnect them from the rest of the changes,
Packit d91b79
	 making them a hunk, and remember the rest for next iteration.  */
Packit d91b79
      next = end->link;
Packit d91b79
      end->link = 0;
Packit d91b79
#ifdef DEBUG
Packit d91b79
      debug_script (this);
Packit d91b79
#endif
Packit d91b79
Packit d91b79
      /* Print this hunk.  */
Packit d91b79
      (*printfun) (this);
Packit d91b79
Packit d91b79
      /* Reconnect the script so it will all be freed properly.  */
Packit d91b79
      end->link = next;
Packit d91b79
    }
Packit d91b79
}
Packit d91b79

Packit d91b79
/* Print the text of a single line LINE,
Packit d91b79
   flagging it with the characters in LINE_FLAG (which say whether
Packit d91b79
   the line is inserted, deleted, changed, etc.).  LINE_FLAG must not
Packit d91b79
   end in a blank, unless it is a single blank.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
print_1_line (char const *line_flag, char const *const *line)
Packit d91b79
{
Packit d91b79
  print_1_line_nl (line_flag, line, false);
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Print the text of a single line LINE,
Packit d91b79
   flagging it with the characters in LINE_FLAG (which say whether
Packit d91b79
   the line is inserted, deleted, changed, etc.).  LINE_FLAG must not
Packit d91b79
   end in a blank, unless it is a single blank.  If SKIP_NL is set, then
Packit d91b79
   the final '\n' is not printed.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
print_1_line_nl (char const *line_flag, char const *const *line, bool skip_nl)
Packit d91b79
{
Packit d91b79
  char const *base = line[0], *limit = line[1]; /* Help the compiler.  */
Packit d91b79
  FILE *out = outfile; /* Help the compiler some more.  */
Packit d91b79
  char const *flag_format = 0;
Packit d91b79
Packit d91b79
  /* If -T was specified, use a Tab between the line-flag and the text.
Packit d91b79
     Otherwise use a Space (as Unix diff does).
Packit d91b79
     Print neither space nor tab if line-flags are empty.
Packit d91b79
     But omit trailing blanks if requested.  */
Packit d91b79
Packit d91b79
  if (line_flag && *line_flag)
Packit d91b79
    {
Packit d91b79
      char const *flag_format_1 = flag_format = initial_tab ? "%s\t" : "%s ";
Packit d91b79
      char const *line_flag_1 = line_flag;
Packit d91b79
Packit d91b79
      if (suppress_blank_empty && **line == '\n')
Packit d91b79
	{
Packit d91b79
	  flag_format_1 = "%s";
Packit d91b79
Packit d91b79
	  /* This hack to omit trailing blanks takes advantage of the
Packit d91b79
	     fact that the only way that LINE_FLAG can end in a blank
Packit d91b79
	     is when LINE_FLAG consists of a single blank.  */
Packit d91b79
	  line_flag_1 += *line_flag_1 == ' ';
Packit d91b79
	}
Packit d91b79
Packit d91b79
      fprintf (out, flag_format_1, line_flag_1);
Packit d91b79
    }
Packit d91b79
Packit d91b79
  output_1_line (base, limit - (skip_nl && limit[-1] == '\n'), flag_format, line_flag);
Packit d91b79
Packit d91b79
  if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
Packit d91b79
    {
Packit d91b79
      set_color_context (RESET_CONTEXT);
Packit d91b79
      fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
Packit d91b79
    }
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Output a line from BASE up to LIMIT.
Packit d91b79
   With -t, expand white space characters to spaces, and if FLAG_FORMAT
Packit d91b79
   is nonzero, output it with argument LINE_FLAG after every
Packit d91b79
   internal carriage return, so that tab stops continue to line up.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
output_1_line (char const *base, char const *limit, char const *flag_format,
Packit d91b79
	       char const *line_flag)
Packit d91b79
{
Packit d91b79
  const size_t MAX_CHUNK = 1024;
Packit d91b79
  if (!expand_tabs)
Packit d91b79
    {
Packit d91b79
      size_t left = limit - base;
Packit d91b79
      while (left)
Packit d91b79
        {
Packit d91b79
          size_t to_write = MIN (left, MAX_CHUNK);
Packit d91b79
          size_t written = fwrite (base, sizeof (char), to_write, outfile);
Packit d91b79
          if (written < to_write)
Packit d91b79
            return;
Packit d91b79
          base += written;
Packit d91b79
          left -= written;
Packit d91b79
          process_signals ();
Packit d91b79
        }
Packit d91b79
    }
Packit d91b79
  else
Packit d91b79
    {
Packit d91b79
      register FILE *out = outfile;
Packit d91b79
      register unsigned char c;
Packit d91b79
      register char const *t = base;
Packit d91b79
      register size_t column = 0;
Packit d91b79
      size_t tab_size = tabsize;
Packit d91b79
      size_t counter_proc_signals = 0;
Packit d91b79
Packit d91b79
      while (t < limit)
Packit d91b79
        {
Packit d91b79
          counter_proc_signals++;
Packit d91b79
          if (counter_proc_signals == MAX_CHUNK)
Packit d91b79
            {
Packit d91b79
              process_signals ();
Packit d91b79
              counter_proc_signals = 0;
Packit d91b79
            }
Packit d91b79
Packit d91b79
          switch ((c = *t++))
Packit d91b79
            {
Packit d91b79
            case '\t':
Packit d91b79
              {
Packit d91b79
                size_t spaces = tab_size - column % tab_size;
Packit d91b79
                column += spaces;
Packit d91b79
                do
Packit d91b79
                  putc (' ', out);
Packit d91b79
                while (--spaces);
Packit d91b79
              }
Packit d91b79
              break;
Packit d91b79
Packit d91b79
            case '\r':
Packit d91b79
              putc (c, out);
Packit d91b79
              if (flag_format && t < limit && *t != '\n')
Packit d91b79
                fprintf (out, flag_format, line_flag);
Packit d91b79
              column = 0;
Packit d91b79
              break;
Packit d91b79
Packit d91b79
            case '\b':
Packit d91b79
              if (column == 0)
Packit d91b79
                continue;
Packit d91b79
              column--;
Packit d91b79
              putc (c, out);
Packit d91b79
              break;
Packit d91b79
Packit d91b79
            default:
Packit d91b79
              column += isprint (c) != 0;
Packit d91b79
              putc (c, out);
Packit d91b79
              break;
Packit d91b79
            }
Packit d91b79
        }
Packit d91b79
    }
Packit d91b79
}
Packit d91b79
Packit d91b79
enum indicator_no
Packit d91b79
  {
Packit d91b79
    C_LEFT, C_RIGHT, C_END, C_RESET, C_HEADER, C_ADD, C_DELETE, C_LINE
Packit d91b79
  };
Packit d91b79
Packit d91b79
static void
Packit d91b79
put_indicator (const struct bin_str *ind)
Packit d91b79
{
Packit d91b79
  fwrite (ind->string, ind->len, 1, outfile);
Packit d91b79
}
Packit d91b79
Packit d91b79
static enum color_context last_context = RESET_CONTEXT;
Packit d91b79
Packit d91b79
void
Packit d91b79
set_color_context (enum color_context color_context)
Packit d91b79
{
Packit d91b79
  if (color_context != RESET_CONTEXT)
Packit d91b79
    process_signals ();
Packit d91b79
  if (colors_enabled && last_context != color_context)
Packit d91b79
    {
Packit d91b79
      put_indicator (&color_indicator[C_LEFT]);
Packit d91b79
      switch (color_context)
Packit d91b79
        {
Packit d91b79
        case HEADER_CONTEXT:
Packit d91b79
          put_indicator (&color_indicator[C_HEADER]);
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case LINE_NUMBER_CONTEXT:
Packit d91b79
          put_indicator (&color_indicator[C_LINE]);
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case ADD_CONTEXT:
Packit d91b79
          put_indicator (&color_indicator[C_ADD]);
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case DELETE_CONTEXT:
Packit d91b79
          put_indicator (&color_indicator[C_DELETE]);
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        case RESET_CONTEXT:
Packit d91b79
          put_indicator (&color_indicator[C_RESET]);
Packit d91b79
          break;
Packit d91b79
Packit d91b79
        default:
Packit d91b79
          abort ();
Packit d91b79
        }
Packit d91b79
      put_indicator (&color_indicator[C_RIGHT]);
Packit d91b79
      last_context = color_context;
Packit d91b79
    }
Packit d91b79
}
Packit d91b79
Packit d91b79
Packit d91b79
char const change_letter[] = { 0, 'd', 'a', 'c' };
Packit d91b79

Packit d91b79
/* Translate an internal line number (an index into diff's table of lines)
Packit d91b79
   into an actual line number in the input file.
Packit d91b79
   The internal line number is I.  FILE points to the data on the file.
Packit d91b79
Packit d91b79
   Internal line numbers count from 0 starting after the prefix.
Packit d91b79
   Actual line numbers count from 1 within the entire file.  */
Packit d91b79
Packit d91b79
lin _GL_ATTRIBUTE_PURE
Packit d91b79
translate_line_number (struct file_data const *file, lin i)
Packit d91b79
{
Packit d91b79
  return i + file->prefix_lines + 1;
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Translate a line number range.  This is always done for printing,
Packit d91b79
   so for convenience translate to printint rather than lin, so that the
Packit d91b79
   caller can use printf with "%"pI"d" without casting.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
translate_range (struct file_data const *file,
Packit d91b79
		 lin a, lin b,
Packit d91b79
		 printint *aptr, printint *bptr)
Packit d91b79
{
Packit d91b79
  *aptr = translate_line_number (file, a - 1) + 1;
Packit d91b79
  *bptr = translate_line_number (file, b + 1) - 1;
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Print a pair of line numbers with SEPCHAR, translated for file FILE.
Packit d91b79
   If the two numbers are identical, print just one number.
Packit d91b79
Packit d91b79
   Args A and B are internal line numbers.
Packit d91b79
   We print the translated (real) line numbers.  */
Packit d91b79
Packit d91b79
void
Packit d91b79
print_number_range (char sepchar, struct file_data *file, lin a, lin b)
Packit d91b79
{
Packit d91b79
  printint trans_a, trans_b;
Packit d91b79
  translate_range (file, a, b, &trans_a, &trans_b);
Packit d91b79
Packit d91b79
  /* Note: we can have B < A in the case of a range of no lines.
Packit d91b79
     In this case, we should print the line number before the range,
Packit d91b79
     which is B.  */
Packit d91b79
  if (trans_b > trans_a)
Packit d91b79
    fprintf (outfile, "%"pI"d%c%"pI"d", trans_a, sepchar, trans_b);
Packit d91b79
  else
Packit d91b79
    fprintf (outfile, "%"pI"d", trans_b);
Packit d91b79
}
Packit d91b79

Packit d91b79
/* Look at a hunk of edit script and report the range of lines in each file
Packit d91b79
   that it applies to.  HUNK is the start of the hunk, which is a chain
Packit d91b79
   of 'struct change'.  The first and last line numbers of file 0 are stored in
Packit d91b79
   *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
Packit d91b79
   Note that these are internal line numbers that count from 0.
Packit d91b79
Packit d91b79
   If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
Packit d91b79
Packit d91b79
   Return UNCHANGED if only ignorable lines are inserted or deleted,
Packit d91b79
   OLD if lines of file 0 are deleted,
Packit d91b79
   NEW if lines of file 1 are inserted,
Packit d91b79
   and CHANGED if both kinds of changes are found. */
Packit d91b79
Packit d91b79
enum changes
Packit d91b79
analyze_hunk (struct change *hunk,
Packit d91b79
	      lin *first0, lin *last0,
Packit d91b79
	      lin *first1, lin *last1)
Packit d91b79
{
Packit d91b79
  struct change *next;
Packit d91b79
  lin l0, l1;
Packit d91b79
  lin show_from, show_to;
Packit d91b79
  lin i;
Packit d91b79
  bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
Packit d91b79
  size_t trivial_length = ignore_blank_lines - 1;
Packit d91b79
    /* If 0, ignore zero-length lines;
Packit d91b79
       if SIZE_MAX, do not ignore lines just because of their length.  */
Packit d91b79
Packit d91b79
  bool skip_white_space =
Packit d91b79
    ignore_blank_lines && IGNORE_TRAILING_SPACE <= ignore_white_space;
Packit d91b79
  bool skip_leading_white_space =
Packit d91b79
    skip_white_space && IGNORE_SPACE_CHANGE <= ignore_white_space;
Packit d91b79
Packit d91b79
  char const * const *linbuf0 = files[0].linbuf;  /* Help the compiler.  */
Packit d91b79
  char const * const *linbuf1 = files[1].linbuf;
Packit d91b79
Packit d91b79
  show_from = show_to = 0;
Packit d91b79
Packit d91b79
  *first0 = hunk->line0;
Packit d91b79
  *first1 = hunk->line1;
Packit d91b79
Packit d91b79
  next = hunk;
Packit d91b79
  do
Packit d91b79
    {
Packit d91b79
      l0 = next->line0 + next->deleted - 1;
Packit d91b79
      l1 = next->line1 + next->inserted - 1;
Packit d91b79
      show_from += next->deleted;
Packit d91b79
      show_to += next->inserted;
Packit d91b79
Packit d91b79
      for (i = next->line0; i <= l0 && trivial; i++)
Packit d91b79
	{
Packit d91b79
	  char const *line = linbuf0[i];
Packit d91b79
	  char const *lastbyte = linbuf0[i + 1] - 1;
Packit d91b79
	  char const *newline = lastbyte + (*lastbyte != '\n');
Packit d91b79
	  size_t len = newline - line;
Packit d91b79
	  char const *p = line;
Packit d91b79
	  if (skip_white_space)
Packit d91b79
	    for (; *p != '\n'; p++)
Packit d91b79
	      if (! isspace ((unsigned char) *p))
Packit d91b79
		{
Packit d91b79
		  if (! skip_leading_white_space)
Packit d91b79
		    p = line;
Packit d91b79
		  break;
Packit d91b79
		}
Packit d91b79
	  if (newline - p != trivial_length
Packit d91b79
	      && (! ignore_regexp.fastmap
Packit d91b79
		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
Packit d91b79
	    trivial = 0;
Packit d91b79
	}
Packit d91b79
Packit d91b79
      for (i = next->line1; i <= l1 && trivial; i++)
Packit d91b79
	{
Packit d91b79
	  char const *line = linbuf1[i];
Packit d91b79
	  char const *lastbyte = linbuf1[i + 1] - 1;
Packit d91b79
	  char const *newline = lastbyte + (*lastbyte != '\n');
Packit d91b79
	  size_t len = newline - line;
Packit d91b79
	  char const *p = line;
Packit d91b79
	  if (skip_white_space)
Packit d91b79
	    for (; *p != '\n'; p++)
Packit d91b79
	      if (! isspace ((unsigned char) *p))
Packit d91b79
		{
Packit d91b79
		  if (! skip_leading_white_space)
Packit d91b79
		    p = line;
Packit d91b79
		  break;
Packit d91b79
		}
Packit d91b79
	  if (newline - p != trivial_length
Packit d91b79
	      && (! ignore_regexp.fastmap
Packit d91b79
		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
Packit d91b79
	    trivial = 0;
Packit d91b79
	}
Packit d91b79
    }
Packit d91b79
  while ((next = next->link) != 0);
Packit d91b79
Packit d91b79
  *last0 = l0;
Packit d91b79
  *last1 = l1;
Packit d91b79
Packit d91b79
  /* If all inserted or deleted lines are ignorable,
Packit d91b79
     tell the caller to ignore this hunk.  */
Packit d91b79
Packit d91b79
  if (trivial)
Packit d91b79
    return UNCHANGED;
Packit d91b79
Packit d91b79
  return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
Packit d91b79
}
Packit d91b79

Packit d91b79
/* Concatenate three strings, returning a newly malloc'd string.  */
Packit d91b79
Packit d91b79
char *
Packit d91b79
concat (char const *s1, char const *s2, char const *s3)
Packit d91b79
{
Packit d91b79
  char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
Packit d91b79
  sprintf (new, "%s%s%s", s1, s2, s3);
Packit d91b79
  return new;
Packit d91b79
}
Packit d91b79
Packit d91b79
/* Yield a new block of SIZE bytes, initialized to zero.  */
Packit d91b79
Packit d91b79
void *
Packit d91b79
zalloc (size_t size)
Packit d91b79
{
Packit d91b79
  void *p = xmalloc (size);
Packit d91b79
  memset (p, 0, size);
Packit d91b79
  return p;
Packit d91b79
}
Packit d91b79

Packit d91b79
void
Packit d91b79
debug_script (struct change *sp)
Packit d91b79
{
Packit d91b79
  fflush (stdout);
Packit d91b79
Packit d91b79
  for (; sp; sp = sp->link)
Packit d91b79
    {
Packit d91b79
      printint line0 = sp->line0;
Packit d91b79
      printint line1 = sp->line1;
Packit d91b79
      printint deleted = sp->deleted;
Packit d91b79
      printint inserted = sp->inserted;
Packit d91b79
      fprintf (stderr, "%3"pI"d %3"pI"d delete %"pI"d insert %"pI"d\n",
Packit d91b79
	       line0, line1, deleted, inserted);
Packit d91b79
    }
Packit d91b79
Packit d91b79
  fflush (stderr);
Packit d91b79
}