1170754Sdelphij/* Support routines for GNU DIFF.
2170754Sdelphij
3170754Sdelphij   Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002,
4170754Sdelphij   2004 Free Software Foundation, Inc.
5170754Sdelphij
6170754Sdelphij   This file is part of GNU DIFF.
7170754Sdelphij
8170754Sdelphij   GNU DIFF is free software; you can redistribute it and/or modify
9170754Sdelphij   it under the terms of the GNU General Public License as published by
10170754Sdelphij   the Free Software Foundation; either version 2, or (at your option)
11170754Sdelphij   any later version.
12170754Sdelphij
13170754Sdelphij   GNU DIFF is distributed in the hope that it will be useful,
14170754Sdelphij   but WITHOUT ANY WARRANTY; without even the implied warranty of
15170754Sdelphij   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16170754Sdelphij   GNU General Public License for more details.
17170754Sdelphij
18170754Sdelphij   You should have received a copy of the GNU General Public License
19170754Sdelphij   along with this program; see the file COPYING.
20170754Sdelphij   If not, write to the Free Software Foundation,
21170754Sdelphij   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
22170754Sdelphij
23170754Sdelphij#include "diff.h"
24170754Sdelphij#include <dirname.h>
25170754Sdelphij#include <error.h>
26170754Sdelphij#include <quotesys.h>
27170754Sdelphij#include <xalloc.h>
28170754Sdelphij
29170754Sdelphijchar const pr_program[] = PR_PROGRAM;
30170754Sdelphij
31170754Sdelphij/* Queue up one-line messages to be printed at the end,
32170754Sdelphij   when -l is specified.  Each message is recorded with a `struct msg'.  */
33170754Sdelphij
34170754Sdelphijstruct msg
35170754Sdelphij{
36170754Sdelphij  struct msg *next;
37170754Sdelphij  char args[1]; /* Format + 4 args, each '\0' terminated, concatenated.  */
38170754Sdelphij};
39170754Sdelphij
40170754Sdelphij/* Head of the chain of queues messages.  */
41170754Sdelphij
42170754Sdelphijstatic struct msg *msg_chain;
43170754Sdelphij
44170754Sdelphij/* Tail of the chain of queues messages.  */
45170754Sdelphij
46170754Sdelphijstatic struct msg **msg_chain_end = &msg_chain;
47170754Sdelphij
48170754Sdelphij/* Use when a system call returns non-zero status.
49170754Sdelphij   NAME should normally be the file name.  */
50170754Sdelphij
51170754Sdelphijvoid
52170754Sdelphijperror_with_name (char const *name)
53170754Sdelphij{
54170754Sdelphij  error (0, errno, "%s", name);
55170754Sdelphij}
56170754Sdelphij
57170754Sdelphij/* Use when a system call returns non-zero status and that is fatal.  */
58170754Sdelphij
59170754Sdelphijvoid
60170754Sdelphijpfatal_with_name (char const *name)
61170754Sdelphij{
62170754Sdelphij  int e = errno;
63170754Sdelphij  print_message_queue ();
64170754Sdelphij  error (EXIT_TROUBLE, e, "%s", name);
65170754Sdelphij  abort ();
66170754Sdelphij}
67170754Sdelphij
68170754Sdelphij/* Print an error message containing MSGID, then exit.  */
69170754Sdelphij
70170754Sdelphijvoid
71170754Sdelphijfatal (char const *msgid)
72170754Sdelphij{
73170754Sdelphij  print_message_queue ();
74170754Sdelphij  error (EXIT_TROUBLE, 0, "%s", _(msgid));
75170754Sdelphij  abort ();
76170754Sdelphij}
77170754Sdelphij
78170754Sdelphij/* Like printf, except if -l in effect then save the message and print later.
79170754Sdelphij   This is used for things like "Only in ...".  */
80170754Sdelphij
81170754Sdelphijvoid
82170754Sdelphijmessage (char const *format_msgid, char const *arg1, char const *arg2)
83170754Sdelphij{
84170754Sdelphij  message5 (format_msgid, arg1, arg2, 0, 0);
85170754Sdelphij}
86170754Sdelphij
87170754Sdelphijvoid
88170754Sdelphijmessage5 (char const *format_msgid, char const *arg1, char const *arg2,
89170754Sdelphij	  char const *arg3, char const *arg4)
90170754Sdelphij{
91170754Sdelphij  if (paginate)
92170754Sdelphij    {
93170754Sdelphij      char *p;
94170754Sdelphij      char const *arg[5];
95170754Sdelphij      int i;
96170754Sdelphij      size_t size[5];
97170754Sdelphij      size_t total_size = offsetof (struct msg, args);
98170754Sdelphij      struct msg *new;
99170754Sdelphij
100170754Sdelphij      arg[0] = format_msgid;
101170754Sdelphij      arg[1] = arg1;
102170754Sdelphij      arg[2] = arg2;
103170754Sdelphij      arg[3] = arg3 ? arg3 : "";
104170754Sdelphij      arg[4] = arg4 ? arg4 : "";
105170754Sdelphij
106170754Sdelphij      for (i = 0;  i < 5;  i++)
107170754Sdelphij	total_size += size[i] = strlen (arg[i]) + 1;
108170754Sdelphij
109170754Sdelphij      new = xmalloc (total_size);
110170754Sdelphij
111170754Sdelphij      for (i = 0, p = new->args;  i < 5;  p += size[i++])
112170754Sdelphij	memcpy (p, arg[i], size[i]);
113170754Sdelphij
114170754Sdelphij      *msg_chain_end = new;
115170754Sdelphij      new->next = 0;
116170754Sdelphij      msg_chain_end = &new->next;
117170754Sdelphij    }
118170754Sdelphij  else
119170754Sdelphij    {
120170754Sdelphij      if (sdiff_merge_assist)
121170754Sdelphij	putchar (' ');
122170754Sdelphij      printf (_(format_msgid), arg1, arg2, arg3, arg4);
123170754Sdelphij    }
124170754Sdelphij}
125170754Sdelphij
126170754Sdelphij/* Output all the messages that were saved up by calls to `message'.  */
127170754Sdelphij
128170754Sdelphijvoid
129170754Sdelphijprint_message_queue (void)
130170754Sdelphij{
131170754Sdelphij  char const *arg[5];
132170754Sdelphij  int i;
133170754Sdelphij  struct msg *m = msg_chain;
134170754Sdelphij
135170754Sdelphij  while (m)
136170754Sdelphij    {
137170754Sdelphij      struct msg *next = m->next;
138170754Sdelphij      arg[0] = m->args;
139170754Sdelphij      for (i = 0;  i < 4;  i++)
140170754Sdelphij	arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
141170754Sdelphij      printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
142170754Sdelphij      free (m);
143170754Sdelphij      m = next;
144170754Sdelphij    }
145170754Sdelphij}
146170754Sdelphij
147170754Sdelphij/* Call before outputting the results of comparing files NAME0 and NAME1
148170754Sdelphij   to set up OUTFILE, the stdio stream for the output to go to.
149170754Sdelphij
150170754Sdelphij   Usually, OUTFILE is just stdout.  But when -l was specified
151170754Sdelphij   we fork off a `pr' and make OUTFILE a pipe to it.
152170754Sdelphij   `pr' then outputs to our stdout.  */
153170754Sdelphij
154170754Sdelphijstatic char const *current_name0;
155170754Sdelphijstatic char const *current_name1;
156170754Sdelphijstatic bool currently_recursive;
157170754Sdelphij
158170754Sdelphijvoid
159170754Sdelphijsetup_output (char const *name0, char const *name1, bool recursive)
160170754Sdelphij{
161170754Sdelphij  current_name0 = name0;
162170754Sdelphij  current_name1 = name1;
163170754Sdelphij  currently_recursive = recursive;
164170754Sdelphij  outfile = 0;
165170754Sdelphij}
166170754Sdelphij
167170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
168170754Sdelphijstatic pid_t pr_pid;
169170754Sdelphij#endif
170170754Sdelphij
171170754Sdelphijvoid
172170754Sdelphijbegin_output (void)
173170754Sdelphij{
174170754Sdelphij  char *name;
175170754Sdelphij
176170754Sdelphij  if (outfile != 0)
177170754Sdelphij    return;
178170754Sdelphij
179170754Sdelphij  /* Construct the header of this piece of diff.  */
180170754Sdelphij  name = xmalloc (strlen (current_name0) + strlen (current_name1)
181170754Sdelphij		  + strlen (switch_string) + 7);
182170754Sdelphij
183170754Sdelphij  /* POSIX 1003.1-2001 specifies this format.  But there are some bugs in
184170754Sdelphij     the standard: it says that we must print only the last component
185170754Sdelphij     of the pathnames, and it requires two spaces after "diff" if
186170754Sdelphij     there are no options.  These requirements are silly and do not
187170754Sdelphij     match historical practice.  */
188170754Sdelphij  sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
189170754Sdelphij
190170754Sdelphij  if (paginate)
191170754Sdelphij    {
192170754Sdelphij      if (fflush (stdout) != 0)
193170754Sdelphij	pfatal_with_name (_("write failed"));
194170754Sdelphij
195170754Sdelphij      /* Make OUTFILE a pipe to a subsidiary `pr'.  */
196170754Sdelphij      {
197170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
198170754Sdelphij	int pipes[2];
199170754Sdelphij
200170754Sdelphij	if (pipe (pipes) != 0)
201170754Sdelphij	  pfatal_with_name ("pipe");
202170754Sdelphij
203170754Sdelphij	pr_pid = vfork ();
204170754Sdelphij	if (pr_pid < 0)
205170754Sdelphij	  pfatal_with_name ("fork");
206170754Sdelphij
207170754Sdelphij	if (pr_pid == 0)
208170754Sdelphij	  {
209170754Sdelphij	    close (pipes[1]);
210170754Sdelphij	    if (pipes[0] != STDIN_FILENO)
211170754Sdelphij	      {
212170754Sdelphij		if (dup2 (pipes[0], STDIN_FILENO) < 0)
213170754Sdelphij		  pfatal_with_name ("dup2");
214170754Sdelphij		close (pipes[0]);
215170754Sdelphij	      }
216170754Sdelphij
217170754Sdelphij	    execl (pr_program, pr_program, "-h", name, (char *) 0);
218170754Sdelphij	    _exit (errno == ENOENT ? 127 : 126);
219170754Sdelphij	  }
220170754Sdelphij	else
221170754Sdelphij	  {
222170754Sdelphij	    close (pipes[0]);
223170754Sdelphij	    outfile = fdopen (pipes[1], "w");
224170754Sdelphij	    if (!outfile)
225170754Sdelphij	      pfatal_with_name ("fdopen");
226170754Sdelphij	  }
227170754Sdelphij#else
228170754Sdelphij	char *command = xmalloc (sizeof pr_program - 1 + 7
229170754Sdelphij				 + quote_system_arg ((char *) 0, name) + 1);
230170754Sdelphij	char *p;
231170754Sdelphij	sprintf (command, "%s -f -h ", pr_program);
232170754Sdelphij	p = command + sizeof pr_program - 1 + 7;
233170754Sdelphij	p += quote_system_arg (p, name);
234170754Sdelphij	*p = 0;
235170754Sdelphij	errno = 0;
236170754Sdelphij	outfile = popen (command, "w");
237170754Sdelphij	if (!outfile)
238170754Sdelphij	  pfatal_with_name (command);
239170754Sdelphij	free (command);
240170754Sdelphij#endif
241170754Sdelphij      }
242170754Sdelphij    }
243170754Sdelphij  else
244170754Sdelphij    {
245170754Sdelphij
246170754Sdelphij      /* If -l was not specified, output the diff straight to `stdout'.  */
247170754Sdelphij
248170754Sdelphij      outfile = stdout;
249170754Sdelphij
250170754Sdelphij      /* If handling multiple files (because scanning a directory),
251170754Sdelphij	 print which files the following output is about.  */
252170754Sdelphij      if (currently_recursive)
253170754Sdelphij	printf ("%s\n", name);
254170754Sdelphij    }
255170754Sdelphij
256170754Sdelphij  free (name);
257170754Sdelphij
258170754Sdelphij  /* A special header is needed at the beginning of context output.  */
259170754Sdelphij  switch (output_style)
260170754Sdelphij    {
261170754Sdelphij    case OUTPUT_CONTEXT:
262170754Sdelphij      print_context_header (files, false);
263170754Sdelphij      break;
264170754Sdelphij
265170754Sdelphij    case OUTPUT_UNIFIED:
266170754Sdelphij      print_context_header (files, true);
267170754Sdelphij      break;
268170754Sdelphij
269170754Sdelphij    default:
270170754Sdelphij      break;
271170754Sdelphij    }
272170754Sdelphij}
273170754Sdelphij
274170754Sdelphij/* Call after the end of output of diffs for one file.
275170754Sdelphij   Close OUTFILE and get rid of the `pr' subfork.  */
276170754Sdelphij
277170754Sdelphijvoid
278170754Sdelphijfinish_output (void)
279170754Sdelphij{
280170754Sdelphij  if (outfile != 0 && outfile != stdout)
281170754Sdelphij    {
282170754Sdelphij      int status;
283170754Sdelphij      int wstatus;
284170754Sdelphij      int werrno = 0;
285170754Sdelphij      if (ferror (outfile))
286170754Sdelphij	fatal ("write failed");
287170754Sdelphij#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
288170754Sdelphij      wstatus = pclose (outfile);
289170754Sdelphij      if (wstatus == -1)
290170754Sdelphij	werrno = errno;
291170754Sdelphij#else
292170754Sdelphij      if (fclose (outfile) != 0)
293170754Sdelphij	pfatal_with_name (_("write failed"));
294170754Sdelphij      if (waitpid (pr_pid, &wstatus, 0) < 0)
295170754Sdelphij	pfatal_with_name ("waitpid");
296170754Sdelphij#endif
297170754Sdelphij      status = (! werrno && WIFEXITED (wstatus)
298170754Sdelphij		? WEXITSTATUS (wstatus)
299170754Sdelphij		: INT_MAX);
300170754Sdelphij      if (status)
301170754Sdelphij	error (EXIT_TROUBLE, werrno,
302170754Sdelphij	       _(status == 126
303170754Sdelphij		 ? "subsidiary program `%s' could not be invoked"
304170754Sdelphij		 : status == 127
305170754Sdelphij		 ? "subsidiary program `%s' not found"
306170754Sdelphij		 : status == INT_MAX
307170754Sdelphij		 ? "subsidiary program `%s' failed"
308170754Sdelphij		 : "subsidiary program `%s' failed (exit status %d)"),
309170754Sdelphij	       pr_program, status);
310170754Sdelphij    }
311170754Sdelphij
312170754Sdelphij  outfile = 0;
313170754Sdelphij}
314170754Sdelphij
315170754Sdelphij/* Compare two lines (typically one from each input file)
316170754Sdelphij   according to the command line options.
317170754Sdelphij   For efficiency, this is invoked only when the lines do not match exactly
318170754Sdelphij   but an option like -i might cause us to ignore the difference.
319170754Sdelphij   Return nonzero if the lines differ.  */
320170754Sdelphij
321170754Sdelphijbool
322170754Sdelphijlines_differ (char const *s1, char const *s2)
323170754Sdelphij{
324170754Sdelphij  register char const *t1 = s1;
325170754Sdelphij  register char const *t2 = s2;
326170754Sdelphij  size_t column = 0;
327170754Sdelphij
328170754Sdelphij  while (1)
329170754Sdelphij    {
330170754Sdelphij      register unsigned char c1 = *t1++;
331170754Sdelphij      register unsigned char c2 = *t2++;
332170754Sdelphij
333170754Sdelphij      /* Test for exact char equality first, since it's a common case.  */
334170754Sdelphij      if (c1 != c2)
335170754Sdelphij	{
336170754Sdelphij	  switch (ignore_white_space)
337170754Sdelphij	    {
338170754Sdelphij	    case IGNORE_ALL_SPACE:
339170754Sdelphij	      /* For -w, just skip past any white space.  */
340170754Sdelphij	      while (isspace (c1) && c1 != '\n') c1 = *t1++;
341170754Sdelphij	      while (isspace (c2) && c2 != '\n') c2 = *t2++;
342170754Sdelphij	      break;
343170754Sdelphij
344170754Sdelphij	    case IGNORE_SPACE_CHANGE:
345170754Sdelphij	      /* For -b, advance past any sequence of white space in
346170754Sdelphij		 line 1 and consider it just one space, or nothing at
347170754Sdelphij		 all if it is at the end of the line.  */
348170754Sdelphij	      if (isspace (c1))
349170754Sdelphij		{
350170754Sdelphij		  while (c1 != '\n')
351170754Sdelphij		    {
352170754Sdelphij		      c1 = *t1++;
353170754Sdelphij		      if (! isspace (c1))
354170754Sdelphij			{
355170754Sdelphij			  --t1;
356170754Sdelphij			  c1 = ' ';
357170754Sdelphij			  break;
358170754Sdelphij			}
359170754Sdelphij		    }
360170754Sdelphij		}
361170754Sdelphij
362170754Sdelphij	      /* Likewise for line 2.  */
363170754Sdelphij	      if (isspace (c2))
364170754Sdelphij		{
365170754Sdelphij		  while (c2 != '\n')
366170754Sdelphij		    {
367170754Sdelphij		      c2 = *t2++;
368170754Sdelphij		      if (! isspace (c2))
369170754Sdelphij			{
370170754Sdelphij			  --t2;
371170754Sdelphij			  c2 = ' ';
372170754Sdelphij			  break;
373170754Sdelphij			}
374170754Sdelphij		    }
375170754Sdelphij		}
376170754Sdelphij
377170754Sdelphij	      if (c1 != c2)
378170754Sdelphij		{
379170754Sdelphij		  /* If we went too far when doing the simple test
380170754Sdelphij		     for equality, go back to the first non-white-space
381170754Sdelphij		     character in both sides and try again.  */
382170754Sdelphij		  if (c2 == ' ' && c1 != '\n'
383170754Sdelphij		      && s1 + 1 < t1
384170754Sdelphij		      && isspace ((unsigned char) t1[-2]))
385170754Sdelphij		    {
386170754Sdelphij		      --t1;
387170754Sdelphij		      continue;
388170754Sdelphij		    }
389170754Sdelphij		  if (c1 == ' ' && c2 != '\n'
390170754Sdelphij		      && s2 + 1 < t2
391170754Sdelphij		      && isspace ((unsigned char) t2[-2]))
392170754Sdelphij		    {
393170754Sdelphij		      --t2;
394170754Sdelphij		      continue;
395170754Sdelphij		    }
396170754Sdelphij		}
397170754Sdelphij
398170754Sdelphij	      break;
399170754Sdelphij
400170754Sdelphij	    case IGNORE_TAB_EXPANSION:
401170754Sdelphij	      if ((c1 == ' ' && c2 == '\t')
402170754Sdelphij		  || (c1 == '\t' && c2 == ' '))
403170754Sdelphij		{
404170754Sdelphij		  size_t column2 = column;
405170754Sdelphij		  for (;; c1 = *t1++)
406170754Sdelphij		    {
407170754Sdelphij		      if (c1 == ' ')
408170754Sdelphij			column++;
409170754Sdelphij		      else if (c1 == '\t')
410170754Sdelphij			column += tabsize - column % tabsize;
411170754Sdelphij		      else
412170754Sdelphij			break;
413170754Sdelphij		    }
414170754Sdelphij		  for (;; c2 = *t2++)
415170754Sdelphij		    {
416170754Sdelphij		      if (c2 == ' ')
417170754Sdelphij			column2++;
418170754Sdelphij		      else if (c2 == '\t')
419170754Sdelphij			column2 += tabsize - column2 % tabsize;
420170754Sdelphij		      else
421170754Sdelphij			break;
422170754Sdelphij		    }
423170754Sdelphij		  if (column != column2)
424170754Sdelphij		    return true;
425170754Sdelphij		}
426170754Sdelphij	      break;
427170754Sdelphij
428170754Sdelphij	    case IGNORE_NO_WHITE_SPACE:
429170754Sdelphij	      break;
430170754Sdelphij	    }
431170754Sdelphij
432170754Sdelphij	  /* Lowercase all letters if -i is specified.  */
433170754Sdelphij
434170754Sdelphij	  if (ignore_case)
435170754Sdelphij	    {
436170754Sdelphij	      c1 = tolower (c1);
437170754Sdelphij	      c2 = tolower (c2);
438170754Sdelphij	    }
439170754Sdelphij
440170754Sdelphij	  if (c1 != c2)
441170754Sdelphij	    break;
442170754Sdelphij	}
443170754Sdelphij      if (c1 == '\n')
444170754Sdelphij	return false;
445170754Sdelphij
446170754Sdelphij      column += c1 == '\t' ? tabsize - column % tabsize : 1;
447170754Sdelphij    }
448170754Sdelphij
449170754Sdelphij  return true;
450170754Sdelphij}
451170754Sdelphij
452170754Sdelphij/* Find the consecutive changes at the start of the script START.
453170754Sdelphij   Return the last link before the first gap.  */
454170754Sdelphij
455170754Sdelphijstruct change *
456170754Sdelphijfind_change (struct change *start)
457170754Sdelphij{
458170754Sdelphij  return start;
459170754Sdelphij}
460170754Sdelphij
461170754Sdelphijstruct change *
462170754Sdelphijfind_reverse_change (struct change *start)
463170754Sdelphij{
464170754Sdelphij  return start;
465170754Sdelphij}
466170754Sdelphij
467170754Sdelphij/* Divide SCRIPT into pieces by calling HUNKFUN and
468170754Sdelphij   print each piece with PRINTFUN.
469170754Sdelphij   Both functions take one arg, an edit script.
470170754Sdelphij
471170754Sdelphij   HUNKFUN is called with the tail of the script
472170754Sdelphij   and returns the last link that belongs together with the start
473170754Sdelphij   of the tail.
474170754Sdelphij
475170754Sdelphij   PRINTFUN takes a subscript which belongs together (with a null
476170754Sdelphij   link at the end) and prints it.  */
477170754Sdelphij
478170754Sdelphijvoid
479170754Sdelphijprint_script (struct change *script,
480170754Sdelphij	      struct change * (*hunkfun) (struct change *),
481170754Sdelphij	      void (*printfun) (struct change *))
482170754Sdelphij{
483170754Sdelphij  struct change *next = script;
484170754Sdelphij
485170754Sdelphij  while (next)
486170754Sdelphij    {
487170754Sdelphij      struct change *this, *end;
488170754Sdelphij
489170754Sdelphij      /* Find a set of changes that belong together.  */
490170754Sdelphij      this = next;
491170754Sdelphij      end = (*hunkfun) (next);
492170754Sdelphij
493170754Sdelphij      /* Disconnect them from the rest of the changes,
494170754Sdelphij	 making them a hunk, and remember the rest for next iteration.  */
495170754Sdelphij      next = end->link;
496170754Sdelphij      end->link = 0;
497170754Sdelphij#ifdef DEBUG
498170754Sdelphij      debug_script (this);
499170754Sdelphij#endif
500170754Sdelphij
501170754Sdelphij      /* Print this hunk.  */
502170754Sdelphij      (*printfun) (this);
503170754Sdelphij
504170754Sdelphij      /* Reconnect the script so it will all be freed properly.  */
505170754Sdelphij      end->link = next;
506170754Sdelphij    }
507170754Sdelphij}
508170754Sdelphij
509170754Sdelphij/* Print the text of a single line LINE,
510170754Sdelphij   flagging it with the characters in LINE_FLAG (which say whether
511170754Sdelphij   the line is inserted, deleted, changed, etc.).  */
512170754Sdelphij
513170754Sdelphijvoid
514170754Sdelphijprint_1_line (char const *line_flag, char const *const *line)
515170754Sdelphij{
516170754Sdelphij  char const *base = line[0], *limit = line[1]; /* Help the compiler.  */
517170754Sdelphij  FILE *out = outfile; /* Help the compiler some more.  */
518170754Sdelphij  char const *flag_format = 0;
519170754Sdelphij
520170754Sdelphij  /* If -T was specified, use a Tab between the line-flag and the text.
521170754Sdelphij     Otherwise use a Space (as Unix diff does).
522170754Sdelphij     Print neither space nor tab if line-flags are empty.  */
523170754Sdelphij
524170754Sdelphij  if (line_flag && *line_flag)
525170754Sdelphij    {
526170754Sdelphij      flag_format = initial_tab ? "%s\t" : "%s ";
527170754Sdelphij      fprintf (out, flag_format, line_flag);
528170754Sdelphij    }
529170754Sdelphij
530170754Sdelphij  output_1_line (base, limit, flag_format, line_flag);
531170754Sdelphij
532170754Sdelphij  if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
533170754Sdelphij    fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
534170754Sdelphij}
535170754Sdelphij
536170754Sdelphij/* Output a line from BASE up to LIMIT.
537170754Sdelphij   With -t, expand white space characters to spaces, and if FLAG_FORMAT
538170754Sdelphij   is nonzero, output it with argument LINE_FLAG after every
539170754Sdelphij   internal carriage return, so that tab stops continue to line up.  */
540170754Sdelphij
541170754Sdelphijvoid
542170754Sdelphijoutput_1_line (char const *base, char const *limit, char const *flag_format,
543170754Sdelphij	       char const *line_flag)
544170754Sdelphij{
545170754Sdelphij  if (!expand_tabs)
546170754Sdelphij    fwrite (base, sizeof (char), limit - base, outfile);
547170754Sdelphij  else
548170754Sdelphij    {
549170754Sdelphij      register FILE *out = outfile;
550170754Sdelphij      register unsigned char c;
551170754Sdelphij      register char const *t = base;
552170754Sdelphij      register size_t column = 0;
553170754Sdelphij      size_t tab_size = tabsize;
554170754Sdelphij
555170754Sdelphij      while (t < limit)
556170754Sdelphij	switch ((c = *t++))
557170754Sdelphij	  {
558170754Sdelphij	  case '\t':
559170754Sdelphij	    {
560170754Sdelphij	      size_t spaces = tab_size - column % tab_size;
561170754Sdelphij	      column += spaces;
562170754Sdelphij	      do
563170754Sdelphij		putc (' ', out);
564170754Sdelphij	      while (--spaces);
565170754Sdelphij	    }
566170754Sdelphij	    break;
567170754Sdelphij
568170754Sdelphij	  case '\r':
569170754Sdelphij	    putc (c, out);
570170754Sdelphij	    if (flag_format && t < limit && *t != '\n')
571170754Sdelphij	      fprintf (out, flag_format, line_flag);
572170754Sdelphij	    column = 0;
573170754Sdelphij	    break;
574170754Sdelphij
575170754Sdelphij	  case '\b':
576170754Sdelphij	    if (column == 0)
577170754Sdelphij	      continue;
578170754Sdelphij	    column--;
579170754Sdelphij	    putc (c, out);
580170754Sdelphij	    break;
581170754Sdelphij
582170754Sdelphij	  default:
583170754Sdelphij	    column += isprint (c) != 0;
584170754Sdelphij	    putc (c, out);
585170754Sdelphij	    break;
586170754Sdelphij	  }
587170754Sdelphij    }
588170754Sdelphij}
589170754Sdelphij
590170754Sdelphijchar const change_letter[] = { 0, 'd', 'a', 'c' };
591170754Sdelphij
592170754Sdelphij/* Translate an internal line number (an index into diff's table of lines)
593170754Sdelphij   into an actual line number in the input file.
594170754Sdelphij   The internal line number is I.  FILE points to the data on the file.
595170754Sdelphij
596170754Sdelphij   Internal line numbers count from 0 starting after the prefix.
597170754Sdelphij   Actual line numbers count from 1 within the entire file.  */
598170754Sdelphij
599170754Sdelphijlin
600170754Sdelphijtranslate_line_number (struct file_data const *file, lin i)
601170754Sdelphij{
602170754Sdelphij  return i + file->prefix_lines + 1;
603170754Sdelphij}
604170754Sdelphij
605170754Sdelphij/* Translate a line number range.  This is always done for printing,
606170754Sdelphij   so for convenience translate to long int rather than lin, so that the
607170754Sdelphij   caller can use printf with "%ld" without casting.  */
608170754Sdelphij
609170754Sdelphijvoid
610170754Sdelphijtranslate_range (struct file_data const *file,
611170754Sdelphij		 lin a, lin b,
612170754Sdelphij		 long int *aptr, long int *bptr)
613170754Sdelphij{
614170754Sdelphij  *aptr = translate_line_number (file, a - 1) + 1;
615170754Sdelphij  *bptr = translate_line_number (file, b + 1) - 1;
616170754Sdelphij}
617170754Sdelphij
618170754Sdelphij/* Print a pair of line numbers with SEPCHAR, translated for file FILE.
619170754Sdelphij   If the two numbers are identical, print just one number.
620170754Sdelphij
621170754Sdelphij   Args A and B are internal line numbers.
622170754Sdelphij   We print the translated (real) line numbers.  */
623170754Sdelphij
624170754Sdelphijvoid
625170754Sdelphijprint_number_range (char sepchar, struct file_data *file, lin a, lin b)
626170754Sdelphij{
627170754Sdelphij  long int trans_a, trans_b;
628170754Sdelphij  translate_range (file, a, b, &trans_a, &trans_b);
629170754Sdelphij
630170754Sdelphij  /* Note: we can have B < A in the case of a range of no lines.
631170754Sdelphij     In this case, we should print the line number before the range,
632170754Sdelphij     which is B.  */
633170754Sdelphij  if (trans_b > trans_a)
634170754Sdelphij    fprintf (outfile, "%ld%c%ld", trans_a, sepchar, trans_b);
635170754Sdelphij  else
636170754Sdelphij    fprintf (outfile, "%ld", trans_b);
637170754Sdelphij}
638170754Sdelphij
639170754Sdelphij/* Look at a hunk of edit script and report the range of lines in each file
640170754Sdelphij   that it applies to.  HUNK is the start of the hunk, which is a chain
641170754Sdelphij   of `struct change'.  The first and last line numbers of file 0 are stored in
642170754Sdelphij   *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
643170754Sdelphij   Note that these are internal line numbers that count from 0.
644170754Sdelphij
645170754Sdelphij   If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
646170754Sdelphij
647170754Sdelphij   Return UNCHANGED if only ignorable lines are inserted or deleted,
648170754Sdelphij   OLD if lines of file 0 are deleted,
649170754Sdelphij   NEW if lines of file 1 are inserted,
650170754Sdelphij   and CHANGED if both kinds of changes are found. */
651170754Sdelphij
652170754Sdelphijenum changes
653170754Sdelphijanalyze_hunk (struct change *hunk,
654170754Sdelphij	      lin *first0, lin *last0,
655170754Sdelphij	      lin *first1, lin *last1)
656170754Sdelphij{
657170754Sdelphij  struct change *next;
658170754Sdelphij  lin l0, l1;
659170754Sdelphij  lin show_from, show_to;
660170754Sdelphij  lin i;
661170754Sdelphij  bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
662170754Sdelphij  size_t trivial_length = ignore_blank_lines - 1;
663170754Sdelphij    /* If 0, ignore zero-length lines;
664170754Sdelphij       if SIZE_MAX, do not ignore lines just because of their length.  */
665170754Sdelphij  bool skip_leading_white_space =
666170754Sdelphij    (ignore_blank_lines && IGNORE_SPACE_CHANGE <= ignore_white_space);
667170754Sdelphij
668170754Sdelphij  char const * const *linbuf0 = files[0].linbuf;  /* Help the compiler.  */
669170754Sdelphij  char const * const *linbuf1 = files[1].linbuf;
670170754Sdelphij
671170754Sdelphij  show_from = show_to = 0;
672170754Sdelphij
673170754Sdelphij  *first0 = hunk->line0;
674170754Sdelphij  *first1 = hunk->line1;
675170754Sdelphij
676170754Sdelphij  next = hunk;
677170754Sdelphij  do
678170754Sdelphij    {
679170754Sdelphij      l0 = next->line0 + next->deleted - 1;
680170754Sdelphij      l1 = next->line1 + next->inserted - 1;
681170754Sdelphij      show_from += next->deleted;
682170754Sdelphij      show_to += next->inserted;
683170754Sdelphij
684170754Sdelphij      for (i = next->line0; i <= l0 && trivial; i++)
685170754Sdelphij	{
686170754Sdelphij	  char const *line = linbuf0[i];
687170754Sdelphij	  char const *newline = linbuf0[i + 1] - 1;
688170754Sdelphij	  size_t len = newline - line;
689170754Sdelphij	  char const *p = line;
690170754Sdelphij	  if (skip_leading_white_space)
691170754Sdelphij	    while (isspace ((unsigned char) *p) && *p != '\n')
692170754Sdelphij	      p++;
693170754Sdelphij	  if (newline - p != trivial_length
694170754Sdelphij	      && (! ignore_regexp.fastmap
695170754Sdelphij		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
696170754Sdelphij	    trivial = 0;
697170754Sdelphij	}
698170754Sdelphij
699170754Sdelphij      for (i = next->line1; i <= l1 && trivial; i++)
700170754Sdelphij	{
701170754Sdelphij	  char const *line = linbuf1[i];
702170754Sdelphij	  char const *newline = linbuf1[i + 1] - 1;
703170754Sdelphij	  size_t len = newline - line;
704170754Sdelphij	  char const *p = line;
705170754Sdelphij	  if (skip_leading_white_space)
706170754Sdelphij	    while (isspace ((unsigned char) *p) && *p != '\n')
707170754Sdelphij	      p++;
708170754Sdelphij	  if (newline - p != trivial_length
709170754Sdelphij	      && (! ignore_regexp.fastmap
710170754Sdelphij		  || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
711170754Sdelphij	    trivial = 0;
712170754Sdelphij	}
713170754Sdelphij    }
714170754Sdelphij  while ((next = next->link) != 0);
715170754Sdelphij
716170754Sdelphij  *last0 = l0;
717170754Sdelphij  *last1 = l1;
718170754Sdelphij
719170754Sdelphij  /* If all inserted or deleted lines are ignorable,
720170754Sdelphij     tell the caller to ignore this hunk.  */
721170754Sdelphij
722170754Sdelphij  if (trivial)
723170754Sdelphij    return UNCHANGED;
724170754Sdelphij
725170754Sdelphij  return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
726170754Sdelphij}
727170754Sdelphij
728170754Sdelphij/* Concatenate three strings, returning a newly malloc'd string.  */
729170754Sdelphij
730170754Sdelphijchar *
731170754Sdelphijconcat (char const *s1, char const *s2, char const *s3)
732170754Sdelphij{
733170754Sdelphij  char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
734170754Sdelphij  sprintf (new, "%s%s%s", s1, s2, s3);
735170754Sdelphij  return new;
736170754Sdelphij}
737170754Sdelphij
738170754Sdelphij/* Yield a new block of SIZE bytes, initialized to zero.  */
739170754Sdelphij
740170754Sdelphijvoid *
741170754Sdelphijzalloc (size_t size)
742170754Sdelphij{
743170754Sdelphij  void *p = xmalloc (size);
744170754Sdelphij  memset (p, 0, size);
745170754Sdelphij  return p;
746170754Sdelphij}
747170754Sdelphij
748170754Sdelphij/* Yield the newly malloc'd pathname
749170754Sdelphij   of the file in DIR whose filename is FILE.  */
750170754Sdelphij
751170754Sdelphijchar *
752170754Sdelphijdir_file_pathname (char const *dir, char const *file)
753170754Sdelphij{
754170754Sdelphij  char const *base = base_name (dir);
755170754Sdelphij  bool omit_slash = !*base || base[strlen (base) - 1] == '/';
756170754Sdelphij  return concat (dir, "/" + omit_slash, file);
757170754Sdelphij}
758170754Sdelphij
759170754Sdelphijvoid
760170754Sdelphijdebug_script (struct change *sp)
761170754Sdelphij{
762170754Sdelphij  fflush (stdout);
763170754Sdelphij
764170754Sdelphij  for (; sp; sp = sp->link)
765170754Sdelphij    {
766170754Sdelphij      long int line0 = sp->line0;
767170754Sdelphij      long int line1 = sp->line1;
768170754Sdelphij      long int deleted = sp->deleted;
769170754Sdelphij      long int inserted = sp->inserted;
770170754Sdelphij      fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n",
771170754Sdelphij	       line0, line1, deleted, inserted);
772170754Sdelphij    }
773170754Sdelphij
774170754Sdelphij  fflush (stderr);
775170754Sdelphij}
776