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