Blame src/util.c

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