source: trunk/essentials/sys-apps/diffutils/src/util.c

Last change on this file was 3047, checked in by bird, 18 years ago

search the path on OS/2.

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