source: trunk/third/diffutils/util.c @ 16149

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