xref: /netbsd-src/external/gpl2/diffutils/dist/src/util.c (revision 372807758056e79bf39280a322290dcebdfd4c30)
1*37280775Sroy /*	$NetBSD: util.c,v 1.2 2020/12/13 00:04:40 roy Exp $	*/
275f6d617Schristos 
375f6d617Schristos /* Support routines for GNU DIFF.
475f6d617Schristos 
575f6d617Schristos    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002
675f6d617Schristos    Free Software Foundation, Inc.
775f6d617Schristos 
875f6d617Schristos    This file is part of GNU DIFF.
975f6d617Schristos 
1075f6d617Schristos    GNU DIFF is free software; you can redistribute it and/or modify
1175f6d617Schristos    it under the terms of the GNU General Public License as published by
1275f6d617Schristos    the Free Software Foundation; either version 2, or (at your option)
1375f6d617Schristos    any later version.
1475f6d617Schristos 
1575f6d617Schristos    GNU DIFF is distributed in the hope that it will be useful,
1675f6d617Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
1775f6d617Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1875f6d617Schristos    GNU General Public License for more details.
1975f6d617Schristos 
2075f6d617Schristos    You should have received a copy of the GNU General Public License
2175f6d617Schristos    along with this program; see the file COPYING.
2275f6d617Schristos    If not, write to the Free Software Foundation,
2375f6d617Schristos    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2475f6d617Schristos 
2575f6d617Schristos #include "diff.h"
2675f6d617Schristos #include <dirname.h>
2775f6d617Schristos #include <error.h>
2875f6d617Schristos #include <quotesys.h>
2975f6d617Schristos #include <regex.h>
3075f6d617Schristos #include <xalloc.h>
3175f6d617Schristos 
3275f6d617Schristos char const pr_program[] = PR_PROGRAM;
3375f6d617Schristos 
3475f6d617Schristos /* Queue up one-line messages to be printed at the end,
3575f6d617Schristos    when -l is specified.  Each message is recorded with a `struct msg'.  */
3675f6d617Schristos 
3775f6d617Schristos struct msg
3875f6d617Schristos {
3975f6d617Schristos   struct msg *next;
4075f6d617Schristos   char args[1]; /* Format + 4 args, each '\0' terminated, concatenated.  */
4175f6d617Schristos };
4275f6d617Schristos 
4375f6d617Schristos /* Head of the chain of queues messages.  */
4475f6d617Schristos 
4575f6d617Schristos static struct msg *msg_chain;
4675f6d617Schristos 
4775f6d617Schristos /* Tail of the chain of queues messages.  */
4875f6d617Schristos 
4975f6d617Schristos static struct msg **msg_chain_end = &msg_chain;
5075f6d617Schristos 
5175f6d617Schristos /* Use when a system call returns non-zero status.
5275f6d617Schristos    NAME should normally be the file name.  */
5375f6d617Schristos 
5475f6d617Schristos void
perror_with_name(char const * name)5575f6d617Schristos perror_with_name (char const *name)
5675f6d617Schristos {
5775f6d617Schristos   error (0, errno, "%s", name);
5875f6d617Schristos }
5975f6d617Schristos 
6075f6d617Schristos /* Use when a system call returns non-zero status and that is fatal.  */
6175f6d617Schristos 
6275f6d617Schristos void
pfatal_with_name(char const * name)6375f6d617Schristos pfatal_with_name (char const *name)
6475f6d617Schristos {
6575f6d617Schristos   int e = errno;
6675f6d617Schristos   print_message_queue ();
6775f6d617Schristos   error (EXIT_TROUBLE, e, "%s", name);
6875f6d617Schristos   abort ();
6975f6d617Schristos }
7075f6d617Schristos 
7175f6d617Schristos /* Print an error message containing MSGID, then exit.  */
7275f6d617Schristos 
7375f6d617Schristos void
fatal(char const * msgid)7475f6d617Schristos fatal (char const *msgid)
7575f6d617Schristos {
7675f6d617Schristos   print_message_queue ();
7775f6d617Schristos   error (EXIT_TROUBLE, 0, "%s", _(msgid));
7875f6d617Schristos   abort ();
7975f6d617Schristos }
8075f6d617Schristos 
8175f6d617Schristos /* Like printf, except if -l in effect then save the message and print later.
8275f6d617Schristos    This is used for things like "Only in ...".  */
8375f6d617Schristos 
8475f6d617Schristos void
message(char const * format_msgid,char const * arg1,char const * arg2)8575f6d617Schristos message (char const *format_msgid, char const *arg1, char const *arg2)
8675f6d617Schristos {
8775f6d617Schristos   message5 (format_msgid, arg1, arg2, 0, 0);
8875f6d617Schristos }
8975f6d617Schristos 
9075f6d617Schristos void
message5(char const * format_msgid,char const * arg1,char const * arg2,char const * arg3,char const * arg4)9175f6d617Schristos message5 (char const *format_msgid, char const *arg1, char const *arg2,
9275f6d617Schristos 	  char const *arg3, char const *arg4)
9375f6d617Schristos {
9475f6d617Schristos   if (paginate)
9575f6d617Schristos     {
9675f6d617Schristos       char *p;
9775f6d617Schristos       char const *arg[5];
9875f6d617Schristos       int i;
9975f6d617Schristos       size_t size[5];
10075f6d617Schristos       size_t total_size = offsetof (struct msg, args);
10175f6d617Schristos       struct msg *new;
10275f6d617Schristos 
10375f6d617Schristos       arg[0] = format_msgid;
10475f6d617Schristos       arg[1] = arg1;
10575f6d617Schristos       arg[2] = arg2;
10675f6d617Schristos       arg[3] = arg3 ? arg3 : "";
10775f6d617Schristos       arg[4] = arg4 ? arg4 : "";
10875f6d617Schristos 
10975f6d617Schristos       for (i = 0;  i < 5;  i++)
11075f6d617Schristos 	total_size += size[i] = strlen (arg[i]) + 1;
11175f6d617Schristos 
11275f6d617Schristos       new = xmalloc (total_size);
11375f6d617Schristos 
11475f6d617Schristos       for (i = 0, p = new->args;  i < 5;  p += size[i++])
11575f6d617Schristos 	memcpy (p, arg[i], size[i]);
11675f6d617Schristos 
11775f6d617Schristos       *msg_chain_end = new;
11875f6d617Schristos       new->next = 0;
11975f6d617Schristos       msg_chain_end = &new->next;
12075f6d617Schristos     }
12175f6d617Schristos   else
12275f6d617Schristos     {
12375f6d617Schristos       if (sdiff_merge_assist)
12475f6d617Schristos 	putchar (' ');
12575f6d617Schristos       printf (_(format_msgid), arg1, arg2, arg3, arg4);
12675f6d617Schristos     }
12775f6d617Schristos }
12875f6d617Schristos 
12975f6d617Schristos /* Output all the messages that were saved up by calls to `message'.  */
13075f6d617Schristos 
13175f6d617Schristos void
print_message_queue(void)13275f6d617Schristos print_message_queue (void)
13375f6d617Schristos {
13475f6d617Schristos   char const *arg[5];
13575f6d617Schristos   int i;
13675f6d617Schristos   struct msg *m = msg_chain;
13775f6d617Schristos 
13875f6d617Schristos   while (m)
13975f6d617Schristos     {
14075f6d617Schristos       struct msg *next = m->next;
14175f6d617Schristos       arg[0] = m->args;
14275f6d617Schristos       for (i = 0;  i < 4;  i++)
14375f6d617Schristos 	arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
14475f6d617Schristos       printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
14575f6d617Schristos       free (m);
14675f6d617Schristos       m = next;
14775f6d617Schristos     }
14875f6d617Schristos }
14975f6d617Schristos 
15075f6d617Schristos /* Call before outputting the results of comparing files NAME0 and NAME1
15175f6d617Schristos    to set up OUTFILE, the stdio stream for the output to go to.
15275f6d617Schristos 
15375f6d617Schristos    Usually, OUTFILE is just stdout.  But when -l was specified
15475f6d617Schristos    we fork off a `pr' and make OUTFILE a pipe to it.
15575f6d617Schristos    `pr' then outputs to our stdout.  */
15675f6d617Schristos 
15775f6d617Schristos static char const *current_name0;
15875f6d617Schristos static char const *current_name1;
15975f6d617Schristos static bool currently_recursive;
16075f6d617Schristos 
16175f6d617Schristos void
setup_output(char const * name0,char const * name1,bool recursive)16275f6d617Schristos setup_output (char const *name0, char const *name1, bool recursive)
16375f6d617Schristos {
16475f6d617Schristos   current_name0 = name0;
16575f6d617Schristos   current_name1 = name1;
16675f6d617Schristos   currently_recursive = recursive;
16775f6d617Schristos   outfile = 0;
16875f6d617Schristos }
16975f6d617Schristos 
17075f6d617Schristos #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
17175f6d617Schristos static pid_t pr_pid;
17275f6d617Schristos #endif
17375f6d617Schristos 
17475f6d617Schristos void
begin_output(void)17575f6d617Schristos begin_output (void)
17675f6d617Schristos {
17775f6d617Schristos   char *name;
17875f6d617Schristos 
17975f6d617Schristos   if (outfile != 0)
18075f6d617Schristos     return;
18175f6d617Schristos 
18275f6d617Schristos   /* Construct the header of this piece of diff.  */
18375f6d617Schristos   name = xmalloc (strlen (current_name0) + strlen (current_name1)
18475f6d617Schristos 		  + strlen (switch_string) + 7);
18575f6d617Schristos 
18675f6d617Schristos   /* POSIX 1003.1-2001 specifies this format.  But there are some bugs in
18775f6d617Schristos      the standard: it says that we must print only the last component
18875f6d617Schristos      of the pathnames, and it requires two spaces after "diff" if
18975f6d617Schristos      there are no options.  These requirements are silly and do not
19075f6d617Schristos      match historical practice.  */
19175f6d617Schristos   sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
19275f6d617Schristos 
19375f6d617Schristos   if (paginate)
19475f6d617Schristos     {
19575f6d617Schristos       if (fflush (stdout) != 0)
19675f6d617Schristos 	pfatal_with_name (_("write failed"));
19775f6d617Schristos 
19875f6d617Schristos       /* Make OUTFILE a pipe to a subsidiary `pr'.  */
19975f6d617Schristos       {
20075f6d617Schristos #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
20175f6d617Schristos 	int pipes[2];
20275f6d617Schristos 
20375f6d617Schristos 	if (pipe (pipes) != 0)
20475f6d617Schristos 	  pfatal_with_name ("pipe");
20575f6d617Schristos 
20675f6d617Schristos 	pr_pid = vfork ();
20775f6d617Schristos 	if (pr_pid < 0)
20875f6d617Schristos 	  pfatal_with_name ("fork");
20975f6d617Schristos 
21075f6d617Schristos 	if (pr_pid == 0)
21175f6d617Schristos 	  {
21275f6d617Schristos 	    close (pipes[1]);
21375f6d617Schristos 	    if (pipes[0] != STDIN_FILENO)
21475f6d617Schristos 	      {
21575f6d617Schristos 		if (dup2 (pipes[0], STDIN_FILENO) < 0)
21675f6d617Schristos 		  pfatal_with_name ("dup2");
21775f6d617Schristos 		close (pipes[0]);
21875f6d617Schristos 	      }
21975f6d617Schristos 
220*37280775Sroy 	    execl (pr_program, pr_program, "-h", name, NULL);
22175f6d617Schristos 	    _exit (errno == ENOEXEC ? 126 : 127);
22275f6d617Schristos 	  }
22375f6d617Schristos 	else
22475f6d617Schristos 	  {
22575f6d617Schristos 	    close (pipes[0]);
22675f6d617Schristos 	    outfile = fdopen (pipes[1], "w");
22775f6d617Schristos 	    if (!outfile)
22875f6d617Schristos 	      pfatal_with_name ("fdopen");
22975f6d617Schristos 	  }
23075f6d617Schristos #else
23175f6d617Schristos 	char *command = xmalloc (sizeof pr_program - 1 + 7
23275f6d617Schristos 				 + quote_system_arg ((char *) 0, name) + 1);
23375f6d617Schristos 	char *p;
23475f6d617Schristos 	sprintf (command, "%s -f -h ", pr_program);
23575f6d617Schristos 	p = command + sizeof pr_program - 1 + 7;
23675f6d617Schristos 	p += quote_system_arg (p, name);
23775f6d617Schristos 	*p = 0;
23875f6d617Schristos 	errno = 0;
23975f6d617Schristos 	outfile = popen (command, "w");
24075f6d617Schristos 	if (!outfile)
24175f6d617Schristos 	  pfatal_with_name (command);
24275f6d617Schristos 	free (command);
24375f6d617Schristos #endif
24475f6d617Schristos       }
24575f6d617Schristos     }
24675f6d617Schristos   else
24775f6d617Schristos     {
24875f6d617Schristos 
24975f6d617Schristos       /* If -l was not specified, output the diff straight to `stdout'.  */
25075f6d617Schristos 
25175f6d617Schristos       outfile = stdout;
25275f6d617Schristos 
25375f6d617Schristos       /* If handling multiple files (because scanning a directory),
25475f6d617Schristos 	 print which files the following output is about.  */
25575f6d617Schristos       if (currently_recursive)
25675f6d617Schristos 	printf ("%s\n", name);
25775f6d617Schristos     }
25875f6d617Schristos 
25975f6d617Schristos   free (name);
26075f6d617Schristos 
26175f6d617Schristos   /* A special header is needed at the beginning of context output.  */
26275f6d617Schristos   switch (output_style)
26375f6d617Schristos     {
26475f6d617Schristos     case OUTPUT_CONTEXT:
26575f6d617Schristos       print_context_header (files, 0);
26675f6d617Schristos       break;
26775f6d617Schristos 
26875f6d617Schristos     case OUTPUT_UNIFIED:
26975f6d617Schristos       print_context_header (files, 1);
27075f6d617Schristos       break;
27175f6d617Schristos 
27275f6d617Schristos     default:
27375f6d617Schristos       break;
27475f6d617Schristos     }
27575f6d617Schristos }
27675f6d617Schristos 
27775f6d617Schristos /* Call after the end of output of diffs for one file.
27875f6d617Schristos    Close OUTFILE and get rid of the `pr' subfork.  */
27975f6d617Schristos 
28075f6d617Schristos void
finish_output(void)28175f6d617Schristos finish_output (void)
28275f6d617Schristos {
28375f6d617Schristos   if (outfile != 0 && outfile != stdout)
28475f6d617Schristos     {
28575f6d617Schristos       int wstatus;
28675f6d617Schristos       int werrno = 0;
28775f6d617Schristos       if (ferror (outfile))
28875f6d617Schristos 	fatal ("write failed");
28975f6d617Schristos #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
29075f6d617Schristos       wstatus = pclose (outfile);
29175f6d617Schristos       if (wstatus == -1)
29275f6d617Schristos 	werrno = errno;
29375f6d617Schristos #else
29475f6d617Schristos       if (fclose (outfile) != 0)
29575f6d617Schristos 	pfatal_with_name (_("write failed"));
29675f6d617Schristos       if (waitpid (pr_pid, &wstatus, 0) < 0)
29775f6d617Schristos 	pfatal_with_name ("waitpid");
29875f6d617Schristos #endif
29975f6d617Schristos       if (! werrno && WIFEXITED (wstatus) && WEXITSTATUS (wstatus) == 127)
30075f6d617Schristos 	error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not found"),
30175f6d617Schristos 	       pr_program);
30275f6d617Schristos       if (wstatus != 0)
30375f6d617Schristos 	error (EXIT_TROUBLE, werrno, _("subsidiary program `%s' failed"),
30475f6d617Schristos 	       pr_program);
30575f6d617Schristos     }
30675f6d617Schristos 
30775f6d617Schristos   outfile = 0;
30875f6d617Schristos }
30975f6d617Schristos 
31075f6d617Schristos /* Compare two lines (typically one from each input file)
31175f6d617Schristos    according to the command line options.
31275f6d617Schristos    For efficiency, this is invoked only when the lines do not match exactly
31375f6d617Schristos    but an option like -i might cause us to ignore the difference.
31475f6d617Schristos    Return nonzero if the lines differ.  */
31575f6d617Schristos 
31675f6d617Schristos bool
lines_differ(char const * s1,char const * s2)31775f6d617Schristos lines_differ (char const *s1, char const *s2)
31875f6d617Schristos {
31975f6d617Schristos   register unsigned char const *t1 = (unsigned char const *) s1;
32075f6d617Schristos   register unsigned char const *t2 = (unsigned char const *) s2;
32175f6d617Schristos   size_t column = 0;
32275f6d617Schristos 
32375f6d617Schristos   while (1)
32475f6d617Schristos     {
32575f6d617Schristos       register unsigned char c1 = *t1++;
32675f6d617Schristos       register unsigned char c2 = *t2++;
32775f6d617Schristos 
32875f6d617Schristos       /* Test for exact char equality first, since it's a common case.  */
32975f6d617Schristos       if (c1 != c2)
33075f6d617Schristos 	{
33175f6d617Schristos 	  switch (ignore_white_space)
33275f6d617Schristos 	    {
33375f6d617Schristos 	    case IGNORE_ALL_SPACE:
33475f6d617Schristos 	      /* For -w, just skip past any white space.  */
33575f6d617Schristos 	      while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
33675f6d617Schristos 	      while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
33775f6d617Schristos 	      break;
33875f6d617Schristos 
33975f6d617Schristos 	    case IGNORE_SPACE_CHANGE:
34075f6d617Schristos 	      /* For -b, advance past any sequence of white space in
34175f6d617Schristos 		 line 1 and consider it just one space, or nothing at
34275f6d617Schristos 		 all if it is at the end of the line.  */
34375f6d617Schristos 	      if (ISSPACE (c1))
34475f6d617Schristos 		{
34575f6d617Schristos 		  while (c1 != '\n')
34675f6d617Schristos 		    {
34775f6d617Schristos 		      c1 = *t1++;
34875f6d617Schristos 		      if (! ISSPACE (c1))
34975f6d617Schristos 			{
35075f6d617Schristos 			  --t1;
35175f6d617Schristos 			  c1 = ' ';
35275f6d617Schristos 			  break;
35375f6d617Schristos 			}
35475f6d617Schristos 		    }
35575f6d617Schristos 		}
35675f6d617Schristos 
35775f6d617Schristos 	      /* Likewise for line 2.  */
35875f6d617Schristos 	      if (ISSPACE (c2))
35975f6d617Schristos 		{
36075f6d617Schristos 		  while (c2 != '\n')
36175f6d617Schristos 		    {
36275f6d617Schristos 		      c2 = *t2++;
36375f6d617Schristos 		      if (! ISSPACE (c2))
36475f6d617Schristos 			{
36575f6d617Schristos 			  --t2;
36675f6d617Schristos 			  c2 = ' ';
36775f6d617Schristos 			  break;
36875f6d617Schristos 			}
36975f6d617Schristos 		    }
37075f6d617Schristos 		}
37175f6d617Schristos 
37275f6d617Schristos 	      if (c1 != c2)
37375f6d617Schristos 		{
37475f6d617Schristos 		  /* If we went too far when doing the simple test
37575f6d617Schristos 		     for equality, go back to the first non-white-space
37675f6d617Schristos 		     character in both sides and try again.  */
37775f6d617Schristos 		  if (c2 == ' ' && c1 != '\n'
37875f6d617Schristos 		      && (unsigned char const *) s1 + 1 < t1
37975f6d617Schristos 		      && ISSPACE (t1[-2]))
38075f6d617Schristos 		    {
38175f6d617Schristos 		      --t1;
38275f6d617Schristos 		      continue;
38375f6d617Schristos 		    }
38475f6d617Schristos 		  if (c1 == ' ' && c2 != '\n'
38575f6d617Schristos 		      && (unsigned char const *) s2 + 1 < t2
38675f6d617Schristos 		      && ISSPACE (t2[-2]))
38775f6d617Schristos 		    {
38875f6d617Schristos 		      --t2;
38975f6d617Schristos 		      continue;
39075f6d617Schristos 		    }
39175f6d617Schristos 		}
39275f6d617Schristos 
39375f6d617Schristos 	      break;
39475f6d617Schristos 
39575f6d617Schristos 	    case IGNORE_TAB_EXPANSION:
39675f6d617Schristos 	      if ((c1 == ' ' && c2 == '\t')
39775f6d617Schristos 		  || (c1 == '\t' && c2 == ' '))
39875f6d617Schristos 		{
39975f6d617Schristos 		  size_t column2 = column;
40075f6d617Schristos 		  for (;; c1 = *t1++)
40175f6d617Schristos 		    {
40275f6d617Schristos 		      if (c1 == ' ')
40375f6d617Schristos 			column++;
40475f6d617Schristos 		      else if (c1 == '\t')
40575f6d617Schristos 			column += TAB_WIDTH - column % TAB_WIDTH;
40675f6d617Schristos 		      else
40775f6d617Schristos 			break;
40875f6d617Schristos 		    }
40975f6d617Schristos 		  for (;; c2 = *t2++)
41075f6d617Schristos 		    {
41175f6d617Schristos 		      if (c2 == ' ')
41275f6d617Schristos 			column2++;
41375f6d617Schristos 		      else if (c2 == '\t')
41475f6d617Schristos 			column2 += TAB_WIDTH - column2 % TAB_WIDTH;
41575f6d617Schristos 		      else
41675f6d617Schristos 			break;
41775f6d617Schristos 		    }
41875f6d617Schristos 		  if (column != column2)
41975f6d617Schristos 		    return 1;
42075f6d617Schristos 		}
42175f6d617Schristos 	      break;
42275f6d617Schristos 
42375f6d617Schristos 	    case IGNORE_NO_WHITE_SPACE:
42475f6d617Schristos 	      break;
42575f6d617Schristos 	    }
42675f6d617Schristos 
42775f6d617Schristos 	  /* Lowercase all letters if -i is specified.  */
42875f6d617Schristos 
42975f6d617Schristos 	  if (ignore_case)
43075f6d617Schristos 	    {
43175f6d617Schristos 	      c1 = TOLOWER (c1);
43275f6d617Schristos 	      c2 = TOLOWER (c2);
43375f6d617Schristos 	    }
43475f6d617Schristos 
43575f6d617Schristos 	  if (c1 != c2)
43675f6d617Schristos 	    break;
43775f6d617Schristos 	}
43875f6d617Schristos       if (c1 == '\n')
43975f6d617Schristos 	return 0;
44075f6d617Schristos 
44175f6d617Schristos       column += c1 == '\t' ? TAB_WIDTH - column % TAB_WIDTH : 1;
44275f6d617Schristos     }
44375f6d617Schristos 
44475f6d617Schristos   return 1;
44575f6d617Schristos }
44675f6d617Schristos 
44775f6d617Schristos /* Find the consecutive changes at the start of the script START.
44875f6d617Schristos    Return the last link before the first gap.  */
44975f6d617Schristos 
45075f6d617Schristos struct change *
find_change(struct change * start)45175f6d617Schristos find_change (struct change *start)
45275f6d617Schristos {
45375f6d617Schristos   return start;
45475f6d617Schristos }
45575f6d617Schristos 
45675f6d617Schristos struct change *
find_reverse_change(struct change * start)45775f6d617Schristos find_reverse_change (struct change *start)
45875f6d617Schristos {
45975f6d617Schristos   return start;
46075f6d617Schristos }
46175f6d617Schristos 
46275f6d617Schristos /* Divide SCRIPT into pieces by calling HUNKFUN and
46375f6d617Schristos    print each piece with PRINTFUN.
46475f6d617Schristos    Both functions take one arg, an edit script.
46575f6d617Schristos 
46675f6d617Schristos    HUNKFUN is called with the tail of the script
46775f6d617Schristos    and returns the last link that belongs together with the start
46875f6d617Schristos    of the tail.
46975f6d617Schristos 
47075f6d617Schristos    PRINTFUN takes a subscript which belongs together (with a null
47175f6d617Schristos    link at the end) and prints it.  */
47275f6d617Schristos 
47375f6d617Schristos void
print_script(struct change * script,struct change * (* hunkfun)(struct change *),void (* printfun)(struct change *))47475f6d617Schristos print_script (struct change *script,
47575f6d617Schristos 	      struct change * (*hunkfun) (struct change *),
47675f6d617Schristos 	      void (*printfun) (struct change *))
47775f6d617Schristos {
47875f6d617Schristos   struct change *next = script;
47975f6d617Schristos 
48075f6d617Schristos   while (next)
48175f6d617Schristos     {
48275f6d617Schristos       struct change *this, *end;
48375f6d617Schristos 
48475f6d617Schristos       /* Find a set of changes that belong together.  */
48575f6d617Schristos       this = next;
48675f6d617Schristos       end = (*hunkfun) (next);
48775f6d617Schristos 
48875f6d617Schristos       /* Disconnect them from the rest of the changes,
48975f6d617Schristos 	 making them a hunk, and remember the rest for next iteration.  */
49075f6d617Schristos       next = end->link;
49175f6d617Schristos       end->link = 0;
49275f6d617Schristos #ifdef DEBUG
49375f6d617Schristos       debug_script (this);
49475f6d617Schristos #endif
49575f6d617Schristos 
49675f6d617Schristos       /* Print this hunk.  */
49775f6d617Schristos       (*printfun) (this);
49875f6d617Schristos 
49975f6d617Schristos       /* Reconnect the script so it will all be freed properly.  */
50075f6d617Schristos       end->link = next;
50175f6d617Schristos     }
50275f6d617Schristos }
50375f6d617Schristos 
50475f6d617Schristos /* Print the text of a single line LINE,
50575f6d617Schristos    flagging it with the characters in LINE_FLAG (which say whether
50675f6d617Schristos    the line is inserted, deleted, changed, etc.).  */
50775f6d617Schristos 
50875f6d617Schristos void
print_1_line(char const * line_flag,char const * const * line)50975f6d617Schristos print_1_line (char const *line_flag, char const *const *line)
51075f6d617Schristos {
51175f6d617Schristos   char const *base = line[0], *limit = line[1]; /* Help the compiler.  */
51275f6d617Schristos   FILE *out = outfile; /* Help the compiler some more.  */
51375f6d617Schristos   char const *flag_format = 0;
51475f6d617Schristos 
51575f6d617Schristos   /* If -T was specified, use a Tab between the line-flag and the text.
51675f6d617Schristos      Otherwise use a Space (as Unix diff does).
51775f6d617Schristos      Print neither space nor tab if line-flags are empty.  */
51875f6d617Schristos 
51975f6d617Schristos   if (line_flag && *line_flag)
52075f6d617Schristos     {
52175f6d617Schristos       flag_format = initial_tab ? "%s\t" : "%s ";
52275f6d617Schristos       fprintf (out, flag_format, line_flag);
52375f6d617Schristos     }
52475f6d617Schristos 
52575f6d617Schristos   output_1_line (base, limit, flag_format, line_flag);
52675f6d617Schristos 
52775f6d617Schristos   if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
52875f6d617Schristos     fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
52975f6d617Schristos }
53075f6d617Schristos 
53175f6d617Schristos /* Output a line from BASE up to LIMIT.
53275f6d617Schristos    With -t, expand white space characters to spaces, and if FLAG_FORMAT
53375f6d617Schristos    is nonzero, output it with argument LINE_FLAG after every
53475f6d617Schristos    internal carriage return, so that tab stops continue to line up.  */
53575f6d617Schristos 
53675f6d617Schristos void
output_1_line(char const * base,char const * limit,char const * flag_format,char const * line_flag)53775f6d617Schristos output_1_line (char const *base, char const *limit, char const *flag_format,
53875f6d617Schristos 	       char const *line_flag)
53975f6d617Schristos {
54075f6d617Schristos   if (!expand_tabs)
54175f6d617Schristos     fwrite (base, limit - base, 1, outfile);
54275f6d617Schristos   else
54375f6d617Schristos     {
54475f6d617Schristos       register FILE *out = outfile;
54575f6d617Schristos       register unsigned char c;
54675f6d617Schristos       register char const *t = base;
54775f6d617Schristos       register unsigned int column = 0;
54875f6d617Schristos 
54975f6d617Schristos       while (t < limit)
55075f6d617Schristos 	switch ((c = *t++))
55175f6d617Schristos 	  {
55275f6d617Schristos 	  case '\t':
55375f6d617Schristos 	    {
55475f6d617Schristos 	      unsigned int spaces = TAB_WIDTH - column % TAB_WIDTH;
55575f6d617Schristos 	      column += spaces;
55675f6d617Schristos 	      do
55775f6d617Schristos 		putc (' ', out);
55875f6d617Schristos 	      while (--spaces);
55975f6d617Schristos 	    }
56075f6d617Schristos 	    break;
56175f6d617Schristos 
56275f6d617Schristos 	  case '\r':
56375f6d617Schristos 	    putc (c, out);
56475f6d617Schristos 	    if (flag_format && t < limit && *t != '\n')
56575f6d617Schristos 	      fprintf (out, flag_format, line_flag);
56675f6d617Schristos 	    column = 0;
56775f6d617Schristos 	    break;
56875f6d617Schristos 
56975f6d617Schristos 	  case '\b':
57075f6d617Schristos 	    if (column == 0)
57175f6d617Schristos 	      continue;
57275f6d617Schristos 	    column--;
57375f6d617Schristos 	    putc (c, out);
57475f6d617Schristos 	    break;
57575f6d617Schristos 
57675f6d617Schristos 	  default:
57775f6d617Schristos 	    if (ISPRINT (c))
57875f6d617Schristos 	      column++;
57975f6d617Schristos 	    putc (c, out);
58075f6d617Schristos 	    break;
58175f6d617Schristos 	  }
58275f6d617Schristos     }
58375f6d617Schristos }
58475f6d617Schristos 
58575f6d617Schristos char const change_letter[] = { 0, 'd', 'a', 'c' };
58675f6d617Schristos 
58775f6d617Schristos /* Translate an internal line number (an index into diff's table of lines)
58875f6d617Schristos    into an actual line number in the input file.
58975f6d617Schristos    The internal line number is I.  FILE points to the data on the file.
59075f6d617Schristos 
59175f6d617Schristos    Internal line numbers count from 0 starting after the prefix.
59275f6d617Schristos    Actual line numbers count from 1 within the entire file.  */
59375f6d617Schristos 
59475f6d617Schristos lin
translate_line_number(struct file_data const * file,lin i)59575f6d617Schristos translate_line_number (struct file_data const *file, lin i)
59675f6d617Schristos {
59775f6d617Schristos   return i + file->prefix_lines + 1;
59875f6d617Schristos }
59975f6d617Schristos 
60075f6d617Schristos /* Translate a line number range.  This is always done for printing,
60175f6d617Schristos    so for convenience translate to long rather than lin, so that the
60275f6d617Schristos    caller can use printf with "%ld" without casting.  */
60375f6d617Schristos 
60475f6d617Schristos void
translate_range(struct file_data const * file,lin a,lin b,long * aptr,long * bptr)60575f6d617Schristos translate_range (struct file_data const *file,
60675f6d617Schristos 		 lin a, lin b,
60775f6d617Schristos 		 long *aptr, long *bptr)
60875f6d617Schristos {
60975f6d617Schristos   *aptr = translate_line_number (file, a - 1) + 1;
61075f6d617Schristos   *bptr = translate_line_number (file, b + 1) - 1;
61175f6d617Schristos }
61275f6d617Schristos 
61375f6d617Schristos /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
61475f6d617Schristos    If the two numbers are identical, print just one number.
61575f6d617Schristos 
61675f6d617Schristos    Args A and B are internal line numbers.
61775f6d617Schristos    We print the translated (real) line numbers.  */
61875f6d617Schristos 
61975f6d617Schristos void
print_number_range(char sepchar,struct file_data * file,lin a,lin b)62075f6d617Schristos print_number_range (char sepchar, struct file_data *file, lin a, lin b)
62175f6d617Schristos {
62275f6d617Schristos   long trans_a, trans_b;
62375f6d617Schristos   translate_range (file, a, b, &trans_a, &trans_b);
62475f6d617Schristos 
62575f6d617Schristos   /* Note: we can have B < A in the case of a range of no lines.
62675f6d617Schristos      In this case, we should print the line number before the range,
62775f6d617Schristos      which is B.  */
62875f6d617Schristos   if (trans_b > trans_a)
62975f6d617Schristos     fprintf (outfile, "%ld%c%ld", trans_a, sepchar, trans_b);
63075f6d617Schristos   else
63175f6d617Schristos     fprintf (outfile, "%ld", trans_b);
63275f6d617Schristos }
63375f6d617Schristos 
63475f6d617Schristos /* Look at a hunk of edit script and report the range of lines in each file
63575f6d617Schristos    that it applies to.  HUNK is the start of the hunk, which is a chain
63675f6d617Schristos    of `struct change'.  The first and last line numbers of file 0 are stored in
63775f6d617Schristos    *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
63875f6d617Schristos    Note that these are internal line numbers that count from 0.
63975f6d617Schristos 
64075f6d617Schristos    If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
64175f6d617Schristos 
64275f6d617Schristos    Return UNCHANGED if only ignorable lines are inserted or deleted,
64375f6d617Schristos    OLD if lines of file 0 are deleted,
64475f6d617Schristos    NEW if lines of file 1 are inserted,
64575f6d617Schristos    and CHANGED if both kinds of changes are found. */
64675f6d617Schristos 
64775f6d617Schristos enum changes
analyze_hunk(struct change * hunk,lin * first0,lin * last0,lin * first1,lin * last1)64875f6d617Schristos analyze_hunk (struct change *hunk,
64975f6d617Schristos 	      lin *first0, lin *last0,
65075f6d617Schristos 	      lin *first1, lin *last1)
65175f6d617Schristos {
65275f6d617Schristos   struct change *next;
65375f6d617Schristos   lin l0, l1;
65475f6d617Schristos   lin show_from, show_to;
65575f6d617Schristos   lin i;
65675f6d617Schristos   bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
65775f6d617Schristos   size_t trivial_length = (int) ignore_blank_lines - 1;
65875f6d617Schristos     /* If 0, ignore zero-length lines;
65975f6d617Schristos        if SIZE_MAX, do not ignore lines just because of their length.  */
66075f6d617Schristos 
66175f6d617Schristos   char const * const *linbuf0 = files[0].linbuf;  /* Help the compiler.  */
66275f6d617Schristos   char const * const *linbuf1 = files[1].linbuf;
66375f6d617Schristos 
66475f6d617Schristos   show_from = show_to = 0;
66575f6d617Schristos 
66675f6d617Schristos   *first0 = hunk->line0;
66775f6d617Schristos   *first1 = hunk->line1;
66875f6d617Schristos 
66975f6d617Schristos   next = hunk;
67075f6d617Schristos   do
67175f6d617Schristos     {
67275f6d617Schristos       l0 = next->line0 + next->deleted - 1;
67375f6d617Schristos       l1 = next->line1 + next->inserted - 1;
67475f6d617Schristos       show_from += next->deleted;
67575f6d617Schristos       show_to += next->inserted;
67675f6d617Schristos 
67775f6d617Schristos       for (i = next->line0; i <= l0 && trivial; i++)
67875f6d617Schristos 	{
67975f6d617Schristos 	  char const *line = linbuf0[i];
68075f6d617Schristos 	  size_t len = linbuf0[i + 1] - line - 1;
68175f6d617Schristos 	  if (len != trivial_length
68275f6d617Schristos 	      && (! ignore_regexp.fastmap
68375f6d617Schristos 		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
68475f6d617Schristos 	    trivial = 0;
68575f6d617Schristos 	}
68675f6d617Schristos 
68775f6d617Schristos       for (i = next->line1; i <= l1 && trivial; i++)
68875f6d617Schristos 	{
68975f6d617Schristos 	  char const *line = linbuf1[i];
69075f6d617Schristos 	  size_t len = linbuf1[i + 1] - line - 1;
69175f6d617Schristos 	  if (len != trivial_length
69275f6d617Schristos 	      && (! ignore_regexp.fastmap
69375f6d617Schristos 		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
69475f6d617Schristos 	    trivial = 0;
69575f6d617Schristos 	}
69675f6d617Schristos     }
69775f6d617Schristos   while ((next = next->link) != 0);
69875f6d617Schristos 
69975f6d617Schristos   *last0 = l0;
70075f6d617Schristos   *last1 = l1;
70175f6d617Schristos 
70275f6d617Schristos   /* If all inserted or deleted lines are ignorable,
70375f6d617Schristos      tell the caller to ignore this hunk.  */
70475f6d617Schristos 
70575f6d617Schristos   if (trivial)
70675f6d617Schristos     return UNCHANGED;
70775f6d617Schristos 
70875f6d617Schristos   return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
70975f6d617Schristos }
71075f6d617Schristos 
71175f6d617Schristos /* Concatenate three strings, returning a newly malloc'd string.  */
71275f6d617Schristos 
71375f6d617Schristos char *
concat(char const * s1,char const * s2,char const * s3)71475f6d617Schristos concat (char const *s1, char const *s2, char const *s3)
71575f6d617Schristos {
71675f6d617Schristos   char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
71775f6d617Schristos   sprintf (new, "%s%s%s", s1, s2, s3);
71875f6d617Schristos   return new;
71975f6d617Schristos }
72075f6d617Schristos 
72175f6d617Schristos /* Yield a new block of SIZE bytes, initialized to zero.  */
72275f6d617Schristos 
72375f6d617Schristos void *
zalloc(size_t size)72475f6d617Schristos zalloc (size_t size)
72575f6d617Schristos {
72675f6d617Schristos   void *p = xmalloc (size);
72775f6d617Schristos   memset (p, 0, size);
72875f6d617Schristos   return p;
72975f6d617Schristos }
73075f6d617Schristos 
73175f6d617Schristos /* Yield the newly malloc'd pathname
73275f6d617Schristos    of the file in DIR whose filename is FILE.  */
73375f6d617Schristos 
73475f6d617Schristos char *
dir_file_pathname(char const * dir,char const * file)73575f6d617Schristos dir_file_pathname (char const *dir, char const *file)
73675f6d617Schristos {
73775f6d617Schristos   char const *base = base_name (dir);
73875f6d617Schristos   bool omit_slash = !*base || base[strlen (base) - 1] == '/';
73975f6d617Schristos   return concat (dir, "/" + omit_slash, file);
74075f6d617Schristos }
74175f6d617Schristos 
74275f6d617Schristos void
debug_script(struct change * sp)74375f6d617Schristos debug_script (struct change *sp)
74475f6d617Schristos {
74575f6d617Schristos   fflush (stdout);
74675f6d617Schristos 
74775f6d617Schristos   for (; sp; sp = sp->link)
74875f6d617Schristos     {
74975f6d617Schristos       long line0 = sp->line0;
75075f6d617Schristos       long line1 = sp->line1;
75175f6d617Schristos       long deleted = sp->deleted;
75275f6d617Schristos       long inserted = sp->inserted;
75375f6d617Schristos       fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n",
75475f6d617Schristos 	       line0, line1, deleted, inserted);
75575f6d617Schristos     }
75675f6d617Schristos 
75775f6d617Schristos   fflush (stderr);
75875f6d617Schristos }
759