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