1 /* Support routines for GNU DIFF.
2 Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
4 This file is part of GNU DIFF.
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)
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.
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. */
23 #define PR_PROGRAM "/bin/pr"
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'. */
39 /* Head of the chain of queues messages. */
41 static struct msg *msg_chain;
43 /* Tail of the chain of queues messages. */
45 static struct msg **msg_chain_end = &msg_chain;
47 /* Use when a system call returns non-zero status.
48 TEXT should normally be the file name. */
51 perror_with_name (text)
55 fprintf (stderr, "%s: ", program_name);
60 /* Use when a system call returns non-zero status and that is fatal. */
63 pfatal_with_name (text)
67 print_message_queue ();
68 fprintf (stderr, "%s: ", program_name);
74 /* Print an error message from the format-string FORMAT
75 with args ARG1 and ARG2. */
78 error (format, arg, arg1)
79 char const *format, *arg, *arg1;
81 fprintf (stderr, "%s: ", program_name);
82 fprintf (stderr, format, arg, arg1);
83 fprintf (stderr, "\n");
86 /* Print an error message containing the string TEXT, then exit. */
92 print_message_queue ();
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 ...". */
101 message (format, arg1, arg2)
102 char const *format, *arg1, *arg2;
104 message5 (format, arg1, arg2, 0, 0);
108 message5 (format, arg1, arg2, arg3, arg4)
109 char const *format, *arg1, *arg2, *arg3, *arg4;
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;
120 *msg_chain_end = new;
121 msg_chain_end = &new->next;
125 if (sdiff_help_sdiff)
127 printf (format, arg1, arg2, arg3, arg4);
131 /* Output all the messages that were saved up by calls to `message'. */
134 print_message_queue ()
138 for (m = msg_chain; m; m = m->next)
139 printf (m->format, m->arg1, m->arg2, m->arg3, m->arg4);
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.
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. */
149 static char const *current_name0;
150 static char const *current_name1;
151 static int current_depth;
154 setup_output (name0, name1, depth)
155 char const *name0, *name1;
158 current_name0 = name0;
159 current_name1 = name1;
160 current_depth = depth;
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);
187 /* Make OUTFILE a pipe to a subsidiary `pr'. */
192 if (pipe (pipes) != 0)
193 pfatal_with_name ("pipe");
199 pfatal_with_name ("vfork");
204 if (pipes[0] != STDIN_FILENO)
206 if (dup2 (pipes[0], STDIN_FILENO) < 0)
207 pfatal_with_name ("dup2");
211 execl (PR_PROGRAM, PR_PROGRAM, "-F", "-h", name, 0);
213 execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, 0);
215 pfatal_with_name (PR_PROGRAM);
220 outfile = fdopen (pipes[1], "w");
222 pfatal_with_name ("fdopen");
224 #else /* ! HAVE_FORK */
225 char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10);
227 char const *a = name;
228 sprintf (command, "%s -f -h ", PR_PROGRAM);
229 p = command + strlen (command);
230 SYSTEM_QUOTE_ARG (p, a);
232 outfile = popen (command, "w");
234 pfatal_with_name (command);
236 #endif /* ! HAVE_FORK */
241 /* If -l was not specified, output the diff straight to `stdout'. */
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);
253 /* A special header is needed at the beginning of context output. */
254 switch (output_style)
257 print_context_header (files, 0);
261 print_context_header (files, 1);
269 /* Call after the end of output of diffs for one file.
270 Close OUTFILE and get rid of the `pr' subfork. */
275 if (outfile != 0 && outfile != stdout)
278 if (ferror (outfile))
279 fatal ("write error");
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 */
289 fatal ("subsidiary pr failed");
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. */
305 register unsigned char const *t1 = (unsigned char const *) s1;
306 register unsigned char const *t2 = (unsigned char const *) s2;
310 register unsigned char c1 = *t1++;
311 register unsigned char c2 = *t2++;
313 /* Test for exact char equality first, since it's a common case. */
316 /* Ignore horizontal white space if -b or -w is specified. */
318 if (ignore_all_space_flag)
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++;
324 else if (ignore_space_change_flag)
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. */
343 /* Likewise for line 2. */
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
370 if (c1 == ' ' && c2 != '\n'
371 && (unsigned char const *) s2 + 1 < t2
380 /* Lowercase all letters if -i is specified. */
382 if (ignore_case_flag)
400 /* Find the consecutive changes at the start of the script START.
401 Return the last link before the first gap. */
405 struct change *start;
411 find_reverse_change (start)
412 struct change *start;
417 /* Divide SCRIPT into pieces by calling HUNKFUN and
418 print each piece with PRINTFUN.
419 Both functions take one arg, an edit script.
421 HUNKFUN is called with the tail of the script
422 and returns the last link that belongs together with the start
425 PRINTFUN takes a subscript which belongs together (with a null
426 link at the end) and prints it. */
429 print_script (script, hunkfun, printfun)
430 struct change *script;
431 struct change * (*hunkfun) PARAMS((struct change *));
432 void (*printfun) PARAMS((struct change *));
434 struct change *next = script;
438 struct change *this, *end;
440 /* Find a set of changes that belong together. */
442 end = (*hunkfun) (next);
444 /* Disconnect them from the rest of the changes,
445 making them a hunk, and remember the rest for next iteration. */
452 /* Print this hunk. */
455 /* Reconnect the script so it will all be freed properly. */
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.). */
465 print_1_line (line_flag, line)
466 char const *line_flag;
467 char const * const *line;
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;
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. */
477 if (line_flag && *line_flag)
479 flag_format = tab_align_flag ? "%s\t" : "%s ";
480 fprintf (out, flag_format, line_flag);
483 output_1_line (text, limit, flag_format, line_flag);
485 if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
486 fprintf (out, "\n\\ No newline at end of file\n");
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. */
495 output_1_line (text, limit, flag_format, line_flag)
496 char const *text, *limit, *flag_format, *line_flag;
498 if (!tab_expand_flag)
499 fwrite (text, sizeof (char), limit - text, outfile);
502 register FILE *out = outfile;
503 register unsigned char c;
504 register char const *t = text;
505 register unsigned column = 0;
512 unsigned spaces = TAB_WIDTH - column % TAB_WIDTH;
522 if (flag_format && t < limit && *t != '\n')
523 fprintf (out, flag_format, line_flag);
544 change_letter (inserts, deletes)
545 int inserts, deletes;
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.
559 Internal line numbers count from 0 starting after the prefix.
560 Actual line numbers count from 1 within the entire file. */
563 translate_line_number (file, lnum)
564 struct file_data const *file;
567 return lnum + file->prefix_lines + 1;
571 translate_range (file, a, b, aptr, bptr)
572 struct file_data const *file;
576 *aptr = translate_line_number (file, a - 1) + 1;
577 *bptr = translate_line_number (file, b + 1) - 1;
580 /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
581 If the two numbers are identical, print just one number.
583 Args A and B are internal line numbers.
584 We print the translated (real) line numbers. */
587 print_number_range (sepchar, file, a, b)
589 struct file_data *file;
592 int trans_a, trans_b;
593 translate_range (file, a, b, &trans_a, &trans_b);
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,
598 if (trans_b > trans_a)
599 fprintf (outfile, "%d%c%d", trans_a, sepchar, trans_b);
601 fprintf (outfile, "%d", trans_b);
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.
610 If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
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
618 analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
620 int *first0, *last0, *first1, *last1;
621 int *deletes, *inserts;
623 int l0, l1, show_from, show_to;
625 int trivial = ignore_blank_lines_flag || ignore_regexp_list;
628 show_from = show_to = 0;
630 *first0 = hunk->line0;
631 *first1 = hunk->line1;
636 l0 = next->line0 + next->deleted - 1;
637 l1 = next->line1 + next->inserted - 1;
638 show_from += next->deleted;
639 show_to += next->inserted;
641 for (i = next->line0; i <= l0 && trivial; i++)
642 if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n')
644 struct regexp_list *r;
645 char const *line = files[0].linbuf[i];
646 int len = files[0].linbuf[i + 1] - line;
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. */
657 for (i = next->line1; i <= l1 && trivial; i++)
658 if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n')
660 struct regexp_list *r;
661 char const *line = files[1].linbuf[i];
662 int len = files[1].linbuf[i + 1] - line;
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. */
673 while ((next = next->link) != 0);
678 /* If all inserted or deleted lines are ignorable,
679 tell the caller to ignore this hunk. */
682 show_from = show_to = 0;
684 *deletes = show_from;
688 /* malloc a block of memory, with fatal error message if we can't do it. */
694 register VOID *value;
699 value = (VOID *) malloc (size);
702 fatal ("memory exhausted");
706 /* realloc a block of memory, with fatal error message if we can't do it. */
713 register VOID *value;
718 value = (VOID *) realloc (old, size);
721 fatal ("memory exhausted");
725 /* Concatenate three strings, returning a newly malloc'd string. */
729 char const *s1, *s2, *s3;
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);
737 /* Yield the newly malloc'd pathname
738 of the file in DIR whose filename is FILE. */
741 dir_file_pathname (dir, file)
742 char const *dir, *file;
744 char const *p = filename_lastdirchar (dir);
745 return concat (dir, "/" + (p && !p[1]), file);
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);