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 | |
---|
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 | |
---|
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 | |
---|
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 | |
---|
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 | |
---|
269 | void |
---|
270 | finish_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 | |
---|
298 | int |
---|
299 | line_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 | |
---|
400 | struct change * |
---|
401 | find_change (start) |
---|
402 | struct change *start; |
---|
403 | { |
---|
404 | return start; |
---|
405 | } |
---|
406 | |
---|
407 | struct change * |
---|
408 | find_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 | |
---|
425 | void |
---|
426 | print_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 | |
---|
461 | void |
---|
462 | print_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 | |
---|
491 | void |
---|
492 | output_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 | |
---|
540 | int |
---|
541 | change_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 | |
---|
559 | int |
---|
560 | translate_line_number (file, lnum) |
---|
561 | struct file_data const *file; |
---|
562 | int lnum; |
---|
563 | { |
---|
564 | return lnum + file->prefix_lines + 1; |
---|
565 | } |
---|
566 | |
---|
567 | void |
---|
568 | translate_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 | |
---|
583 | void |
---|
584 | print_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 | |
---|
614 | void |
---|
615 | analyze_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 | |
---|
687 | VOID * |
---|
688 | xmalloc (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 | |
---|
705 | VOID * |
---|
706 | xrealloc (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 | |
---|
724 | char * |
---|
725 | concat (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 | |
---|
737 | char * |
---|
738 | dir_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 | |
---|
745 | void |
---|
746 | debug_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 | } |
---|