xref: /plan9/sys/src/ape/cmd/diff/util.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
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