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