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