util.c revision 170755
155682Smarkm/* Support routines for GNU DIFF.
255682Smarkm
355682Smarkm   Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002,
455682Smarkm   2004 Free Software Foundation, Inc.
555682Smarkm
655682Smarkm   This file is part of GNU DIFF.
755682Smarkm
855682Smarkm   GNU DIFF is free software; you can redistribute it and/or modify
955682Smarkm   it under the terms of the GNU General Public License as published by
1055682Smarkm   the Free Software Foundation; either version 2, or (at your option)
1155682Smarkm   any later version.
1255682Smarkm
1355682Smarkm   GNU DIFF is distributed in the hope that it will be useful,
1455682Smarkm   but WITHOUT ANY WARRANTY; without even the implied warranty of
1555682Smarkm   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1655682Smarkm   GNU General Public License for more details.
1755682Smarkm
1855682Smarkm   You should have received a copy of the GNU General Public License
1955682Smarkm   along with this program; see the file COPYING.
2055682Smarkm   If not, write to the Free Software Foundation,
2155682Smarkm   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2255682Smarkm
2355682Smarkm#include "diff.h"
2455682Smarkm#include <dirname.h>
2555682Smarkm#include <error.h>
2655682Smarkm#include <quotesys.h>
2755682Smarkm#include <xalloc.h>
2855682Smarkm
2955682Smarkmchar const pr_program[] = PR_PROGRAM;
3055682Smarkm
3155682Smarkm/* Queue up one-line messages to be printed at the end,
3255682Smarkm   when -l is specified.  Each message is recorded with a `struct msg'.  */
3355682Smarkm
3455682Smarkmstruct msg
3555682Smarkm{
3655682Smarkm  struct msg *next;
3755682Smarkm  char args[1]; /* Format + 4 args, each '\0' terminated, concatenated.  */
3855682Smarkm};
3955682Smarkm
4055682Smarkm/* Head of the chain of queues messages.  */
4155682Smarkm
4255682Smarkmstatic struct msg *msg_chain;
4355682Smarkm
4455682Smarkm/* Tail of the chain of queues messages.  */
4555682Smarkm
4655682Smarkmstatic struct msg **msg_chain_end = &msg_chain;
4755682Smarkm
4855682Smarkm/* Use when a system call returns non-zero status.
4955682Smarkm   NAME should normally be the file name.  */
5055682Smarkm
5155682Smarkmvoid
5255682Smarkmperror_with_name (char const *name)
5355682Smarkm{
5455682Smarkm  error (0, errno, "%s", name);
5555682Smarkm}
5655682Smarkm
5755682Smarkm/* Use when a system call returns non-zero status and that is fatal.  */
5855682Smarkm
5955682Smarkmvoid
6055682Smarkmpfatal_with_name (char const *name)
6155682Smarkm{
6255682Smarkm  int e = errno;
6355682Smarkm  print_message_queue ();
6455682Smarkm  error (EXIT_TROUBLE, e, "%s", name);
6555682Smarkm  abort ();
6655682Smarkm}
6755682Smarkm
6855682Smarkm/* Print an error message containing MSGID, then exit.  */
6955682Smarkm
7055682Smarkmvoid
7155682Smarkmfatal (char const *msgid)
7255682Smarkm{
7355682Smarkm  print_message_queue ();
7455682Smarkm  error (EXIT_TROUBLE, 0, "%s", _(msgid));
7555682Smarkm  abort ();
7655682Smarkm}
7755682Smarkm
7855682Smarkm/* Like printf, except if -l in effect then save the message and print later.
7955682Smarkm   This is used for things like "Only in ...".  */
8055682Smarkm
8155682Smarkmvoid
8255682Smarkmmessage (char const *format_msgid, char const *arg1, char const *arg2)
8355682Smarkm{
8455682Smarkm  message5 (format_msgid, arg1, arg2, 0, 0);
8555682Smarkm}
8655682Smarkm
8755682Smarkmvoid
8855682Smarkmmessage5 (char const *format_msgid, char const *arg1, char const *arg2,
8955682Smarkm	  char const *arg3, char const *arg4)
9055682Smarkm{
9155682Smarkm  if (paginate)
9255682Smarkm    {
9355682Smarkm      char *p;
9455682Smarkm      char const *arg[5];
9555682Smarkm      int i;
9655682Smarkm      size_t size[5];
9755682Smarkm      size_t total_size = offsetof (struct msg, args);
9855682Smarkm      struct msg *new;
9955682Smarkm
10055682Smarkm      arg[0] = format_msgid;
10155682Smarkm      arg[1] = arg1;
10255682Smarkm      arg[2] = arg2;
10355682Smarkm      arg[3] = arg3 ? arg3 : "";
10455682Smarkm      arg[4] = arg4 ? arg4 : "";
10555682Smarkm
10655682Smarkm      for (i = 0;  i < 5;  i++)
10755682Smarkm	total_size += size[i] = strlen (arg[i]) + 1;
10855682Smarkm
10955682Smarkm      new = xmalloc (total_size);
11055682Smarkm
11155682Smarkm      for (i = 0, p = new->args;  i < 5;  p += size[i++])
11255682Smarkm	memcpy (p, arg[i], size[i]);
11355682Smarkm
11455682Smarkm      *msg_chain_end = new;
11555682Smarkm      new->next = 0;
11655682Smarkm      msg_chain_end = &new->next;
11755682Smarkm    }
11855682Smarkm  else
11955682Smarkm    {
12055682Smarkm      if (sdiff_merge_assist)
12155682Smarkm	putchar (' ');
12255682Smarkm      printf (_(format_msgid), arg1, arg2, arg3, arg4);
12355682Smarkm    }
12455682Smarkm}
12555682Smarkm
12655682Smarkm/* Output all the messages that were saved up by calls to `message'.  */
12755682Smarkm
12855682Smarkmvoid
12955682Smarkmprint_message_queue (void)
13055682Smarkm{
13155682Smarkm  char const *arg[5];
13255682Smarkm  int i;
13355682Smarkm  struct msg *m = msg_chain;
13455682Smarkm
13555682Smarkm  while (m)
13655682Smarkm    {
13755682Smarkm      struct msg *next = m->next;
13855682Smarkm      arg[0] = m->args;
13955682Smarkm      for (i = 0;  i < 4;  i++)
14055682Smarkm	arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
14155682Smarkm      printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
14255682Smarkm      free (m);
14355682Smarkm      m = next;
14455682Smarkm    }
14555682Smarkm}
14655682Smarkm
14755682Smarkm/* Call before outputting the results of comparing files NAME0 and NAME1
14855682Smarkm   to set up OUTFILE, the stdio stream for the output to go to.
14955682Smarkm
15055682Smarkm   Usually, OUTFILE is just stdout.  But when -l was specified
15155682Smarkm   we fork off a `pr' and make OUTFILE a pipe to it.
15255682Smarkm   `pr' then outputs to our stdout.  */
15355682Smarkm
15455682Smarkmstatic char const *current_name0;
15555682Smarkmstatic char const *current_name1;
15655682Smarkmstatic bool currently_recursive;
15755682Smarkm
15855682Smarkmvoid
15955682Smarkmsetup_output (char const *name0, char const *name1, bool recursive)
16055682Smarkm{
16155682Smarkm  current_name0 = name0;
16255682Smarkm  current_name1 = name1;
16355682Smarkm  currently_recursive = recursive;
16455682Smarkm  outfile = 0;
16555682Smarkm}
16655682Smarkm
16755682Smarkm#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
16855682Smarkmstatic pid_t pr_pid;
16955682Smarkm#endif
17055682Smarkm
17155682Smarkmvoid
17255682Smarkmbegin_output (void)
17355682Smarkm{
17455682Smarkm  char *name;
17555682Smarkm
17655682Smarkm  if (outfile != 0)
17755682Smarkm    return;
17855682Smarkm
17955682Smarkm  /* Construct the header of this piece of diff.  */
18055682Smarkm  name = xmalloc (strlen (current_name0) + strlen (current_name1)
18155682Smarkm		  + strlen (switch_string) + 7);
18255682Smarkm
18355682Smarkm  /* POSIX 1003.1-2001 specifies this format.  But there are some bugs in
18455682Smarkm     the standard: it says that we must print only the last component
18555682Smarkm     of the pathnames, and it requires two spaces after "diff" if
18655682Smarkm     there are no options.  These requirements are silly and do not
18755682Smarkm     match historical practice.  */
18855682Smarkm  sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
18955682Smarkm
19055682Smarkm  if (paginate)
19155682Smarkm    {
19255682Smarkm      if (fflush (stdout) != 0)
19355682Smarkm	pfatal_with_name (_("write failed"));
19455682Smarkm
19555682Smarkm      /* Make OUTFILE a pipe to a subsidiary `pr'.  */
19655682Smarkm      {
19755682Smarkm#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
19855682Smarkm	int pipes[2];
19955682Smarkm
20055682Smarkm	if (pipe (pipes) != 0)
20155682Smarkm	  pfatal_with_name ("pipe");
20255682Smarkm
20355682Smarkm	pr_pid = vfork ();
20455682Smarkm	if (pr_pid < 0)
20555682Smarkm	  pfatal_with_name ("fork");
20655682Smarkm
20755682Smarkm	if (pr_pid == 0)
20855682Smarkm	  {
20955682Smarkm	    close (pipes[1]);
21055682Smarkm	    if (pipes[0] != STDIN_FILENO)
21155682Smarkm	      {
21255682Smarkm		if (dup2 (pipes[0], STDIN_FILENO) < 0)
21355682Smarkm		  pfatal_with_name ("dup2");
21455682Smarkm		close (pipes[0]);
21555682Smarkm	      }
21655682Smarkm
21755682Smarkm	    execl (pr_program, pr_program, "-h", name, (char *) 0);
21855682Smarkm	    _exit (errno == ENOENT ? 127 : 126);
21955682Smarkm	  }
22055682Smarkm	else
22155682Smarkm	  {
22255682Smarkm	    close (pipes[0]);
22355682Smarkm	    outfile = fdopen (pipes[1], "w");
22455682Smarkm	    if (!outfile)
22555682Smarkm	      pfatal_with_name ("fdopen");
22655682Smarkm	  }
22755682Smarkm#else
22855682Smarkm	char *command = xmalloc (sizeof pr_program - 1 + 7
22955682Smarkm				 + quote_system_arg ((char *) 0, name) + 1);
23055682Smarkm	char *p;
23155682Smarkm	sprintf (command, "%s -f -h ", pr_program);
23255682Smarkm	p = command + sizeof pr_program - 1 + 7;
23355682Smarkm	p += quote_system_arg (p, name);
23455682Smarkm	*p = 0;
23555682Smarkm	errno = 0;
23655682Smarkm	outfile = popen (command, "w");
23755682Smarkm	if (!outfile)
23855682Smarkm	  pfatal_with_name (command);
23955682Smarkm	free (command);
24055682Smarkm#endif
24155682Smarkm      }
24255682Smarkm    }
24355682Smarkm  else
24455682Smarkm    {
24555682Smarkm
24655682Smarkm      /* If -l was not specified, output the diff straight to `stdout'.  */
24755682Smarkm
24855682Smarkm      outfile = stdout;
24955682Smarkm
25055682Smarkm      /* If handling multiple files (because scanning a directory),
25155682Smarkm	 print which files the following output is about.  */
25255682Smarkm      if (currently_recursive)
25355682Smarkm	printf ("%s\n", name);
25455682Smarkm    }
25555682Smarkm
25655682Smarkm  free (name);
25755682Smarkm
25855682Smarkm  /* A special header is needed at the beginning of context output.  */
25955682Smarkm  switch (output_style)
26055682Smarkm    {
26155682Smarkm    case OUTPUT_CONTEXT:
26255682Smarkm      print_context_header (files, false);
26355682Smarkm      break;
26455682Smarkm
26555682Smarkm    case OUTPUT_UNIFIED:
26655682Smarkm      print_context_header (files, true);
26755682Smarkm      break;
26855682Smarkm
26955682Smarkm    default:
27055682Smarkm      break;
27155682Smarkm    }
27255682Smarkm}
27355682Smarkm
27455682Smarkm/* Call after the end of output of diffs for one file.
27555682Smarkm   Close OUTFILE and get rid of the `pr' subfork.  */
27655682Smarkm
27755682Smarkmvoid
27855682Smarkmfinish_output (void)
27955682Smarkm{
28055682Smarkm  if (outfile != 0 && outfile != stdout)
28155682Smarkm    {
28255682Smarkm      int status;
28355682Smarkm      int wstatus;
28455682Smarkm      int werrno = 0;
28555682Smarkm      if (ferror (outfile))
28655682Smarkm	fatal ("write failed");
28755682Smarkm#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
28855682Smarkm      wstatus = pclose (outfile);
28955682Smarkm      if (wstatus == -1)
29055682Smarkm	werrno = errno;
29155682Smarkm#else
29255682Smarkm      if (fclose (outfile) != 0)
29355682Smarkm	pfatal_with_name (_("write failed"));
29455682Smarkm      if (waitpid (pr_pid, &wstatus, 0) < 0)
29555682Smarkm	pfatal_with_name ("waitpid");
29655682Smarkm#endif
29755682Smarkm      status = (! werrno && WIFEXITED (wstatus)
29855682Smarkm		? WEXITSTATUS (wstatus)
29955682Smarkm		: INT_MAX);
30055682Smarkm      if (status)
30155682Smarkm	error (EXIT_TROUBLE, werrno,
30255682Smarkm	       _(status == 126
30355682Smarkm		 ? "subsidiary program `%s' could not be invoked"
30455682Smarkm		 : status == 127
30555682Smarkm		 ? "subsidiary program `%s' not found"
30655682Smarkm		 : status == INT_MAX
30755682Smarkm		 ? "subsidiary program `%s' failed"
30855682Smarkm		 : "subsidiary program `%s' failed (exit status %d)"),
30955682Smarkm	       pr_program, status);
31055682Smarkm    }
31155682Smarkm
31255682Smarkm  outfile = 0;
31355682Smarkm}
31455682Smarkm
31555682Smarkm/* Compare two lines (typically one from each input file)
31655682Smarkm   according to the command line options.
31755682Smarkm   For efficiency, this is invoked only when the lines do not match exactly
31855682Smarkm   but an option like -i might cause us to ignore the difference.
31955682Smarkm   Return nonzero if the lines differ.  */
32055682Smarkm
32155682Smarkmbool
32255682Smarkmlines_differ (char const *s1, char const *s2)
32355682Smarkm{
32455682Smarkm  register char const *t1 = s1;
32555682Smarkm  register char const *t2 = s2;
32655682Smarkm  size_t column = 0;
32755682Smarkm
32855682Smarkm  while (1)
32955682Smarkm    {
33055682Smarkm      register unsigned char c1 = *t1++;
33155682Smarkm      register unsigned char c2 = *t2++;
33255682Smarkm
33355682Smarkm      /* Test for exact char equality first, since it's a common case.  */
33455682Smarkm      if (c1 != c2)
335	{
336	  switch (ignore_white_space)
337	    {
338	    case IGNORE_ALL_SPACE:
339	      /* For -w, just skip past any white space.  */
340	      while (isspace (c1) && c1 != '\n') c1 = *t1++;
341	      while (isspace (c2) && c2 != '\n') c2 = *t2++;
342	      break;
343
344	    case IGNORE_SPACE_CHANGE:
345	      /* For -b, advance past any sequence of white space in
346		 line 1 and consider it just one space, or nothing at
347		 all if it is at the end of the line.  */
348	      if (isspace (c1))
349		{
350		  while (c1 != '\n')
351		    {
352		      c1 = *t1++;
353		      if (! isspace (c1))
354			{
355			  --t1;
356			  c1 = ' ';
357			  break;
358			}
359		    }
360		}
361
362	      /* Likewise for line 2.  */
363	      if (isspace (c2))
364		{
365		  while (c2 != '\n')
366		    {
367		      c2 = *t2++;
368		      if (! isspace (c2))
369			{
370			  --t2;
371			  c2 = ' ';
372			  break;
373			}
374		    }
375		}
376
377	      if (c1 != c2)
378		{
379		  /* If we went too far when doing the simple test
380		     for equality, go back to the first non-white-space
381		     character in both sides and try again.  */
382		  if (c2 == ' ' && c1 != '\n'
383		      && s1 + 1 < t1
384		      && isspace ((unsigned char) t1[-2]))
385		    {
386		      --t1;
387		      continue;
388		    }
389		  if (c1 == ' ' && c2 != '\n'
390		      && s2 + 1 < t2
391		      && isspace ((unsigned char) t2[-2]))
392		    {
393		      --t2;
394		      continue;
395		    }
396		}
397
398	      break;
399
400	    case IGNORE_TAB_EXPANSION:
401	      if ((c1 == ' ' && c2 == '\t')
402		  || (c1 == '\t' && c2 == ' '))
403		{
404		  size_t column2 = column;
405		  for (;; c1 = *t1++)
406		    {
407		      if (c1 == ' ')
408			column++;
409		      else if (c1 == '\t')
410			column += tabsize - column % tabsize;
411		      else
412			break;
413		    }
414		  for (;; c2 = *t2++)
415		    {
416		      if (c2 == ' ')
417			column2++;
418		      else if (c2 == '\t')
419			column2 += tabsize - column2 % tabsize;
420		      else
421			break;
422		    }
423		  if (column != column2)
424		    return true;
425		}
426	      break;
427
428	    case IGNORE_NO_WHITE_SPACE:
429	      break;
430	    }
431
432	  /* Lowercase all letters if -i is specified.  */
433
434	  if (ignore_case)
435	    {
436	      c1 = tolower (c1);
437	      c2 = tolower (c2);
438	    }
439
440	  if (c1 != c2)
441	    break;
442	}
443      if (c1 == '\n')
444	return false;
445
446      column += c1 == '\t' ? tabsize - column % tabsize : 1;
447    }
448
449  return true;
450}
451
452/* Find the consecutive changes at the start of the script START.
453   Return the last link before the first gap.  */
454
455struct change *
456find_change (struct change *start)
457{
458  return start;
459}
460
461struct change *
462find_reverse_change (struct change *start)
463{
464  return start;
465}
466
467/* Divide SCRIPT into pieces by calling HUNKFUN and
468   print each piece with PRINTFUN.
469   Both functions take one arg, an edit script.
470
471   HUNKFUN is called with the tail of the script
472   and returns the last link that belongs together with the start
473   of the tail.
474
475   PRINTFUN takes a subscript which belongs together (with a null
476   link at the end) and prints it.  */
477
478void
479print_script (struct change *script,
480	      struct change * (*hunkfun) (struct change *),
481	      void (*printfun) (struct change *))
482{
483  struct change *next = script;
484
485  while (next)
486    {
487      struct change *this, *end;
488
489      /* Find a set of changes that belong together.  */
490      this = next;
491      end = (*hunkfun) (next);
492
493      /* Disconnect them from the rest of the changes,
494	 making them a hunk, and remember the rest for next iteration.  */
495      next = end->link;
496      end->link = 0;
497#ifdef DEBUG
498      debug_script (this);
499#endif
500
501      /* Print this hunk.  */
502      (*printfun) (this);
503
504      /* Reconnect the script so it will all be freed properly.  */
505      end->link = next;
506    }
507}
508
509/* Print the text of a single line LINE,
510   flagging it with the characters in LINE_FLAG (which say whether
511   the line is inserted, deleted, changed, etc.).  */
512
513void
514print_1_line (char const *line_flag, char const *const *line)
515{
516  char const *base = line[0], *limit = line[1]; /* Help the compiler.  */
517  FILE *out = outfile; /* Help the compiler some more.  */
518  char const *flag_format = 0;
519
520  /* If -T was specified, use a Tab between the line-flag and the text.
521     Otherwise use a Space (as Unix diff does).
522     Print neither space nor tab if line-flags are empty.  */
523
524  if (line_flag && *line_flag)
525    {
526      flag_format = initial_tab ? "%s\t" : "%s ";
527      fprintf (out, flag_format, line_flag);
528    }
529
530  output_1_line (base, limit, flag_format, line_flag);
531
532  if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
533    fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
534}
535
536/* Output a line from BASE up to LIMIT.
537   With -t, expand white space characters to spaces, and if FLAG_FORMAT
538   is nonzero, output it with argument LINE_FLAG after every
539   internal carriage return, so that tab stops continue to line up.  */
540
541void
542output_1_line (char const *base, char const *limit, char const *flag_format,
543	       char const *line_flag)
544{
545  if (!expand_tabs)
546    fwrite (base, sizeof (char), limit - base, outfile);
547  else
548    {
549      register FILE *out = outfile;
550      register unsigned char c;
551      register char const *t = base;
552      register size_t column = 0;
553      size_t tab_size = tabsize;
554
555      while (t < limit)
556	switch ((c = *t++))
557	  {
558	  case '\t':
559	    {
560	      size_t spaces = tab_size - column % tab_size;
561	      column += spaces;
562	      do
563		putc (' ', out);
564	      while (--spaces);
565	    }
566	    break;
567
568	  case '\r':
569	    putc (c, out);
570	    if (flag_format && t < limit && *t != '\n')
571	      fprintf (out, flag_format, line_flag);
572	    column = 0;
573	    break;
574
575	  case '\b':
576	    if (column == 0)
577	      continue;
578	    column--;
579	    putc (c, out);
580	    break;
581
582	  default:
583	    column += isprint (c) != 0;
584	    putc (c, out);
585	    break;
586	  }
587    }
588}
589
590char const change_letter[] = { 0, 'd', 'a', 'c' };
591
592/* Translate an internal line number (an index into diff's table of lines)
593   into an actual line number in the input file.
594   The internal line number is I.  FILE points to the data on the file.
595
596   Internal line numbers count from 0 starting after the prefix.
597   Actual line numbers count from 1 within the entire file.  */
598
599lin
600translate_line_number (struct file_data const *file, lin i)
601{
602  return i + file->prefix_lines + 1;
603}
604
605/* Translate a line number range.  This is always done for printing,
606   so for convenience translate to long int rather than lin, so that the
607   caller can use printf with "%ld" without casting.  */
608
609void
610translate_range (struct file_data const *file,
611		 lin a, lin b,
612		 long int *aptr, long int *bptr)
613{
614  *aptr = translate_line_number (file, a - 1) + 1;
615  *bptr = translate_line_number (file, b + 1) - 1;
616}
617
618/* Print a pair of line numbers with SEPCHAR, translated for file FILE.
619   If the two numbers are identical, print just one number.
620
621   Args A and B are internal line numbers.
622   We print the translated (real) line numbers.  */
623
624void
625print_number_range (char sepchar, struct file_data *file, lin a, lin b)
626{
627  long int trans_a, trans_b;
628  translate_range (file, a, b, &trans_a, &trans_b);
629
630  /* Note: we can have B < A in the case of a range of no lines.
631     In this case, we should print the line number before the range,
632     which is B.  */
633  if (trans_b > trans_a)
634    fprintf (outfile, "%ld%c%ld", trans_a, sepchar, trans_b);
635  else
636    fprintf (outfile, "%ld", trans_b);
637}
638
639/* Look at a hunk of edit script and report the range of lines in each file
640   that it applies to.  HUNK is the start of the hunk, which is a chain
641   of `struct change'.  The first and last line numbers of file 0 are stored in
642   *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
643   Note that these are internal line numbers that count from 0.
644
645   If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
646
647   Return UNCHANGED if only ignorable lines are inserted or deleted,
648   OLD if lines of file 0 are deleted,
649   NEW if lines of file 1 are inserted,
650   and CHANGED if both kinds of changes are found. */
651
652enum changes
653analyze_hunk (struct change *hunk,
654	      lin *first0, lin *last0,
655	      lin *first1, lin *last1)
656{
657  struct change *next;
658  lin l0, l1;
659  lin show_from, show_to;
660  lin i;
661  bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
662  size_t trivial_length = ignore_blank_lines - 1;
663    /* If 0, ignore zero-length lines;
664       if SIZE_MAX, do not ignore lines just because of their length.  */
665  bool skip_leading_white_space =
666    (ignore_blank_lines && IGNORE_SPACE_CHANGE <= ignore_white_space);
667
668  char const * const *linbuf0 = files[0].linbuf;  /* Help the compiler.  */
669  char const * const *linbuf1 = files[1].linbuf;
670
671  show_from = show_to = 0;
672
673  *first0 = hunk->line0;
674  *first1 = hunk->line1;
675
676  next = hunk;
677  do
678    {
679      l0 = next->line0 + next->deleted - 1;
680      l1 = next->line1 + next->inserted - 1;
681      show_from += next->deleted;
682      show_to += next->inserted;
683
684      for (i = next->line0; i <= l0 && trivial; i++)
685	{
686	  char const *line = linbuf0[i];
687	  char const *newline = linbuf0[i + 1] - 1;
688	  size_t len = newline - line;
689	  char const *p = line;
690	  if (skip_leading_white_space)
691	    while (isspace ((unsigned char) *p) && *p != '\n')
692	      p++;
693	  if (newline - p != trivial_length
694	      && (! ignore_regexp.fastmap
695		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
696	    trivial = 0;
697	}
698
699      for (i = next->line1; i <= l1 && trivial; i++)
700	{
701	  char const *line = linbuf1[i];
702	  char const *newline = linbuf1[i + 1] - 1;
703	  size_t len = newline - line;
704	  char const *p = line;
705	  if (skip_leading_white_space)
706	    while (isspace ((unsigned char) *p) && *p != '\n')
707	      p++;
708	  if (newline - p != trivial_length
709	      && (! ignore_regexp.fastmap
710		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
711	    trivial = 0;
712	}
713    }
714  while ((next = next->link) != 0);
715
716  *last0 = l0;
717  *last1 = l1;
718
719  /* If all inserted or deleted lines are ignorable,
720     tell the caller to ignore this hunk.  */
721
722  if (trivial)
723    return UNCHANGED;
724
725  return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
726}
727
728/* Concatenate three strings, returning a newly malloc'd string.  */
729
730char *
731concat (char const *s1, char const *s2, char const *s3)
732{
733  char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
734  sprintf (new, "%s%s%s", s1, s2, s3);
735  return new;
736}
737
738/* Yield a new block of SIZE bytes, initialized to zero.  */
739
740void *
741zalloc (size_t size)
742{
743  void *p = xmalloc (size);
744  memset (p, 0, size);
745  return p;
746}
747
748/* Yield the newly malloc'd pathname
749   of the file in DIR whose filename is FILE.  */
750
751char *
752dir_file_pathname (char const *dir, char const *file)
753{
754  char const *base = base_name (dir);
755  bool omit_slash = !*base || base[strlen (base) - 1] == '/';
756  return concat (dir, "/" + omit_slash, file);
757}
758
759void
760debug_script (struct change *sp)
761{
762  fflush (stdout);
763
764  for (; sp; sp = sp->link)
765    {
766      long int line0 = sp->line0;
767      long int line1 = sp->line1;
768      long int deleted = sp->deleted;
769      long int inserted = sp->inserted;
770      fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n",
771	       line0, line1, deleted, inserted);
772    }
773
774  fflush (stderr);
775}
776