386BSD 0.1 development
[unix-history] / usr / src / usr.bin / diff / util.c
CommitLineData
e3754cda
WJ
1/* Support routines for GNU DIFF.
2 Copyright (C) 1988, 1989 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 1, 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/* Use when a system call returns non-zero status.
23 TEXT should normally be the file name. */
24
25void
26perror_with_name (text)
27 char *text;
28{
29 fprintf (stderr, "%s: ", program);
30 perror (text);
31}
32
33/* Use when a system call returns non-zero status and that is fatal. */
34
35void
36pfatal_with_name (text)
37 char *text;
38{
39 print_message_queue ();
40 fprintf (stderr, "%s: ", program);
41 perror (text);
42 exit (2);
43}
44
45/* Print an error message from the format-string FORMAT
46 with args ARG1 and ARG2. */
47
48void
49error (format, arg, arg1)
50 char *format;
51 char *arg;
52 char *arg1;
53{
54 fprintf (stderr, "%s: ", program);
55 fprintf (stderr, format, arg, arg1);
56 fprintf (stderr, "\n");
57}
58
59/* Print an error message containing the string TEXT, then exit. */
60
61void
62fatal (message)
63 char *message;
64{
65 print_message_queue ();
66 error (message, "");
67 exit (2);
68}
69\f
70/* Like printf, except if -l in effect then save the message and print later.
71 This is used for things like "binary files differ" and "Only in ...". */
72
73void
74message (format, arg1, arg2)
75 char *format, *arg1, *arg2;
76{
77 if (paginate_flag)
78 {
79 struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
80 if (msg_chain_end == 0)
81 msg_chain = msg_chain_end = new;
82 else
83 {
84 msg_chain_end->next = new;
85 msg_chain_end = new;
86 }
87 new->format = format;
88 new->arg1 = concat (arg1, "", "");
89 new->arg2 = concat (arg2, "", "");
90 new->next = 0;
91 }
92 else
93 printf (format, arg1, arg2);
94}
95
96/* Output all the messages that were saved up by calls to `message'. */
97
98void
99print_message_queue ()
100{
101 struct msg *m;
102
103 for (m = msg_chain; m; m = m->next)
104 printf (m->format, m->arg1, m->arg2);
105}
106\f
107/* Call before outputting the results of comparing files NAME0 and NAME1
108 to set up OUTFILE, the stdio stream for the output to go to.
109
110 Usually, OUTFILE is just stdout. But when -l was specified
111 we fork off a `pr' and make OUTFILE a pipe to it.
112 `pr' then outputs to our stdout. */
113
114void
115setup_output (name0, name1, depth)
116 char *name0, *name1;
117 int depth;
118{
119 char *name;
120
121 /* Construct the header of this piece of diff. */
122 name = (char *) xmalloc (strlen (name0) + strlen (name1)
123 + strlen (switch_string) + 15);
124
125 strcpy (name, "diff");
126 strcat (name, switch_string);
127 strcat (name, " ");
128 strcat (name, name0);
129 strcat (name, " ");
130 strcat (name, name1);
131
132 if (paginate_flag)
133 {
134 int pipes[2];
135 int desc;
136
137 /* For a `pr' and make OUTFILE a pipe to it. */
138 if (pipe (pipes) < 0)
139 pfatal_with_name ("pipe");
140
141 fflush (stdout);
142
143 desc = vfork ();
144 if (desc < 0)
145 pfatal_with_name ("vfork");
146
147 if (desc == 0)
148 {
149 close (pipes[1]);
150 if (pipes[0] != fileno (stdin))
151 {
152 if (dup2 (pipes[0], fileno (stdin)) < 0)
153 pfatal_with_name ("dup2");
154 close (pipes[0]);
155 }
156
157 if (execl (PR_FILE_NAME, PR_FILE_NAME, "-f", "-h", name, 0) < 0)
158 pfatal_with_name (PR_FILE_NAME);
159 }
160 else
161 {
162 close (pipes[0]);
163 outfile = fdopen (pipes[1], "w");
164 }
165 }
166 else
167 {
168
169 /* If -l was not specified, output the diff straight to `stdout'. */
170
171 outfile = stdout;
172
173 /* If handling multiple files (because scanning a directory),
174 print which files the following output is about. */
175 if (depth > 0)
176 printf ("%s\n", name);
177 }
178
179 free (name);
180}
181
182/* Call after the end of output of diffs for one file.
183 Close OUTFILE and get rid of the `pr' subfork. */
184
185void
186finish_output ()
187{
188 if (outfile != stdout)
189 {
190 fclose (outfile);
191 wait (0);
192 }
193}
194\f
195/* Compare two lines (typically one from each input file)
196 according to the command line options.
197 Each line is described by a `struct line_def'.
198 Return 1 if the lines differ, like `bcmp'. */
199
200int
201line_cmp (s1, s2)
202 struct line_def *s1, *s2;
203{
204 register char *t1, *t2;
205 register char end_char = line_end_char;
206 int savechar;
207
208 /* Check first for exact identity.
209 If that is true, return 0 immediately.
210 This detects the common case of exact identity
211 faster than complete comparison would. */
212
213 t1 = s1->text;
214 t2 = s2->text;
215
216 /* Alter the character following line 2 so it doesn't
217 match that following line 1.
218 (We used to alter the character after line 1,
219 but that caused trouble if line 2 directly follows line 1.) */
220 savechar = s2->text[s2->length];
221 s2->text[s2->length] = s1->text[s1->length] + 1;
222
223 /* Now find the first mismatch; this won't go past the
224 character we just changed. */
225 while (*t1++ == *t2++);
226
227 /* Undo the alteration. */
228 s2->text[s2->length] = savechar;
229
230 /* If the comparison stopped at the alteration,
231 the two lines are identical. */
232 if (t2 == s2->text + s2->length + 1)
233 return 0;
234
235 /* Not exactly identical, but perhaps they match anyway
236 when case or whitespace is ignored. */
237
238 if (ignore_case_flag || ignore_space_change_flag || ignore_all_space_flag)
239 {
240 t1 = s1->text;
241 t2 = s2->text;
242
243 while (1)
244 {
245 register char c1 = *t1++;
246 register char c2 = *t2++;
247
248 /* Ignore horizontal whitespace if -b or -w is specified. */
249
250 if (ignore_all_space_flag)
251 {
252 /* For -w, just skip past any spaces or tabs. */
253 while (c1 == ' ' || c1 == '\t') c1 = *t1++;
254 while (c2 == ' ' || c2 == '\t') c2 = *t2++;
255 }
256 else if (ignore_space_change_flag)
257 {
258 /* For -b, advance past any sequence of whitespace in line 1
259 and consider it just one Space, or nothing at all
260 if it is at the end of the line. */
261 if (c1 == ' ' || c1 == '\t')
262 {
263 while (1)
264 {
265 c1 = *t1++;
266 if (c1 == end_char)
267 break;
268 if (c1 != ' ' && c1 != '\t')
269 {
270 --t1;
271 c1 = ' ';
272 break;
273 }
274 }
275 }
276
277 /* Likewise for line 2. */
278 if (c2 == ' ' || c2 == '\t')
279 {
280 while (1)
281 {
282 c2 = *t2++;
283 if (c2 == end_char)
284 break;
285 if (c2 != ' ' && c2 != '\t')
286 {
287 --t2;
288 c2 = ' ';
289 break;
290 }
291 }
292 }
293 }
294
295 /* Upcase all letters if -i is specified. */
296
297 if (ignore_case_flag)
298 {
299 if (islower (c1))
300 c1 = toupper (c1);
301 if (islower (c2))
302 c2 = toupper (c2);
303 }
304
305 if (c1 != c2)
306 break;
307 if (c1 == end_char)
308 return 0;
309 }
310 }
311
312 return (1);
313}
314\f
315/* Find the consecutive changes at the start of the script START.
316 Return the last link before the first gap. */
317
318struct change *
319find_change (start)
320 struct change *start;
321{
322 return start;
323}
324
325struct change *
326find_reverse_change (start)
327 struct change *start;
328{
329 return start;
330}
331\f
332/* Divide SCRIPT into pieces by calling HUNKFUN and
333 print each piece with PRINTFUN.
334 Both functions take one arg, an edit script.
335
336 HUNKFUN is called with the tail of the script
337 and returns the last link that belongs together with the start
338 of the tail.
339
340 PRINTFUN takes a subscript which belongs together (with a null
341 link at the end) and prints it. */
342
343void
344print_script (script, hunkfun, printfun)
345 struct change *script;
346 struct change * (*hunkfun) ();
347 void (*printfun) ();
348{
349 struct change *next = script;
350
351 while (next)
352 {
353 struct change *this, *end;
354
355 /* Find a set of changes that belong together. */
356 this = next;
357 end = (*hunkfun) (next);
358
359 /* Disconnect them from the rest of the changes,
360 making them a hunk, and remember the rest for next iteration. */
361 next = end->link;
362 end->link = NULL;
363#ifdef DEBUG
364 debug_script (this);
365#endif
366
367 /* Print this hunk. */
368 (*printfun) (this);
369
370 /* Reconnect the script so it will all be freed properly. */
371 end->link = next;
372 }
373}
374\f
375/* Print the text of a single line LINE,
376 flagging it with the characters in LINE_FLAG (which say whether
377 the line is inserted, deleted, changed, etc.). */
378
379void
380print_1_line (line_flag, line)
381 char *line_flag;
382 struct line_def *line;
383{
384 int length = line->length; /* must be nonzero */
385 const char *text = line->text; /* Help the compiler. */
386 FILE *out = outfile; /* Help the compiler some more. */
387
388 /* If -T was specified, use a Tab between the line-flag and the text.
389 Otherwise use a Space (as Unix diff does).
390 Print neither space nor tab if line-flags are empty. */
391
392 if (line_flag != NULL && line_flag[0] != 0)
393 fprintf (out, tab_align_flag ? "%s\t" : "%s ", line_flag);
394
395 /* Now output the contents of the line.
396 If -t was specified, expand tabs to spaces.
397 Otherwise output verbatim. */
398
399 if (tab_expand_flag)
400 {
401 register int column = 0;
402 register int i;
403 for (i = 0; i < line->length; i++)
404 {
405 register char c = line->text[i];
406 switch (c)
407 {
408 case '\t':
409 column++;
410 while (column & 7)
411 {
412 putc (' ', out);
413 column++;
414 }
415 c = ' ';
416 break;
417 case '\b':
418 column--;
419 break;
420 default:
421 column++;
422 break;
423 }
424 putc (c, out);
425 }
426 }
427 else
428 fwrite (text, sizeof (char), length, out);
429 if ((line_flag == NULL || line_flag[0] != 0) && text[length - 1] != '\n'
430 && line_end_char == '\n')
431 fprintf (out, "\n\\ No newline at end of file\n");
432}
433
434change_letter (inserts, deletes)
435 int inserts, deletes;
436{
437 if (!inserts)
438 return 'd';
439 else if (!deletes)
440 return 'a';
441 else
442 return 'c';
443}
444\f
445/* Translate an internal line number (an index into diff's table of lines)
446 into an actual line number in the input file.
447 The internal line number is LNUM. FILE points to the data on the file.
448
449 Internal line numbers count from 0 within the current chunk.
450 Actual line numbers count from 1 within the entire file;
451 in addition, they include lines ignored for comparison purposes.
452
453 The `ltran' feature is no longer in use. */
454
455int
456translate_line_number (file, lnum)
457 struct file_data *file;
458 int lnum;
459{
460 return lnum + 1;
461}
462
463void
464translate_range (file, a, b, aptr, bptr)
465 struct file_data *file;
466 int a, b;
467 int *aptr, *bptr;
468{
469 *aptr = translate_line_number (file, a - 1) + 1;
470 *bptr = translate_line_number (file, b + 1) - 1;
471}
472
473/* Print a pair of line numbers with SEPCHAR, translated for file FILE.
474 If the two numbers are identical, print just one number.
475
476 Args A and B are internal line numbers.
477 We print the translated (real) line numbers. */
478
479void
480print_number_range (sepchar, file, a, b)
481 char sepchar;
482 struct file_data *file;
483 int a, b;
484{
485 int trans_a, trans_b;
486 translate_range (file, a, b, &trans_a, &trans_b);
487
488 /* Note: we can have B < A in the case of a range of no lines.
489 In this case, we should print the line number before the range,
490 which is B. */
491 if (trans_b > trans_a)
492 fprintf (outfile, "%d%c%d", trans_a, sepchar, trans_b);
493 else
494 fprintf (outfile, "%d", trans_b);
495}
496\f
497/* Look at a hunk of edit script and report the range of lines in each file
498 that it applies to. HUNK is the start of the hunk, which is a chain
499 of `struct change'. The first and last line numbers of file 0 are stored in
500 *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
501 Note that these are internal line numbers that count from 0.
502
503 If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
504
505 Also set *DELETES nonzero if any lines of file 0 are deleted
506 and set *INSERTS nonzero if any lines of file 1 are inserted.
507 If only ignorable lines are inserted or deleted, both are
508 set to 0. */
509
510void
511analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
512 struct change *hunk;
513 int *first0, *last0, *first1, *last1;
514 int *deletes, *inserts;
515{
516 int f0, l0, f1, l1, show_from, show_to;
517 int i;
518 int nontrivial = !(ignore_blank_lines_flag || ignore_regexp);
519 struct change *next;
520
521 show_from = show_to = 0;
522
523 f0 = hunk->line0;
524 f1 = hunk->line1;
525
526 for (next = hunk; next; next = next->link)
527 {
528 l0 = next->line0 + next->deleted - 1;
529 l1 = next->line1 + next->inserted - 1;
530 show_from += next->deleted;
531 show_to += next->inserted;
532
533 for (i = next->line0; i <= l0 && ! nontrivial; i++)
534 if ((!ignore_blank_lines_flag || files[0].linbuf[i].length > 1)
535 && (!ignore_regexp
536 || 0 > re_search (&ignore_regexp_compiled,
537 files[0].linbuf[i].text,
538 files[0].linbuf[i].length, 0,
539 files[0].linbuf[i].length, 0)))
540 nontrivial = 1;
541
542 for (i = next->line1; i <= l1 && ! nontrivial; i++)
543 if ((!ignore_blank_lines_flag || files[1].linbuf[i].length > 1)
544 && (!ignore_regexp
545 || 0 > re_search (&ignore_regexp_compiled,
546 files[1].linbuf[i].text,
547 files[1].linbuf[i].length, 0,
548 files[1].linbuf[i].length, 0)))
549 nontrivial = 1;
550 }
551
552 *first0 = f0;
553 *last0 = l0;
554 *first1 = f1;
555 *last1 = l1;
556
557 /* If all inserted or deleted lines are ignorable,
558 tell the caller to ignore this hunk. */
559
560 if (!nontrivial)
561 show_from = show_to = 0;
562
563 *deletes = show_from;
564 *inserts = show_to;
565}
566\f
567/* malloc a block of memory, with fatal error message if we can't do it. */
568
569VOID *
570xmalloc (size)
571 unsigned size;
572{
573 register VOID *value;
574
575 if (size == 0)
576 size = 1;
577
578 value = (VOID *) malloc (size);
579
580 if (!value)
581 fatal ("virtual memory exhausted");
582 return value;
583}
584
585/* realloc a block of memory, with fatal error message if we can't do it. */
586
587VOID *
588xrealloc (old, size)
589 VOID *old;
590 unsigned int size;
591{
592 register VOID *value;
593
594 if (size == 0)
595 size = 1;
596
597 value = (VOID *) realloc (old, size);
598
599 if (!value)
600 fatal ("virtual memory exhausted");
601 return value;
602}
603
604/* Concatenate three strings, returning a newly malloc'd string. */
605
606char *
607concat (s1, s2, s3)
608 char *s1, *s2, *s3;
609{
610 int len = strlen (s1) + strlen (s2) + strlen (s3);
611 char *new = (char *) xmalloc (len + 1);
612 strcpy (new, s1);
613 strcat (new, s2);
614 strcat (new, s3);
615 return new;
616}
617\f
618debug_script (sp)
619 struct change *sp;
620{
621 fflush (stdout);
622 for (; sp; sp = sp->link)
623 fprintf (stderr, "%3d %3d delete %d insert %d\n",
624 sp->line0, sp->line1, sp->deleted, sp->inserted);
625 fflush (stderr);
626}