xref: /openbsd-src/gnu/usr.bin/cvs/diff/util.c (revision 2286d8ed900f26153a3cd5227a124b1c0adce72f)
1*2286d8edStholo /* Support routines for GNU DIFF.
2*2286d8edStholo    Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
3*2286d8edStholo 
4*2286d8edStholo This file is part of GNU DIFF.
5*2286d8edStholo 
6*2286d8edStholo GNU DIFF is free software; you can redistribute it and/or modify
7*2286d8edStholo it under the terms of the GNU General Public License as published by
8*2286d8edStholo the Free Software Foundation; either version 2, or (at your option)
9*2286d8edStholo any later version.
10*2286d8edStholo 
11*2286d8edStholo GNU DIFF is distributed in the hope that it will be useful,
12*2286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
13*2286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*2286d8edStholo GNU General Public License for more details.
15*2286d8edStholo 
16*2286d8edStholo You should have received a copy of the GNU General Public License
17*2286d8edStholo along with GNU DIFF; see the file COPYING.  If not, write to
18*2286d8edStholo the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19*2286d8edStholo 
20*2286d8edStholo #include "diff.h"
21*2286d8edStholo 
22*2286d8edStholo /* Queue up one-line messages to be printed at the end,
23*2286d8edStholo    when -l is specified.  Each message is recorded with a `struct msg'.  */
24*2286d8edStholo 
25*2286d8edStholo struct msg
26*2286d8edStholo {
27*2286d8edStholo   struct msg *next;
28*2286d8edStholo   char const *format;
29*2286d8edStholo   char const *arg1;
30*2286d8edStholo   char const *arg2;
31*2286d8edStholo   char const *arg3;
32*2286d8edStholo   char const *arg4;
33*2286d8edStholo };
34*2286d8edStholo 
35*2286d8edStholo /* Head of the chain of queues messages.  */
36*2286d8edStholo 
37*2286d8edStholo static struct msg *msg_chain;
38*2286d8edStholo 
39*2286d8edStholo /* Tail of the chain of queues messages.  */
40*2286d8edStholo 
41*2286d8edStholo static struct msg **msg_chain_end = &msg_chain;
42*2286d8edStholo 
43*2286d8edStholo /* Use when a system call returns non-zero status.
44*2286d8edStholo    TEXT should normally be the file name.  */
45*2286d8edStholo 
46*2286d8edStholo void
47*2286d8edStholo perror_with_name (text)
48*2286d8edStholo      char const *text;
49*2286d8edStholo {
50*2286d8edStholo   int e = errno;
51*2286d8edStholo   fprintf (stderr, "%s: ", diff_program_name);
52*2286d8edStholo   errno = e;
53*2286d8edStholo   perror (text);
54*2286d8edStholo }
55*2286d8edStholo 
56*2286d8edStholo /* Use when a system call returns non-zero status and that is fatal.  */
57*2286d8edStholo 
58*2286d8edStholo void
59*2286d8edStholo pfatal_with_name (text)
60*2286d8edStholo      char const *text;
61*2286d8edStholo {
62*2286d8edStholo   int e = errno;
63*2286d8edStholo   print_message_queue ();
64*2286d8edStholo   fprintf (stderr, "%s: ", diff_program_name);
65*2286d8edStholo   errno = e;
66*2286d8edStholo   perror (text);
67*2286d8edStholo   DIFF_ABORT (2);
68*2286d8edStholo }
69*2286d8edStholo 
70*2286d8edStholo /* Print an error message from the format-string FORMAT
71*2286d8edStholo    with args ARG1 and ARG2.  */
72*2286d8edStholo 
73*2286d8edStholo void
74*2286d8edStholo diff_error (format, arg, arg1)
75*2286d8edStholo      char const *format, *arg, *arg1;
76*2286d8edStholo {
77*2286d8edStholo   fprintf (stderr, "%s: ", diff_program_name);
78*2286d8edStholo   fprintf (stderr, format, arg, arg1);
79*2286d8edStholo   fprintf (stderr, "\n");
80*2286d8edStholo }
81*2286d8edStholo 
82*2286d8edStholo /* Print an error message containing the string TEXT, then exit.  */
83*2286d8edStholo 
84*2286d8edStholo void
85*2286d8edStholo fatal (m)
86*2286d8edStholo      char const *m;
87*2286d8edStholo {
88*2286d8edStholo   print_message_queue ();
89*2286d8edStholo   diff_error ("%s", m, 0);
90*2286d8edStholo   DIFF_ABORT (2);
91*2286d8edStholo }
92*2286d8edStholo 
93*2286d8edStholo /* Like printf, except if -l in effect then save the message and print later.
94*2286d8edStholo    This is used for things like "binary files differ" and "Only in ...".  */
95*2286d8edStholo 
96*2286d8edStholo void
97*2286d8edStholo message (format, arg1, arg2)
98*2286d8edStholo      char const *format, *arg1, *arg2;
99*2286d8edStholo {
100*2286d8edStholo   message5 (format, arg1, arg2, 0, 0);
101*2286d8edStholo }
102*2286d8edStholo 
103*2286d8edStholo void
104*2286d8edStholo message5 (format, arg1, arg2, arg3, arg4)
105*2286d8edStholo      char const *format, *arg1, *arg2, *arg3, *arg4;
106*2286d8edStholo {
107*2286d8edStholo   if (paginate_flag)
108*2286d8edStholo     {
109*2286d8edStholo       struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
110*2286d8edStholo       new->format = format;
111*2286d8edStholo       new->arg1 = concat (arg1, "", "");
112*2286d8edStholo       new->arg2 = concat (arg2, "", "");
113*2286d8edStholo       new->arg3 = arg3 ? concat (arg3, "", "") : 0;
114*2286d8edStholo       new->arg4 = arg4 ? concat (arg4, "", "") : 0;
115*2286d8edStholo       new->next = 0;
116*2286d8edStholo       *msg_chain_end = new;
117*2286d8edStholo       msg_chain_end = &new->next;
118*2286d8edStholo     }
119*2286d8edStholo   else
120*2286d8edStholo     {
121*2286d8edStholo       if (sdiff_help_sdiff)
122*2286d8edStholo 	putc (' ', outfile);
123*2286d8edStholo       fprintf (outfile, format, arg1, arg2, arg3, arg4);
124*2286d8edStholo     }
125*2286d8edStholo }
126*2286d8edStholo 
127*2286d8edStholo /* Output all the messages that were saved up by calls to `message'.  */
128*2286d8edStholo 
129*2286d8edStholo void
130*2286d8edStholo print_message_queue ()
131*2286d8edStholo {
132*2286d8edStholo   struct msg *m;
133*2286d8edStholo 
134*2286d8edStholo   for (m = msg_chain; m; m = m->next)
135*2286d8edStholo     fprintf (outfile, m->format, m->arg1, m->arg2, m->arg3, m->arg4);
136*2286d8edStholo }
137*2286d8edStholo 
138*2286d8edStholo /* Call before outputting the results of comparing files NAME0 and NAME1
139*2286d8edStholo    to set up OUTFILE, the stdio stream for the output to go to.
140*2286d8edStholo 
141*2286d8edStholo    Usually, OUTFILE is just stdout.  But when -l was specified
142*2286d8edStholo    we fork off a `pr' and make OUTFILE a pipe to it.
143*2286d8edStholo    `pr' then outputs to our stdout.  */
144*2286d8edStholo 
145*2286d8edStholo static char const *current_name0;
146*2286d8edStholo static char const *current_name1;
147*2286d8edStholo static int current_depth;
148*2286d8edStholo 
149*2286d8edStholo static int output_in_progress = 0;
150*2286d8edStholo 
151*2286d8edStholo void
152*2286d8edStholo setup_output (name0, name1, depth)
153*2286d8edStholo      char const *name0, *name1;
154*2286d8edStholo      int depth;
155*2286d8edStholo {
156*2286d8edStholo   current_name0 = name0;
157*2286d8edStholo   current_name1 = name1;
158*2286d8edStholo   current_depth = depth;
159*2286d8edStholo }
160*2286d8edStholo 
161*2286d8edStholo #if HAVE_FORK && defined (PR_PROGRAM)
162*2286d8edStholo static pid_t pr_pid;
163*2286d8edStholo #endif
164*2286d8edStholo 
165*2286d8edStholo void
166*2286d8edStholo begin_output ()
167*2286d8edStholo {
168*2286d8edStholo   char *name;
169*2286d8edStholo 
170*2286d8edStholo   if (output_in_progress)
171*2286d8edStholo     return;
172*2286d8edStholo   output_in_progress = 1;
173*2286d8edStholo 
174*2286d8edStholo   /* Construct the header of this piece of diff.  */
175*2286d8edStholo   name = xmalloc (strlen (current_name0) + strlen (current_name1)
176*2286d8edStholo 		  + strlen (switch_string) + 7);
177*2286d8edStholo   /* Posix.2 section 4.17.6.1.1 specifies this format.  But there is a
178*2286d8edStholo      bug in the first printing (IEEE Std 1003.2-1992 p 251 l 3304):
179*2286d8edStholo      it says that we must print only the last component of the pathnames.
180*2286d8edStholo      This requirement is silly and does not match historical practice.  */
181*2286d8edStholo   sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
182*2286d8edStholo 
183*2286d8edStholo   if (paginate_flag)
184*2286d8edStholo     {
185*2286d8edStholo       /* Make OUTFILE a pipe to a subsidiary `pr'.  */
186*2286d8edStholo 
187*2286d8edStholo #ifdef PR_PROGRAM
188*2286d8edStholo 
189*2286d8edStholo # if HAVE_FORK
190*2286d8edStholo       int pipes[2];
191*2286d8edStholo 
192*2286d8edStholo       if (pipe (pipes) != 0)
193*2286d8edStholo 	pfatal_with_name ("pipe");
194*2286d8edStholo 
195*2286d8edStholo       fflush (stdout);
196*2286d8edStholo 
197*2286d8edStholo       pr_pid = vfork ();
198*2286d8edStholo       if (pr_pid < 0)
199*2286d8edStholo 	pfatal_with_name ("vfork");
200*2286d8edStholo 
201*2286d8edStholo       if (pr_pid == 0)
202*2286d8edStholo 	{
203*2286d8edStholo 	  close (pipes[1]);
204*2286d8edStholo 	  if (pipes[0] != STDIN_FILENO)
205*2286d8edStholo 	    {
206*2286d8edStholo 	      if (dup2 (pipes[0], STDIN_FILENO) < 0)
207*2286d8edStholo 		pfatal_with_name ("dup2");
208*2286d8edStholo 	      close (pipes[0]);
209*2286d8edStholo 	    }
210*2286d8edStholo 
211*2286d8edStholo 	  execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, 0);
212*2286d8edStholo 	  pfatal_with_name (PR_PROGRAM);
213*2286d8edStholo 	}
214*2286d8edStholo       else
215*2286d8edStholo 	{
216*2286d8edStholo 	  close (pipes[0]);
217*2286d8edStholo 	  outfile = fdopen (pipes[1], "w");
218*2286d8edStholo 	  if (!outfile)
219*2286d8edStholo 	    pfatal_with_name ("fdopen");
220*2286d8edStholo 	}
221*2286d8edStholo # else /* ! HAVE_FORK */
222*2286d8edStholo       char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10);
223*2286d8edStholo       char *p;
224*2286d8edStholo       char const *a = name;
225*2286d8edStholo       sprintf (command, "%s -f -h ", PR_PROGRAM);
226*2286d8edStholo       p = command + strlen (command);
227*2286d8edStholo       SYSTEM_QUOTE_ARG (p, a);
228*2286d8edStholo       *p = 0;
229*2286d8edStholo       outfile = popen (command, "w");
230*2286d8edStholo       if (!outfile)
231*2286d8edStholo 	pfatal_with_name (command);
232*2286d8edStholo       free (command);
233*2286d8edStholo # endif /* ! HAVE_FORK */
234*2286d8edStholo #else
235*2286d8edStholo       fatal ("This port does not support the --paginate option to diff.");
236*2286d8edStholo #endif
237*2286d8edStholo     }
238*2286d8edStholo   else
239*2286d8edStholo     {
240*2286d8edStholo 
241*2286d8edStholo       /* If -l was not specified, output the diff straight to `stdout'.  */
242*2286d8edStholo 
243*2286d8edStholo       /* If handling multiple files (because scanning a directory),
244*2286d8edStholo 	 print which files the following output is about.  */
245*2286d8edStholo       if (current_depth > 0)
246*2286d8edStholo 	fprintf (outfile, "%s\n", name);
247*2286d8edStholo     }
248*2286d8edStholo 
249*2286d8edStholo   free (name);
250*2286d8edStholo 
251*2286d8edStholo   /* A special header is needed at the beginning of context output.  */
252*2286d8edStholo   switch (output_style)
253*2286d8edStholo     {
254*2286d8edStholo     case OUTPUT_CONTEXT:
255*2286d8edStholo       print_context_header (files, 0);
256*2286d8edStholo       break;
257*2286d8edStholo 
258*2286d8edStholo     case OUTPUT_UNIFIED:
259*2286d8edStholo       print_context_header (files, 1);
260*2286d8edStholo       break;
261*2286d8edStholo 
262*2286d8edStholo     default:
263*2286d8edStholo       break;
264*2286d8edStholo     }
265*2286d8edStholo }
266*2286d8edStholo 
267*2286d8edStholo /* Call after the end of output of diffs for one file.
268*2286d8edStholo    If -l was given, close OUTFILE and get rid of the `pr' subfork.  */
269*2286d8edStholo 
270*2286d8edStholo void
271*2286d8edStholo finish_output ()
272*2286d8edStholo {
273*2286d8edStholo   if (paginate_flag && outfile != 0 && outfile != stdout)
274*2286d8edStholo     {
275*2286d8edStholo #ifdef PR_PROGRAM
276*2286d8edStholo       int wstatus;
277*2286d8edStholo       if (ferror (outfile))
278*2286d8edStholo 	fatal ("write error");
279*2286d8edStholo # if ! HAVE_FORK
280*2286d8edStholo       wstatus = pclose (outfile);
281*2286d8edStholo # else /* HAVE_FORK */
282*2286d8edStholo       if (fclose (outfile) != 0)
283*2286d8edStholo 	pfatal_with_name ("write error");
284*2286d8edStholo       if (waitpid (pr_pid, &wstatus, 0) < 0)
285*2286d8edStholo 	pfatal_with_name ("waitpid");
286*2286d8edStholo # endif /* HAVE_FORK */
287*2286d8edStholo       if (wstatus != 0)
288*2286d8edStholo 	fatal ("subsidiary pr failed");
289*2286d8edStholo #else
290*2286d8edStholo       fatal ("internal error in finish_output");
291*2286d8edStholo #endif
292*2286d8edStholo     }
293*2286d8edStholo 
294*2286d8edStholo   output_in_progress = 0;
295*2286d8edStholo }
296*2286d8edStholo 
297*2286d8edStholo /* Compare two lines (typically one from each input file)
298*2286d8edStholo    according to the command line options.
299*2286d8edStholo    For efficiency, this is invoked only when the lines do not match exactly
300*2286d8edStholo    but an option like -i might cause us to ignore the difference.
301*2286d8edStholo    Return nonzero if the lines differ.  */
302*2286d8edStholo 
303*2286d8edStholo int
304*2286d8edStholo line_cmp (s1, s2)
305*2286d8edStholo      char const *s1, *s2;
306*2286d8edStholo {
307*2286d8edStholo   register unsigned char const *t1 = (unsigned char const *) s1;
308*2286d8edStholo   register unsigned char const *t2 = (unsigned char const *) s2;
309*2286d8edStholo 
310*2286d8edStholo   while (1)
311*2286d8edStholo     {
312*2286d8edStholo       register unsigned char c1 = *t1++;
313*2286d8edStholo       register unsigned char c2 = *t2++;
314*2286d8edStholo 
315*2286d8edStholo       /* Test for exact char equality first, since it's a common case.  */
316*2286d8edStholo       if (c1 != c2)
317*2286d8edStholo 	{
318*2286d8edStholo 	  /* Ignore horizontal white space if -b or -w is specified.  */
319*2286d8edStholo 
320*2286d8edStholo 	  if (ignore_all_space_flag)
321*2286d8edStholo 	    {
322*2286d8edStholo 	      /* For -w, just skip past any white space.  */
323*2286d8edStholo 	      while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
324*2286d8edStholo 	      while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
325*2286d8edStholo 	    }
326*2286d8edStholo 	  else if (ignore_space_change_flag)
327*2286d8edStholo 	    {
328*2286d8edStholo 	      /* For -b, advance past any sequence of white space in line 1
329*2286d8edStholo 		 and consider it just one Space, or nothing at all
330*2286d8edStholo 		 if it is at the end of the line.  */
331*2286d8edStholo 	      if (ISSPACE (c1))
332*2286d8edStholo 		{
333*2286d8edStholo 		  while (c1 != '\n')
334*2286d8edStholo 		    {
335*2286d8edStholo 		      c1 = *t1++;
336*2286d8edStholo 		      if (! ISSPACE (c1))
337*2286d8edStholo 			{
338*2286d8edStholo 			  --t1;
339*2286d8edStholo 			  c1 = ' ';
340*2286d8edStholo 			  break;
341*2286d8edStholo 			}
342*2286d8edStholo 		    }
343*2286d8edStholo 		}
344*2286d8edStholo 
345*2286d8edStholo 	      /* Likewise for line 2.  */
346*2286d8edStholo 	      if (ISSPACE (c2))
347*2286d8edStholo 		{
348*2286d8edStholo 		  while (c2 != '\n')
349*2286d8edStholo 		    {
350*2286d8edStholo 		      c2 = *t2++;
351*2286d8edStholo 		      if (! ISSPACE (c2))
352*2286d8edStholo 			{
353*2286d8edStholo 			  --t2;
354*2286d8edStholo 			  c2 = ' ';
355*2286d8edStholo 			  break;
356*2286d8edStholo 			}
357*2286d8edStholo 		    }
358*2286d8edStholo 		}
359*2286d8edStholo 
360*2286d8edStholo 	      if (c1 != c2)
361*2286d8edStholo 		{
362*2286d8edStholo 		  /* If we went too far when doing the simple test
363*2286d8edStholo 		     for equality, go back to the first non-white-space
364*2286d8edStholo 		     character in both sides and try again.  */
365*2286d8edStholo 		  if (c2 == ' ' && c1 != '\n'
366*2286d8edStholo 		      && (unsigned char const *) s1 + 1 < t1
367*2286d8edStholo 		      && ISSPACE(t1[-2]))
368*2286d8edStholo 		    {
369*2286d8edStholo 		      --t1;
370*2286d8edStholo 		      continue;
371*2286d8edStholo 		    }
372*2286d8edStholo 		  if (c1 == ' ' && c2 != '\n'
373*2286d8edStholo 		      && (unsigned char const *) s2 + 1 < t2
374*2286d8edStholo 		      && ISSPACE(t2[-2]))
375*2286d8edStholo 		    {
376*2286d8edStholo 		      --t2;
377*2286d8edStholo 		      continue;
378*2286d8edStholo 		    }
379*2286d8edStholo 		}
380*2286d8edStholo 	    }
381*2286d8edStholo 
382*2286d8edStholo 	  /* Lowercase all letters if -i is specified.  */
383*2286d8edStholo 
384*2286d8edStholo 	  if (ignore_case_flag)
385*2286d8edStholo 	    {
386*2286d8edStholo 	      if (ISUPPER (c1))
387*2286d8edStholo 		c1 = tolower (c1);
388*2286d8edStholo 	      if (ISUPPER (c2))
389*2286d8edStholo 		c2 = tolower (c2);
390*2286d8edStholo 	    }
391*2286d8edStholo 
392*2286d8edStholo 	  if (c1 != c2)
393*2286d8edStholo 	    break;
394*2286d8edStholo 	}
395*2286d8edStholo       if (c1 == '\n')
396*2286d8edStholo 	return 0;
397*2286d8edStholo     }
398*2286d8edStholo 
399*2286d8edStholo   return (1);
400*2286d8edStholo }
401*2286d8edStholo 
402*2286d8edStholo /* Find the consecutive changes at the start of the script START.
403*2286d8edStholo    Return the last link before the first gap.  */
404*2286d8edStholo 
405*2286d8edStholo struct change *
406*2286d8edStholo find_change (start)
407*2286d8edStholo      struct change *start;
408*2286d8edStholo {
409*2286d8edStholo   return start;
410*2286d8edStholo }
411*2286d8edStholo 
412*2286d8edStholo struct change *
413*2286d8edStholo find_reverse_change (start)
414*2286d8edStholo      struct change *start;
415*2286d8edStholo {
416*2286d8edStholo   return start;
417*2286d8edStholo }
418*2286d8edStholo 
419*2286d8edStholo /* Divide SCRIPT into pieces by calling HUNKFUN and
420*2286d8edStholo    print each piece with PRINTFUN.
421*2286d8edStholo    Both functions take one arg, an edit script.
422*2286d8edStholo 
423*2286d8edStholo    HUNKFUN is called with the tail of the script
424*2286d8edStholo    and returns the last link that belongs together with the start
425*2286d8edStholo    of the tail.
426*2286d8edStholo 
427*2286d8edStholo    PRINTFUN takes a subscript which belongs together (with a null
428*2286d8edStholo    link at the end) and prints it.  */
429*2286d8edStholo 
430*2286d8edStholo void
431*2286d8edStholo print_script (script, hunkfun, printfun)
432*2286d8edStholo      struct change *script;
433*2286d8edStholo      struct change * (*hunkfun) PARAMS((struct change *));
434*2286d8edStholo      void (*printfun) PARAMS((struct change *));
435*2286d8edStholo {
436*2286d8edStholo   struct change *next = script;
437*2286d8edStholo 
438*2286d8edStholo   while (next)
439*2286d8edStholo     {
440*2286d8edStholo       struct change *this, *end;
441*2286d8edStholo 
442*2286d8edStholo       /* Find a set of changes that belong together.  */
443*2286d8edStholo       this = next;
444*2286d8edStholo       end = (*hunkfun) (next);
445*2286d8edStholo 
446*2286d8edStholo       /* Disconnect them from the rest of the changes,
447*2286d8edStholo 	 making them a hunk, and remember the rest for next iteration.  */
448*2286d8edStholo       next = end->link;
449*2286d8edStholo       end->link = 0;
450*2286d8edStholo #ifdef DEBUG
451*2286d8edStholo       debug_script (this);
452*2286d8edStholo #endif
453*2286d8edStholo 
454*2286d8edStholo       /* Print this hunk.  */
455*2286d8edStholo       (*printfun) (this);
456*2286d8edStholo 
457*2286d8edStholo       /* Reconnect the script so it will all be freed properly.  */
458*2286d8edStholo       end->link = next;
459*2286d8edStholo     }
460*2286d8edStholo }
461*2286d8edStholo 
462*2286d8edStholo /* Print the text of a single line LINE,
463*2286d8edStholo    flagging it with the characters in LINE_FLAG (which say whether
464*2286d8edStholo    the line is inserted, deleted, changed, etc.).  */
465*2286d8edStholo 
466*2286d8edStholo void
467*2286d8edStholo print_1_line (line_flag, line)
468*2286d8edStholo      char const *line_flag;
469*2286d8edStholo      char const * const *line;
470*2286d8edStholo {
471*2286d8edStholo   char const *text = line[0], *limit = line[1]; /* Help the compiler.  */
472*2286d8edStholo   FILE *out = outfile; /* Help the compiler some more.  */
473*2286d8edStholo   char const *flag_format = 0;
474*2286d8edStholo 
475*2286d8edStholo   /* If -T was specified, use a Tab between the line-flag and the text.
476*2286d8edStholo      Otherwise use a Space (as Unix diff does).
477*2286d8edStholo      Print neither space nor tab if line-flags are empty.  */
478*2286d8edStholo 
479*2286d8edStholo   if (line_flag && *line_flag)
480*2286d8edStholo     {
481*2286d8edStholo       flag_format = tab_align_flag ? "%s\t" : "%s ";
482*2286d8edStholo       fprintf (out, flag_format, line_flag);
483*2286d8edStholo     }
484*2286d8edStholo 
485*2286d8edStholo   output_1_line (text, limit, flag_format, line_flag);
486*2286d8edStholo 
487*2286d8edStholo   if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
488*2286d8edStholo     fprintf (out, "\n\\ No newline at end of file\n");
489*2286d8edStholo }
490*2286d8edStholo 
491*2286d8edStholo /* Output a line from TEXT up to LIMIT.  Without -t, output verbatim.
492*2286d8edStholo    With -t, expand white space characters to spaces, and if FLAG_FORMAT
493*2286d8edStholo    is nonzero, output it with argument LINE_FLAG after every
494*2286d8edStholo    internal carriage return, so that tab stops continue to line up.  */
495*2286d8edStholo 
496*2286d8edStholo void
497*2286d8edStholo output_1_line (text, limit, flag_format, line_flag)
498*2286d8edStholo      char const *text, *limit, *flag_format, *line_flag;
499*2286d8edStholo {
500*2286d8edStholo   if (!tab_expand_flag)
501*2286d8edStholo     fwrite (text, sizeof (char), limit - text, outfile);
502*2286d8edStholo   else
503*2286d8edStholo     {
504*2286d8edStholo       register FILE *out = outfile;
505*2286d8edStholo       register unsigned char c;
506*2286d8edStholo       register char const *t = text;
507*2286d8edStholo       register unsigned column = 0;
508*2286d8edStholo 
509*2286d8edStholo       while (t < limit)
510*2286d8edStholo 	switch ((c = *t++))
511*2286d8edStholo 	  {
512*2286d8edStholo 	  case '\t':
513*2286d8edStholo 	    {
514*2286d8edStholo 	      unsigned spaces = TAB_WIDTH - column % TAB_WIDTH;
515*2286d8edStholo 	      column += spaces;
516*2286d8edStholo 	      do
517*2286d8edStholo 		putc (' ', out);
518*2286d8edStholo 	      while (--spaces);
519*2286d8edStholo 	    }
520*2286d8edStholo 	    break;
521*2286d8edStholo 
522*2286d8edStholo 	  case '\r':
523*2286d8edStholo 	    putc (c, out);
524*2286d8edStholo 	    if (flag_format && t < limit && *t != '\n')
525*2286d8edStholo 	      fprintf (out, flag_format, line_flag);
526*2286d8edStholo 	    column = 0;
527*2286d8edStholo 	    break;
528*2286d8edStholo 
529*2286d8edStholo 	  case '\b':
530*2286d8edStholo 	    if (column == 0)
531*2286d8edStholo 	      continue;
532*2286d8edStholo 	    column--;
533*2286d8edStholo 	    putc (c, out);
534*2286d8edStholo 	    break;
535*2286d8edStholo 
536*2286d8edStholo 	  default:
537*2286d8edStholo 	    if (ISPRINT (c))
538*2286d8edStholo 	      column++;
539*2286d8edStholo 	    putc (c, out);
540*2286d8edStholo 	    break;
541*2286d8edStholo 	  }
542*2286d8edStholo     }
543*2286d8edStholo }
544*2286d8edStholo 
545*2286d8edStholo int
546*2286d8edStholo change_letter (inserts, deletes)
547*2286d8edStholo      int inserts, deletes;
548*2286d8edStholo {
549*2286d8edStholo   if (!inserts)
550*2286d8edStholo     return 'd';
551*2286d8edStholo   else if (!deletes)
552*2286d8edStholo     return 'a';
553*2286d8edStholo   else
554*2286d8edStholo     return 'c';
555*2286d8edStholo }
556*2286d8edStholo 
557*2286d8edStholo /* Translate an internal line number (an index into diff's table of lines)
558*2286d8edStholo    into an actual line number in the input file.
559*2286d8edStholo    The internal line number is LNUM.  FILE points to the data on the file.
560*2286d8edStholo 
561*2286d8edStholo    Internal line numbers count from 0 starting after the prefix.
562*2286d8edStholo    Actual line numbers count from 1 within the entire file.  */
563*2286d8edStholo 
564*2286d8edStholo int
565*2286d8edStholo translate_line_number (file, lnum)
566*2286d8edStholo      struct file_data const *file;
567*2286d8edStholo      int lnum;
568*2286d8edStholo {
569*2286d8edStholo   return lnum + file->prefix_lines + 1;
570*2286d8edStholo }
571*2286d8edStholo 
572*2286d8edStholo void
573*2286d8edStholo translate_range (file, a, b, aptr, bptr)
574*2286d8edStholo      struct file_data const *file;
575*2286d8edStholo      int a, b;
576*2286d8edStholo      int *aptr, *bptr;
577*2286d8edStholo {
578*2286d8edStholo   *aptr = translate_line_number (file, a - 1) + 1;
579*2286d8edStholo   *bptr = translate_line_number (file, b + 1) - 1;
580*2286d8edStholo }
581*2286d8edStholo 
582*2286d8edStholo /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
583*2286d8edStholo    If the two numbers are identical, print just one number.
584*2286d8edStholo 
585*2286d8edStholo    Args A and B are internal line numbers.
586*2286d8edStholo    We print the translated (real) line numbers.  */
587*2286d8edStholo 
588*2286d8edStholo void
589*2286d8edStholo print_number_range (sepchar, file, a, b)
590*2286d8edStholo      int sepchar;
591*2286d8edStholo      struct file_data *file;
592*2286d8edStholo      int a, b;
593*2286d8edStholo {
594*2286d8edStholo   int trans_a, trans_b;
595*2286d8edStholo   translate_range (file, a, b, &trans_a, &trans_b);
596*2286d8edStholo 
597*2286d8edStholo   /* Note: we can have B < A in the case of a range of no lines.
598*2286d8edStholo      In this case, we should print the line number before the range,
599*2286d8edStholo      which is B.  */
600*2286d8edStholo   if (trans_b > trans_a)
601*2286d8edStholo     fprintf (outfile, "%d%c%d", trans_a, sepchar, trans_b);
602*2286d8edStholo   else
603*2286d8edStholo     fprintf (outfile, "%d", trans_b);
604*2286d8edStholo }
605*2286d8edStholo 
606*2286d8edStholo /* Look at a hunk of edit script and report the range of lines in each file
607*2286d8edStholo    that it applies to.  HUNK is the start of the hunk, which is a chain
608*2286d8edStholo    of `struct change'.  The first and last line numbers of file 0 are stored in
609*2286d8edStholo    *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
610*2286d8edStholo    Note that these are internal line numbers that count from 0.
611*2286d8edStholo 
612*2286d8edStholo    If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
613*2286d8edStholo 
614*2286d8edStholo    Also set *DELETES nonzero if any lines of file 0 are deleted
615*2286d8edStholo    and set *INSERTS nonzero if any lines of file 1 are inserted.
616*2286d8edStholo    If only ignorable lines are inserted or deleted, both are
617*2286d8edStholo    set to 0.  */
618*2286d8edStholo 
619*2286d8edStholo void
620*2286d8edStholo analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
621*2286d8edStholo      struct change *hunk;
622*2286d8edStholo      int *first0, *last0, *first1, *last1;
623*2286d8edStholo      int *deletes, *inserts;
624*2286d8edStholo {
625*2286d8edStholo   int l0, l1, show_from, show_to;
626*2286d8edStholo   int i;
627*2286d8edStholo   int trivial = ignore_blank_lines_flag || ignore_regexp_list;
628*2286d8edStholo   struct change *next;
629*2286d8edStholo 
630*2286d8edStholo   show_from = show_to = 0;
631*2286d8edStholo 
632*2286d8edStholo   *first0 = hunk->line0;
633*2286d8edStholo   *first1 = hunk->line1;
634*2286d8edStholo 
635*2286d8edStholo   next = hunk;
636*2286d8edStholo   do
637*2286d8edStholo     {
638*2286d8edStholo       l0 = next->line0 + next->deleted - 1;
639*2286d8edStholo       l1 = next->line1 + next->inserted - 1;
640*2286d8edStholo       show_from += next->deleted;
641*2286d8edStholo       show_to += next->inserted;
642*2286d8edStholo 
643*2286d8edStholo       for (i = next->line0; i <= l0 && trivial; i++)
644*2286d8edStholo 	if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n')
645*2286d8edStholo 	  {
646*2286d8edStholo 	    struct regexp_list *r;
647*2286d8edStholo 	    char const *line = files[0].linbuf[i];
648*2286d8edStholo 	    int len = files[0].linbuf[i + 1] - line;
649*2286d8edStholo 
650*2286d8edStholo 	    for (r = ignore_regexp_list; r; r = r->next)
651*2286d8edStholo 	      if (0 <= re_search (&r->buf, line, len, 0, len, 0))
652*2286d8edStholo 		break;	/* Found a match.  Ignore this line.  */
653*2286d8edStholo 	    /* If we got all the way through the regexp list without
654*2286d8edStholo 	       finding a match, then it's nontrivial.  */
655*2286d8edStholo 	    if (!r)
656*2286d8edStholo 	      trivial = 0;
657*2286d8edStholo 	  }
658*2286d8edStholo 
659*2286d8edStholo       for (i = next->line1; i <= l1 && trivial; i++)
660*2286d8edStholo 	if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n')
661*2286d8edStholo 	  {
662*2286d8edStholo 	    struct regexp_list *r;
663*2286d8edStholo 	    char const *line = files[1].linbuf[i];
664*2286d8edStholo 	    int len = files[1].linbuf[i + 1] - line;
665*2286d8edStholo 
666*2286d8edStholo 	    for (r = ignore_regexp_list; r; r = r->next)
667*2286d8edStholo 	      if (0 <= re_search (&r->buf, line, len, 0, len, 0))
668*2286d8edStholo 		break;	/* Found a match.  Ignore this line.  */
669*2286d8edStholo 	    /* If we got all the way through the regexp list without
670*2286d8edStholo 	       finding a match, then it's nontrivial.  */
671*2286d8edStholo 	    if (!r)
672*2286d8edStholo 	      trivial = 0;
673*2286d8edStholo 	  }
674*2286d8edStholo     }
675*2286d8edStholo   while ((next = next->link) != 0);
676*2286d8edStholo 
677*2286d8edStholo   *last0 = l0;
678*2286d8edStholo   *last1 = l1;
679*2286d8edStholo 
680*2286d8edStholo   /* If all inserted or deleted lines are ignorable,
681*2286d8edStholo      tell the caller to ignore this hunk.  */
682*2286d8edStholo 
683*2286d8edStholo   if (trivial)
684*2286d8edStholo     show_from = show_to = 0;
685*2286d8edStholo 
686*2286d8edStholo   *deletes = show_from;
687*2286d8edStholo   *inserts = show_to;
688*2286d8edStholo }
689*2286d8edStholo 
690*2286d8edStholo /* Concatenate three strings, returning a newly malloc'd string.  */
691*2286d8edStholo 
692*2286d8edStholo char *
693*2286d8edStholo concat (s1, s2, s3)
694*2286d8edStholo      char const *s1, *s2, *s3;
695*2286d8edStholo {
696*2286d8edStholo   size_t len = strlen (s1) + strlen (s2) + strlen (s3);
697*2286d8edStholo   char *new = xmalloc (len + 1);
698*2286d8edStholo   sprintf (new, "%s%s%s", s1, s2, s3);
699*2286d8edStholo   return new;
700*2286d8edStholo }
701*2286d8edStholo 
702*2286d8edStholo /* Yield the newly malloc'd pathname
703*2286d8edStholo    of the file in DIR whose filename is FILE.  */
704*2286d8edStholo 
705*2286d8edStholo char *
706*2286d8edStholo dir_file_pathname (dir, file)
707*2286d8edStholo      char const *dir, *file;
708*2286d8edStholo {
709*2286d8edStholo   char const *p = filename_lastdirchar (dir);
710*2286d8edStholo   return concat (dir, "/" + (p && !p[1]), file);
711*2286d8edStholo }
712*2286d8edStholo 
713*2286d8edStholo void
714*2286d8edStholo debug_script (sp)
715*2286d8edStholo      struct change *sp;
716*2286d8edStholo {
717*2286d8edStholo   fflush (stdout);
718*2286d8edStholo   for (; sp; sp = sp->link)
719*2286d8edStholo     fprintf (stderr, "%3d %3d delete %d insert %d\n",
720*2286d8edStholo 	     sp->line0, sp->line1, sp->deleted, sp->inserted);
721*2286d8edStholo   fflush (stderr);
722*2286d8edStholo }
723