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