xref: /openbsd-src/gnu/usr.bin/cvs/diff/util.c (revision b2346922a76a50a89e33beab4ebbc0950de8a8df)
12286d8edStholo /* Support routines for GNU DIFF.
2*b2346922Stholo    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc.
32286d8edStholo 
42286d8edStholo This file is part of GNU DIFF.
52286d8edStholo 
62286d8edStholo GNU DIFF is free software; you can redistribute it and/or modify
72286d8edStholo it under the terms of the GNU General Public License as published by
82286d8edStholo the Free Software Foundation; either version 2, or (at your option)
92286d8edStholo any later version.
102286d8edStholo 
112286d8edStholo GNU DIFF is distributed in the hope that it will be useful,
122286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
132286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
142286d8edStholo GNU General Public License for more details.
152286d8edStholo 
162286d8edStholo You should have received a copy of the GNU General Public License
172286d8edStholo along with GNU DIFF; see the file COPYING.  If not, write to
182286d8edStholo the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
192286d8edStholo 
202286d8edStholo #include "diff.h"
212286d8edStholo 
22*b2346922Stholo #ifdef __STDC__
23*b2346922Stholo #include <stdarg.h>
24*b2346922Stholo #else
25*b2346922Stholo #include <varargs.h>
26*b2346922Stholo #endif
27*b2346922Stholo 
28*b2346922Stholo #ifndef strerror
29*b2346922Stholo extern char *strerror ();
30*b2346922Stholo #endif
31*b2346922Stholo 
322286d8edStholo /* Queue up one-line messages to be printed at the end,
332286d8edStholo    when -l is specified.  Each message is recorded with a `struct msg'.  */
342286d8edStholo 
352286d8edStholo struct msg
362286d8edStholo {
372286d8edStholo   struct msg *next;
382286d8edStholo   char const *format;
392286d8edStholo   char const *arg1;
402286d8edStholo   char const *arg2;
412286d8edStholo   char const *arg3;
422286d8edStholo   char const *arg4;
432286d8edStholo };
442286d8edStholo 
452286d8edStholo /* Head of the chain of queues messages.  */
462286d8edStholo 
472286d8edStholo static struct msg *msg_chain;
482286d8edStholo 
492286d8edStholo /* Tail of the chain of queues messages.  */
502286d8edStholo 
512286d8edStholo static struct msg **msg_chain_end = &msg_chain;
522286d8edStholo 
532286d8edStholo /* Use when a system call returns non-zero status.
542286d8edStholo    TEXT should normally be the file name.  */
552286d8edStholo 
562286d8edStholo void
572286d8edStholo perror_with_name (text)
582286d8edStholo      char const *text;
592286d8edStholo {
602286d8edStholo   int e = errno;
61*b2346922Stholo 
62*b2346922Stholo   if (callbacks && callbacks->error)
63*b2346922Stholo     (*callbacks->error) ("%s: %s", text, strerror (e));
64*b2346922Stholo   else
65*b2346922Stholo     {
662286d8edStholo       fprintf (stderr, "%s: ", diff_program_name);
672286d8edStholo       errno = e;
682286d8edStholo       perror (text);
692286d8edStholo     }
70*b2346922Stholo }
712286d8edStholo 
722286d8edStholo /* Use when a system call returns non-zero status and that is fatal.  */
732286d8edStholo 
742286d8edStholo void
752286d8edStholo pfatal_with_name (text)
762286d8edStholo      char const *text;
772286d8edStholo {
782286d8edStholo   int e = errno;
792286d8edStholo   print_message_queue ();
80*b2346922Stholo   if (callbacks && callbacks->error)
81*b2346922Stholo     (*callbacks->error) ("%s: %s", text, strerror (e));
82*b2346922Stholo   else
83*b2346922Stholo     {
842286d8edStholo       fprintf (stderr, "%s: ", diff_program_name);
852286d8edStholo       errno = e;
862286d8edStholo       perror (text);
87*b2346922Stholo     }
882286d8edStholo   DIFF_ABORT (2);
892286d8edStholo }
902286d8edStholo 
912286d8edStholo /* Print an error message from the format-string FORMAT
922286d8edStholo    with args ARG1 and ARG2.  */
932286d8edStholo 
942286d8edStholo void
952286d8edStholo diff_error (format, arg, arg1)
962286d8edStholo      char const *format, *arg, *arg1;
972286d8edStholo {
98*b2346922Stholo   if (callbacks && callbacks->error)
99*b2346922Stholo     (*callbacks->error) (format, arg, arg1);
100*b2346922Stholo   else
101*b2346922Stholo     {
1022286d8edStholo       fprintf (stderr, "%s: ", diff_program_name);
1032286d8edStholo       fprintf (stderr, format, arg, arg1);
1042286d8edStholo       fprintf (stderr, "\n");
1052286d8edStholo     }
106*b2346922Stholo }
1072286d8edStholo 
1082286d8edStholo /* Print an error message containing the string TEXT, then exit.  */
1092286d8edStholo 
1102286d8edStholo void
1112286d8edStholo fatal (m)
1122286d8edStholo      char const *m;
1132286d8edStholo {
1142286d8edStholo   print_message_queue ();
1152286d8edStholo   diff_error ("%s", m, 0);
1162286d8edStholo   DIFF_ABORT (2);
1172286d8edStholo }
1182286d8edStholo 
1192286d8edStholo /* Like printf, except if -l in effect then save the message and print later.
1202286d8edStholo    This is used for things like "binary files differ" and "Only in ...".  */
1212286d8edStholo 
1222286d8edStholo void
1232286d8edStholo message (format, arg1, arg2)
1242286d8edStholo      char const *format, *arg1, *arg2;
1252286d8edStholo {
1262286d8edStholo   message5 (format, arg1, arg2, 0, 0);
1272286d8edStholo }
1282286d8edStholo 
1292286d8edStholo void
1302286d8edStholo message5 (format, arg1, arg2, arg3, arg4)
1312286d8edStholo      char const *format, *arg1, *arg2, *arg3, *arg4;
1322286d8edStholo {
1332286d8edStholo   if (paginate_flag)
1342286d8edStholo     {
1352286d8edStholo       struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
1362286d8edStholo       new->format = format;
1372286d8edStholo       new->arg1 = concat (arg1, "", "");
1382286d8edStholo       new->arg2 = concat (arg2, "", "");
1392286d8edStholo       new->arg3 = arg3 ? concat (arg3, "", "") : 0;
1402286d8edStholo       new->arg4 = arg4 ? concat (arg4, "", "") : 0;
1412286d8edStholo       new->next = 0;
1422286d8edStholo       *msg_chain_end = new;
1432286d8edStholo       msg_chain_end = &new->next;
1442286d8edStholo     }
1452286d8edStholo   else
1462286d8edStholo     {
1472286d8edStholo       if (sdiff_help_sdiff)
148*b2346922Stholo 	write_output (" ", 1);
149*b2346922Stholo       printf_output (format, arg1, arg2, arg3, arg4);
1502286d8edStholo     }
1512286d8edStholo }
1522286d8edStholo 
1532286d8edStholo /* Output all the messages that were saved up by calls to `message'.  */
1542286d8edStholo 
1552286d8edStholo void
1562286d8edStholo print_message_queue ()
1572286d8edStholo {
1582286d8edStholo   struct msg *m;
1592286d8edStholo 
1602286d8edStholo   for (m = msg_chain; m; m = m->next)
161*b2346922Stholo     printf_output (m->format, m->arg1, m->arg2, m->arg3, m->arg4);
1622286d8edStholo }
1632286d8edStholo 
1642286d8edStholo /* Call before outputting the results of comparing files NAME0 and NAME1
1652286d8edStholo    to set up OUTFILE, the stdio stream for the output to go to.
1662286d8edStholo 
1672286d8edStholo    Usually, OUTFILE is just stdout.  But when -l was specified
1682286d8edStholo    we fork off a `pr' and make OUTFILE a pipe to it.
1692286d8edStholo    `pr' then outputs to our stdout.  */
1702286d8edStholo 
1712286d8edStholo static char const *current_name0;
1722286d8edStholo static char const *current_name1;
1732286d8edStholo static int current_depth;
1742286d8edStholo 
1752286d8edStholo static int output_in_progress = 0;
1762286d8edStholo 
1772286d8edStholo void
1782286d8edStholo setup_output (name0, name1, depth)
1792286d8edStholo      char const *name0, *name1;
1802286d8edStholo      int depth;
1812286d8edStholo {
1822286d8edStholo   current_name0 = name0;
1832286d8edStholo   current_name1 = name1;
1842286d8edStholo   current_depth = depth;
1852286d8edStholo }
1862286d8edStholo 
1872286d8edStholo #if HAVE_FORK && defined (PR_PROGRAM)
1882286d8edStholo static pid_t pr_pid;
1892286d8edStholo #endif
1902286d8edStholo 
1912286d8edStholo void
1922286d8edStholo begin_output ()
1932286d8edStholo {
1942286d8edStholo   char *name;
1952286d8edStholo 
1962286d8edStholo   if (output_in_progress)
1972286d8edStholo     return;
1982286d8edStholo   output_in_progress = 1;
1992286d8edStholo 
2002286d8edStholo   /* Construct the header of this piece of diff.  */
2012286d8edStholo   name = xmalloc (strlen (current_name0) + strlen (current_name1)
2022286d8edStholo 		  + strlen (switch_string) + 7);
2032286d8edStholo   /* Posix.2 section 4.17.6.1.1 specifies this format.  But there is a
2042286d8edStholo      bug in the first printing (IEEE Std 1003.2-1992 p 251 l 3304):
2052286d8edStholo      it says that we must print only the last component of the pathnames.
2062286d8edStholo      This requirement is silly and does not match historical practice.  */
2072286d8edStholo   sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
2082286d8edStholo 
209*b2346922Stholo   if (paginate_flag && callbacks && callbacks->write_output)
210*b2346922Stholo     fatal ("can't paginate when using library callbacks");
211*b2346922Stholo 
2122286d8edStholo   if (paginate_flag)
2132286d8edStholo     {
2142286d8edStholo       /* Make OUTFILE a pipe to a subsidiary `pr'.  */
2152286d8edStholo 
2162286d8edStholo #ifdef PR_PROGRAM
2172286d8edStholo 
2182286d8edStholo # if HAVE_FORK
2192286d8edStholo       int pipes[2];
2202286d8edStholo 
2212286d8edStholo       if (pipe (pipes) != 0)
2222286d8edStholo 	pfatal_with_name ("pipe");
2232286d8edStholo 
2242286d8edStholo       fflush (stdout);
2252286d8edStholo 
2262286d8edStholo       pr_pid = vfork ();
2272286d8edStholo       if (pr_pid < 0)
2282286d8edStholo 	pfatal_with_name ("vfork");
2292286d8edStholo 
2302286d8edStholo       if (pr_pid == 0)
2312286d8edStholo 	{
2322286d8edStholo 	  close (pipes[1]);
2332286d8edStholo 	  if (pipes[0] != STDIN_FILENO)
2342286d8edStholo 	    {
2352286d8edStholo 	      if (dup2 (pipes[0], STDIN_FILENO) < 0)
2362286d8edStholo 		pfatal_with_name ("dup2");
2372286d8edStholo 	      close (pipes[0]);
2382286d8edStholo 	    }
2392286d8edStholo 
2402286d8edStholo 	  execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, 0);
2412286d8edStholo 	  pfatal_with_name (PR_PROGRAM);
2422286d8edStholo 	}
2432286d8edStholo       else
2442286d8edStholo 	{
2452286d8edStholo 	  close (pipes[0]);
2462286d8edStholo 	  outfile = fdopen (pipes[1], "w");
2472286d8edStholo 	  if (!outfile)
2482286d8edStholo 	    pfatal_with_name ("fdopen");
2492286d8edStholo 	}
2502286d8edStholo # else /* ! HAVE_FORK */
2512286d8edStholo       char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10);
2522286d8edStholo       char *p;
2532286d8edStholo       char const *a = name;
2542286d8edStholo       sprintf (command, "%s -f -h ", PR_PROGRAM);
2552286d8edStholo       p = command + strlen (command);
2562286d8edStholo       SYSTEM_QUOTE_ARG (p, a);
2572286d8edStholo       *p = 0;
2582286d8edStholo       outfile = popen (command, "w");
2592286d8edStholo       if (!outfile)
2602286d8edStholo 	pfatal_with_name (command);
2612286d8edStholo       free (command);
2622286d8edStholo # endif /* ! HAVE_FORK */
2632286d8edStholo #else
2642286d8edStholo       fatal ("This port does not support the --paginate option to diff.");
2652286d8edStholo #endif
2662286d8edStholo     }
2672286d8edStholo   else
2682286d8edStholo     {
2692286d8edStholo 
2702286d8edStholo       /* If -l was not specified, output the diff straight to `stdout'.  */
2712286d8edStholo 
2722286d8edStholo       /* If handling multiple files (because scanning a directory),
2732286d8edStholo 	 print which files the following output is about.  */
2742286d8edStholo       if (current_depth > 0)
275*b2346922Stholo 	printf_output ("%s\n", name);
2762286d8edStholo     }
2772286d8edStholo 
2782286d8edStholo   free (name);
2792286d8edStholo 
2802286d8edStholo   /* A special header is needed at the beginning of context output.  */
2812286d8edStholo   switch (output_style)
2822286d8edStholo     {
2832286d8edStholo     case OUTPUT_CONTEXT:
2842286d8edStholo       print_context_header (files, 0);
2852286d8edStholo       break;
2862286d8edStholo 
2872286d8edStholo     case OUTPUT_UNIFIED:
2882286d8edStholo       print_context_header (files, 1);
2892286d8edStholo       break;
2902286d8edStholo 
2912286d8edStholo     default:
2922286d8edStholo       break;
2932286d8edStholo     }
2942286d8edStholo }
2952286d8edStholo 
2962286d8edStholo /* Call after the end of output of diffs for one file.
2972286d8edStholo    If -l was given, close OUTFILE and get rid of the `pr' subfork.  */
2982286d8edStholo 
2992286d8edStholo void
3002286d8edStholo finish_output ()
3012286d8edStholo {
3022286d8edStholo   if (paginate_flag && outfile != 0 && outfile != stdout)
3032286d8edStholo     {
3042286d8edStholo #ifdef PR_PROGRAM
3052286d8edStholo       int wstatus;
3062286d8edStholo       if (ferror (outfile))
3072286d8edStholo 	fatal ("write error");
3082286d8edStholo # if ! HAVE_FORK
3092286d8edStholo       wstatus = pclose (outfile);
3102286d8edStholo # else /* HAVE_FORK */
3112286d8edStholo       if (fclose (outfile) != 0)
3122286d8edStholo 	pfatal_with_name ("write error");
3132286d8edStholo       if (waitpid (pr_pid, &wstatus, 0) < 0)
3142286d8edStholo 	pfatal_with_name ("waitpid");
3152286d8edStholo # endif /* HAVE_FORK */
3162286d8edStholo       if (wstatus != 0)
3172286d8edStholo 	fatal ("subsidiary pr failed");
3182286d8edStholo #else
3192286d8edStholo       fatal ("internal error in finish_output");
3202286d8edStholo #endif
3212286d8edStholo     }
3222286d8edStholo 
3232286d8edStholo   output_in_progress = 0;
3242286d8edStholo }
325*b2346922Stholo 
326*b2346922Stholo /* Write something to the output file.  */
327*b2346922Stholo 
328*b2346922Stholo void
329*b2346922Stholo write_output (text, len)
330*b2346922Stholo      char const *text;
331*b2346922Stholo      size_t len;
332*b2346922Stholo {
333*b2346922Stholo   if (callbacks && callbacks->write_output)
334*b2346922Stholo     (*callbacks->write_output) (text, len);
335*b2346922Stholo   else if (len == 1)
336*b2346922Stholo     putc (*text, outfile);
337*b2346922Stholo   else
338*b2346922Stholo     fwrite (text, sizeof (char), len, outfile);
339*b2346922Stholo }
340*b2346922Stholo 
341*b2346922Stholo /* Printf something to the output file.  */
342*b2346922Stholo 
343*b2346922Stholo #ifdef __STDC__
344*b2346922Stholo #define VA_START(args, lastarg) va_start(args, lastarg)
345*b2346922Stholo #else /* ! __STDC__ */
346*b2346922Stholo #define VA_START(args, lastarg) va_start(args)
347*b2346922Stholo #endif /* __STDC__ */
348*b2346922Stholo 
349*b2346922Stholo void
350*b2346922Stholo #if defined (__STDC__)
351*b2346922Stholo printf_output (const char *format, ...)
352*b2346922Stholo #else
353*b2346922Stholo printf_output (format, va_alist)
354*b2346922Stholo      char const *format;
355*b2346922Stholo      va_dcl
356*b2346922Stholo #endif
357*b2346922Stholo {
358*b2346922Stholo   va_list args;
359*b2346922Stholo 
360*b2346922Stholo   VA_START (args, format);
361*b2346922Stholo   if (callbacks && callbacks->write_output)
362*b2346922Stholo     {
363*b2346922Stholo       char *p;
364*b2346922Stholo 
365*b2346922Stholo       p = NULL;
366*b2346922Stholo       vasprintf (&p, format, args);
367*b2346922Stholo       if (p == NULL)
368*b2346922Stholo 	fatal ("out of memory");
369*b2346922Stholo       (*callbacks->write_output) (p, strlen (p));
370*b2346922Stholo       free (p);
371*b2346922Stholo     }
372*b2346922Stholo   else
373*b2346922Stholo     vfprintf (outfile, format, args);
374*b2346922Stholo   va_end (args);
375*b2346922Stholo }
376*b2346922Stholo 
377*b2346922Stholo /* Flush the output file.  */
378*b2346922Stholo 
379*b2346922Stholo void
380*b2346922Stholo flush_output ()
381*b2346922Stholo {
382*b2346922Stholo   if (callbacks && callbacks->flush_output)
383*b2346922Stholo     (*callbacks->flush_output) ();
384*b2346922Stholo   else
385*b2346922Stholo     fflush (outfile);
386*b2346922Stholo }
3872286d8edStholo 
3882286d8edStholo /* Compare two lines (typically one from each input file)
3892286d8edStholo    according to the command line options.
3902286d8edStholo    For efficiency, this is invoked only when the lines do not match exactly
3912286d8edStholo    but an option like -i might cause us to ignore the difference.
3922286d8edStholo    Return nonzero if the lines differ.  */
3932286d8edStholo 
3942286d8edStholo int
3952286d8edStholo line_cmp (s1, s2)
3962286d8edStholo      char const *s1, *s2;
3972286d8edStholo {
3982286d8edStholo   register unsigned char const *t1 = (unsigned char const *) s1;
3992286d8edStholo   register unsigned char const *t2 = (unsigned char const *) s2;
4002286d8edStholo 
4012286d8edStholo   while (1)
4022286d8edStholo     {
4032286d8edStholo       register unsigned char c1 = *t1++;
4042286d8edStholo       register unsigned char c2 = *t2++;
4052286d8edStholo 
4062286d8edStholo       /* Test for exact char equality first, since it's a common case.  */
4072286d8edStholo       if (c1 != c2)
4082286d8edStholo 	{
4092286d8edStholo 	  /* Ignore horizontal white space if -b or -w is specified.  */
4102286d8edStholo 
4112286d8edStholo 	  if (ignore_all_space_flag)
4122286d8edStholo 	    {
4132286d8edStholo 	      /* For -w, just skip past any white space.  */
4142286d8edStholo 	      while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
4152286d8edStholo 	      while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
4162286d8edStholo 	    }
4172286d8edStholo 	  else if (ignore_space_change_flag)
4182286d8edStholo 	    {
4192286d8edStholo 	      /* For -b, advance past any sequence of white space in line 1
4202286d8edStholo 		 and consider it just one Space, or nothing at all
4212286d8edStholo 		 if it is at the end of the line.  */
4222286d8edStholo 	      if (ISSPACE (c1))
4232286d8edStholo 		{
4242286d8edStholo 		  while (c1 != '\n')
4252286d8edStholo 		    {
4262286d8edStholo 		      c1 = *t1++;
4272286d8edStholo 		      if (! ISSPACE (c1))
4282286d8edStholo 			{
4292286d8edStholo 			  --t1;
4302286d8edStholo 			  c1 = ' ';
4312286d8edStholo 			  break;
4322286d8edStholo 			}
4332286d8edStholo 		    }
4342286d8edStholo 		}
4352286d8edStholo 
4362286d8edStholo 	      /* Likewise for line 2.  */
4372286d8edStholo 	      if (ISSPACE (c2))
4382286d8edStholo 		{
4392286d8edStholo 		  while (c2 != '\n')
4402286d8edStholo 		    {
4412286d8edStholo 		      c2 = *t2++;
4422286d8edStholo 		      if (! ISSPACE (c2))
4432286d8edStholo 			{
4442286d8edStholo 			  --t2;
4452286d8edStholo 			  c2 = ' ';
4462286d8edStholo 			  break;
4472286d8edStholo 			}
4482286d8edStholo 		    }
4492286d8edStholo 		}
4502286d8edStholo 
4512286d8edStholo 	      if (c1 != c2)
4522286d8edStholo 		{
4532286d8edStholo 		  /* If we went too far when doing the simple test
4542286d8edStholo 		     for equality, go back to the first non-white-space
4552286d8edStholo 		     character in both sides and try again.  */
4562286d8edStholo 		  if (c2 == ' ' && c1 != '\n'
4572286d8edStholo 		      && (unsigned char const *) s1 + 1 < t1
4582286d8edStholo 		      && ISSPACE(t1[-2]))
4592286d8edStholo 		    {
4602286d8edStholo 		      --t1;
4612286d8edStholo 		      continue;
4622286d8edStholo 		    }
4632286d8edStholo 		  if (c1 == ' ' && c2 != '\n'
4642286d8edStholo 		      && (unsigned char const *) s2 + 1 < t2
4652286d8edStholo 		      && ISSPACE(t2[-2]))
4662286d8edStholo 		    {
4672286d8edStholo 		      --t2;
4682286d8edStholo 		      continue;
4692286d8edStholo 		    }
4702286d8edStholo 		}
4712286d8edStholo 	    }
4722286d8edStholo 
4732286d8edStholo 	  /* Lowercase all letters if -i is specified.  */
4742286d8edStholo 
4752286d8edStholo 	  if (ignore_case_flag)
4762286d8edStholo 	    {
4772286d8edStholo 	      if (ISUPPER (c1))
4782286d8edStholo 		c1 = tolower (c1);
4792286d8edStholo 	      if (ISUPPER (c2))
4802286d8edStholo 		c2 = tolower (c2);
4812286d8edStholo 	    }
4822286d8edStholo 
4832286d8edStholo 	  if (c1 != c2)
4842286d8edStholo 	    break;
4852286d8edStholo 	}
4862286d8edStholo       if (c1 == '\n')
4872286d8edStholo 	return 0;
4882286d8edStholo     }
4892286d8edStholo 
4902286d8edStholo   return (1);
4912286d8edStholo }
4922286d8edStholo 
4932286d8edStholo /* Find the consecutive changes at the start of the script START.
4942286d8edStholo    Return the last link before the first gap.  */
4952286d8edStholo 
4962286d8edStholo struct change *
4972286d8edStholo find_change (start)
4982286d8edStholo      struct change *start;
4992286d8edStholo {
5002286d8edStholo   return start;
5012286d8edStholo }
5022286d8edStholo 
5032286d8edStholo struct change *
5042286d8edStholo find_reverse_change (start)
5052286d8edStholo      struct change *start;
5062286d8edStholo {
5072286d8edStholo   return start;
5082286d8edStholo }
5092286d8edStholo 
5102286d8edStholo /* Divide SCRIPT into pieces by calling HUNKFUN and
5112286d8edStholo    print each piece with PRINTFUN.
5122286d8edStholo    Both functions take one arg, an edit script.
5132286d8edStholo 
5142286d8edStholo    HUNKFUN is called with the tail of the script
5152286d8edStholo    and returns the last link that belongs together with the start
5162286d8edStholo    of the tail.
5172286d8edStholo 
5182286d8edStholo    PRINTFUN takes a subscript which belongs together (with a null
5192286d8edStholo    link at the end) and prints it.  */
5202286d8edStholo 
5212286d8edStholo void
5222286d8edStholo print_script (script, hunkfun, printfun)
5232286d8edStholo      struct change *script;
5242286d8edStholo      struct change * (*hunkfun) PARAMS((struct change *));
5252286d8edStholo      void (*printfun) PARAMS((struct change *));
5262286d8edStholo {
5272286d8edStholo   struct change *next = script;
5282286d8edStholo 
5292286d8edStholo   while (next)
5302286d8edStholo     {
5312286d8edStholo       struct change *this, *end;
5322286d8edStholo 
5332286d8edStholo       /* Find a set of changes that belong together.  */
5342286d8edStholo       this = next;
5352286d8edStholo       end = (*hunkfun) (next);
5362286d8edStholo 
5372286d8edStholo       /* Disconnect them from the rest of the changes,
5382286d8edStholo 	 making them a hunk, and remember the rest for next iteration.  */
5392286d8edStholo       next = end->link;
5402286d8edStholo       end->link = 0;
5412286d8edStholo #ifdef DEBUG
5422286d8edStholo       debug_script (this);
5432286d8edStholo #endif
5442286d8edStholo 
5452286d8edStholo       /* Print this hunk.  */
5462286d8edStholo       (*printfun) (this);
5472286d8edStholo 
5482286d8edStholo       /* Reconnect the script so it will all be freed properly.  */
5492286d8edStholo       end->link = next;
5502286d8edStholo     }
5512286d8edStholo }
5522286d8edStholo 
5532286d8edStholo /* Print the text of a single line LINE,
5542286d8edStholo    flagging it with the characters in LINE_FLAG (which say whether
5552286d8edStholo    the line is inserted, deleted, changed, etc.).  */
5562286d8edStholo 
5572286d8edStholo void
5582286d8edStholo print_1_line (line_flag, line)
5592286d8edStholo      char const *line_flag;
5602286d8edStholo      char const * const *line;
5612286d8edStholo {
5622286d8edStholo   char const *text = line[0], *limit = line[1]; /* Help the compiler.  */
5632286d8edStholo   char const *flag_format = 0;
5642286d8edStholo 
5652286d8edStholo   /* If -T was specified, use a Tab between the line-flag and the text.
5662286d8edStholo      Otherwise use a Space (as Unix diff does).
5672286d8edStholo      Print neither space nor tab if line-flags are empty.  */
5682286d8edStholo 
5692286d8edStholo   if (line_flag && *line_flag)
5702286d8edStholo     {
5712286d8edStholo       flag_format = tab_align_flag ? "%s\t" : "%s ";
572*b2346922Stholo       printf_output (flag_format, line_flag);
5732286d8edStholo     }
5742286d8edStholo 
5752286d8edStholo   output_1_line (text, limit, flag_format, line_flag);
5762286d8edStholo 
5772286d8edStholo   if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
578*b2346922Stholo     printf_output ("\n\\ No newline at end of file\n");
5792286d8edStholo }
5802286d8edStholo 
5812286d8edStholo /* Output a line from TEXT up to LIMIT.  Without -t, output verbatim.
5822286d8edStholo    With -t, expand white space characters to spaces, and if FLAG_FORMAT
5832286d8edStholo    is nonzero, output it with argument LINE_FLAG after every
5842286d8edStholo    internal carriage return, so that tab stops continue to line up.  */
5852286d8edStholo 
5862286d8edStholo void
5872286d8edStholo output_1_line (text, limit, flag_format, line_flag)
5882286d8edStholo      char const *text, *limit, *flag_format, *line_flag;
5892286d8edStholo {
5902286d8edStholo   if (!tab_expand_flag)
591*b2346922Stholo     write_output (text, limit - text);
5922286d8edStholo   else
5932286d8edStholo     {
5942286d8edStholo       register unsigned char c;
5952286d8edStholo       register char const *t = text;
5962286d8edStholo       register unsigned column = 0;
597*b2346922Stholo       /* CC is used to avoid taking the address of the register
598*b2346922Stholo          variable C.  */
599*b2346922Stholo       char cc;
6002286d8edStholo 
6012286d8edStholo       while (t < limit)
6022286d8edStholo 	switch ((c = *t++))
6032286d8edStholo 	  {
6042286d8edStholo 	  case '\t':
6052286d8edStholo 	    {
6062286d8edStholo 	      unsigned spaces = TAB_WIDTH - column % TAB_WIDTH;
6072286d8edStholo 	      column += spaces;
6082286d8edStholo 	      do
609*b2346922Stholo 		write_output (" ", 1);
6102286d8edStholo 	      while (--spaces);
6112286d8edStholo 	    }
6122286d8edStholo 	    break;
6132286d8edStholo 
6142286d8edStholo 	  case '\r':
615*b2346922Stholo 	    write_output ("\r", 1);
6162286d8edStholo 	    if (flag_format && t < limit && *t != '\n')
617*b2346922Stholo 	      printf_output (flag_format, line_flag);
6182286d8edStholo 	    column = 0;
6192286d8edStholo 	    break;
6202286d8edStholo 
6212286d8edStholo 	  case '\b':
6222286d8edStholo 	    if (column == 0)
6232286d8edStholo 	      continue;
6242286d8edStholo 	    column--;
625*b2346922Stholo 	    write_output ("\b", 1);
6262286d8edStholo 	    break;
6272286d8edStholo 
6282286d8edStholo 	  default:
6292286d8edStholo 	    if (ISPRINT (c))
6302286d8edStholo 	      column++;
631*b2346922Stholo 	    cc = c;
632*b2346922Stholo 	    write_output (&cc, 1);
6332286d8edStholo 	    break;
6342286d8edStholo 	  }
6352286d8edStholo     }
6362286d8edStholo }
6372286d8edStholo 
6382286d8edStholo int
6392286d8edStholo change_letter (inserts, deletes)
6402286d8edStholo      int inserts, deletes;
6412286d8edStholo {
6422286d8edStholo   if (!inserts)
6432286d8edStholo     return 'd';
6442286d8edStholo   else if (!deletes)
6452286d8edStholo     return 'a';
6462286d8edStholo   else
6472286d8edStholo     return 'c';
6482286d8edStholo }
6492286d8edStholo 
6502286d8edStholo /* Translate an internal line number (an index into diff's table of lines)
6512286d8edStholo    into an actual line number in the input file.
6522286d8edStholo    The internal line number is LNUM.  FILE points to the data on the file.
6532286d8edStholo 
6542286d8edStholo    Internal line numbers count from 0 starting after the prefix.
6552286d8edStholo    Actual line numbers count from 1 within the entire file.  */
6562286d8edStholo 
6572286d8edStholo int
6582286d8edStholo translate_line_number (file, lnum)
6592286d8edStholo      struct file_data const *file;
6602286d8edStholo      int lnum;
6612286d8edStholo {
6622286d8edStholo   return lnum + file->prefix_lines + 1;
6632286d8edStholo }
6642286d8edStholo 
6652286d8edStholo void
6662286d8edStholo translate_range (file, a, b, aptr, bptr)
6672286d8edStholo      struct file_data const *file;
6682286d8edStholo      int a, b;
6692286d8edStholo      int *aptr, *bptr;
6702286d8edStholo {
6712286d8edStholo   *aptr = translate_line_number (file, a - 1) + 1;
6722286d8edStholo   *bptr = translate_line_number (file, b + 1) - 1;
6732286d8edStholo }
6742286d8edStholo 
6752286d8edStholo /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
6762286d8edStholo    If the two numbers are identical, print just one number.
6772286d8edStholo 
6782286d8edStholo    Args A and B are internal line numbers.
6792286d8edStholo    We print the translated (real) line numbers.  */
6802286d8edStholo 
6812286d8edStholo void
6822286d8edStholo print_number_range (sepchar, file, a, b)
6832286d8edStholo      int sepchar;
6842286d8edStholo      struct file_data *file;
6852286d8edStholo      int a, b;
6862286d8edStholo {
6872286d8edStholo   int trans_a, trans_b;
6882286d8edStholo   translate_range (file, a, b, &trans_a, &trans_b);
6892286d8edStholo 
6902286d8edStholo   /* Note: we can have B < A in the case of a range of no lines.
6912286d8edStholo      In this case, we should print the line number before the range,
6922286d8edStholo      which is B.  */
6932286d8edStholo   if (trans_b > trans_a)
694*b2346922Stholo     printf_output ("%d%c%d", trans_a, sepchar, trans_b);
6952286d8edStholo   else
696*b2346922Stholo     printf_output ("%d", trans_b);
6972286d8edStholo }
6982286d8edStholo 
6992286d8edStholo /* Look at a hunk of edit script and report the range of lines in each file
7002286d8edStholo    that it applies to.  HUNK is the start of the hunk, which is a chain
7012286d8edStholo    of `struct change'.  The first and last line numbers of file 0 are stored in
7022286d8edStholo    *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
7032286d8edStholo    Note that these are internal line numbers that count from 0.
7042286d8edStholo 
7052286d8edStholo    If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
7062286d8edStholo 
7072286d8edStholo    Also set *DELETES nonzero if any lines of file 0 are deleted
7082286d8edStholo    and set *INSERTS nonzero if any lines of file 1 are inserted.
7092286d8edStholo    If only ignorable lines are inserted or deleted, both are
7102286d8edStholo    set to 0.  */
7112286d8edStholo 
7122286d8edStholo void
7132286d8edStholo analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
7142286d8edStholo      struct change *hunk;
7152286d8edStholo      int *first0, *last0, *first1, *last1;
7162286d8edStholo      int *deletes, *inserts;
7172286d8edStholo {
7182286d8edStholo   int l0, l1, show_from, show_to;
7192286d8edStholo   int i;
7202286d8edStholo   int trivial = ignore_blank_lines_flag || ignore_regexp_list;
7212286d8edStholo   struct change *next;
7222286d8edStholo 
7232286d8edStholo   show_from = show_to = 0;
7242286d8edStholo 
7252286d8edStholo   *first0 = hunk->line0;
7262286d8edStholo   *first1 = hunk->line1;
7272286d8edStholo 
7282286d8edStholo   next = hunk;
7292286d8edStholo   do
7302286d8edStholo     {
7312286d8edStholo       l0 = next->line0 + next->deleted - 1;
7322286d8edStholo       l1 = next->line1 + next->inserted - 1;
7332286d8edStholo       show_from += next->deleted;
7342286d8edStholo       show_to += next->inserted;
7352286d8edStholo 
7362286d8edStholo       for (i = next->line0; i <= l0 && trivial; i++)
7372286d8edStholo 	if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n')
7382286d8edStholo 	  {
7392286d8edStholo 	    struct regexp_list *r;
7402286d8edStholo 	    char const *line = files[0].linbuf[i];
7412286d8edStholo 	    int len = files[0].linbuf[i + 1] - line;
7422286d8edStholo 
7432286d8edStholo 	    for (r = ignore_regexp_list; r; r = r->next)
7442286d8edStholo 	      if (0 <= re_search (&r->buf, line, len, 0, len, 0))
7452286d8edStholo 		break;	/* Found a match.  Ignore this line.  */
7462286d8edStholo 	    /* If we got all the way through the regexp list without
7472286d8edStholo 	       finding a match, then it's nontrivial.  */
7482286d8edStholo 	    if (!r)
7492286d8edStholo 	      trivial = 0;
7502286d8edStholo 	  }
7512286d8edStholo 
7522286d8edStholo       for (i = next->line1; i <= l1 && trivial; i++)
7532286d8edStholo 	if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n')
7542286d8edStholo 	  {
7552286d8edStholo 	    struct regexp_list *r;
7562286d8edStholo 	    char const *line = files[1].linbuf[i];
7572286d8edStholo 	    int len = files[1].linbuf[i + 1] - line;
7582286d8edStholo 
7592286d8edStholo 	    for (r = ignore_regexp_list; r; r = r->next)
7602286d8edStholo 	      if (0 <= re_search (&r->buf, line, len, 0, len, 0))
7612286d8edStholo 		break;	/* Found a match.  Ignore this line.  */
7622286d8edStholo 	    /* If we got all the way through the regexp list without
7632286d8edStholo 	       finding a match, then it's nontrivial.  */
7642286d8edStholo 	    if (!r)
7652286d8edStholo 	      trivial = 0;
7662286d8edStholo 	  }
7672286d8edStholo     }
7682286d8edStholo   while ((next = next->link) != 0);
7692286d8edStholo 
7702286d8edStholo   *last0 = l0;
7712286d8edStholo   *last1 = l1;
7722286d8edStholo 
7732286d8edStholo   /* If all inserted or deleted lines are ignorable,
7742286d8edStholo      tell the caller to ignore this hunk.  */
7752286d8edStholo 
7762286d8edStholo   if (trivial)
7772286d8edStholo     show_from = show_to = 0;
7782286d8edStholo 
7792286d8edStholo   *deletes = show_from;
7802286d8edStholo   *inserts = show_to;
7812286d8edStholo }
7822286d8edStholo 
7832286d8edStholo /* Concatenate three strings, returning a newly malloc'd string.  */
7842286d8edStholo 
7852286d8edStholo char *
7862286d8edStholo concat (s1, s2, s3)
7872286d8edStholo      char const *s1, *s2, *s3;
7882286d8edStholo {
7892286d8edStholo   size_t len = strlen (s1) + strlen (s2) + strlen (s3);
7902286d8edStholo   char *new = xmalloc (len + 1);
7912286d8edStholo   sprintf (new, "%s%s%s", s1, s2, s3);
7922286d8edStholo   return new;
7932286d8edStholo }
7942286d8edStholo 
7952286d8edStholo /* Yield the newly malloc'd pathname
7962286d8edStholo    of the file in DIR whose filename is FILE.  */
7972286d8edStholo 
7982286d8edStholo char *
7992286d8edStholo dir_file_pathname (dir, file)
8002286d8edStholo      char const *dir, *file;
8012286d8edStholo {
8022286d8edStholo   char const *p = filename_lastdirchar (dir);
8032286d8edStholo   return concat (dir, "/" + (p && !p[1]), file);
8042286d8edStholo }
8052286d8edStholo 
8062286d8edStholo void
8072286d8edStholo debug_script (sp)
8082286d8edStholo      struct change *sp;
8092286d8edStholo {
8102286d8edStholo   fflush (stdout);
8112286d8edStholo   for (; sp; sp = sp->link)
8122286d8edStholo     fprintf (stderr, "%3d %3d delete %d insert %d\n",
8132286d8edStholo 	     sp->line0, sp->line1, sp->deleted, sp->inserted);
8142286d8edStholo   fflush (stderr);
8152286d8edStholo }
816