1 /* Support routines for GNU DIFF.
2 Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 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 */
17
18 #include "diff.h"
19
20 __RCSID("$MirOS: src/gnu/usr.bin/cvs/diff/util.c,v 1.4 2010/09/19 19:42:51 tg Exp $");
21
22 #if __STDC__
23 #include <stdarg.h>
24 #else
25 #include <varargs.h>
26 #endif
27
28 #ifndef strerror
29 extern char *strerror ();
30 #endif
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
35 struct msg
36 {
37 struct msg *next;
38 char const *format;
39 char const *arg1;
40 char const *arg2;
41 char const *arg3;
42 char const *arg4;
43 };
44
45 /* Head of the chain of queues messages. */
46
47 static struct msg *msg_chain;
48
49 /* Tail of the chain of queues messages. */
50
51 static struct msg **msg_chain_end = &msg_chain;
52
53 /* Use when a system call returns non-zero status.
54 TEXT should normally be the file name. */
55
56 void
perror_with_name(text)57 perror_with_name (text)
58 char const *text;
59 {
60 int e = errno;
61
62 if (callbacks && callbacks->error)
63 (*callbacks->error) ("%s: %s", text, strerror (e));
64 else
65 {
66 fprintf (stderr, "%s: ", diff_program_name);
67 errno = e;
68 perror (text);
69 }
70 }
71
72 /* Use when a system call returns non-zero status and that is fatal. */
73
74 void
pfatal_with_name(text)75 pfatal_with_name (text)
76 char const *text;
77 {
78 int e = errno;
79 print_message_queue ();
80 if (callbacks && callbacks->error)
81 (*callbacks->error) ("%s: %s", text, strerror (e));
82 else
83 {
84 fprintf (stderr, "%s: ", diff_program_name);
85 errno = e;
86 perror (text);
87 }
88 DIFF_ABORT (2);
89 }
90
91 /* Print an error message from the format-string FORMAT
92 with args ARG1 and ARG2. */
93
94 void
diff_error(format,arg,arg1)95 diff_error (format, arg, arg1)
96 char const *format, *arg, *arg1;
97 {
98 if (callbacks && callbacks->error)
99 (*callbacks->error) (format, arg, arg1);
100 else
101 {
102 fprintf (stderr, "%s: ", diff_program_name);
103 fprintf (stderr, format, arg, arg1);
104 fprintf (stderr, "\n");
105 }
106 }
107
108 /* Print an error message containing the string TEXT, then exit. */
109
110 void
fatal(m)111 fatal (m)
112 char const *m;
113 {
114 print_message_queue ();
115 diff_error ("%s", m, 0);
116 DIFF_ABORT (2);
117 }
118
119 /* Like printf, except if -l in effect then save the message and print later.
120 This is used for things like "binary files differ" and "Only in ...". */
121
122 void
message(format,arg1,arg2)123 message (format, arg1, arg2)
124 char const *format, *arg1, *arg2;
125 {
126 message5 (format, arg1, arg2, 0, 0);
127 }
128
129 void
message5(format,arg1,arg2,arg3,arg4)130 message5 (format, arg1, arg2, arg3, arg4)
131 char const *format, *arg1, *arg2, *arg3, *arg4;
132 {
133 if (paginate_flag)
134 {
135 struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
136 new->format = format;
137 new->arg1 = concat (arg1, "", "");
138 new->arg2 = concat (arg2, "", "");
139 new->arg3 = arg3 ? concat (arg3, "", "") : 0;
140 new->arg4 = arg4 ? concat (arg4, "", "") : 0;
141 new->next = 0;
142 *msg_chain_end = new;
143 msg_chain_end = &new->next;
144 }
145 else
146 {
147 if (sdiff_help_sdiff)
148 write_output (" ", 1);
149 printf_output (format, arg1, arg2, arg3, arg4);
150 }
151 }
152
153 /* Output all the messages that were saved up by calls to `message'. */
154
155 void
print_message_queue()156 print_message_queue ()
157 {
158 struct msg *m;
159
160 for (m = msg_chain; m; m = m->next)
161 printf_output (m->format, m->arg1, m->arg2, m->arg3, m->arg4);
162 }
163
164 /* Call before outputting the results of comparing files NAME0 and NAME1
165 to set up OUTFILE, the stdio stream for the output to go to.
166
167 Usually, OUTFILE is just stdout. But when -l was specified
168 we fork off a `pr' and make OUTFILE a pipe to it.
169 `pr' then outputs to our stdout. */
170
171 static char const *current_name0;
172 static char const *current_name1;
173 static int current_depth;
174
175 static int output_in_progress = 0;
176
177 void
setup_output(name0,name1,depth)178 setup_output (name0, name1, depth)
179 char const *name0, *name1;
180 int depth;
181 {
182 current_name0 = name0;
183 current_name1 = name1;
184 current_depth = depth;
185 }
186
187 #if HAVE_FORK && defined (PR_PROGRAM)
188 static pid_t pr_pid;
189 #endif
190
191 void
begin_output()192 begin_output ()
193 {
194 char *name;
195
196 if (output_in_progress)
197 return;
198 output_in_progress = 1;
199
200 /* Construct the header of this piece of diff. */
201 name = xmalloc (strlen (current_name0) + strlen (current_name1)
202 + strlen (switch_string) + 7);
203 /* Posix.2 section 4.17.6.1.1 specifies this format. But there is a
204 bug in the first printing (IEEE Std 1003.2-1992 p 251 l 3304):
205 it says that we must print only the last component of the pathnames.
206 This requirement is silly and does not match historical practice. */
207 sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
208
209 if (paginate_flag && callbacks && callbacks->write_output)
210 fatal ("can't paginate when using library callbacks");
211
212 if (paginate_flag)
213 {
214 /* Make OUTFILE a pipe to a subsidiary `pr'. */
215
216 #ifdef PR_PROGRAM
217
218 # if HAVE_FORK
219 int pipes[2];
220
221 if (pipe (pipes) != 0)
222 pfatal_with_name ("pipe");
223
224 fflush (stdout);
225
226 pr_pid = vfork ();
227 if (pr_pid < 0)
228 pfatal_with_name ("vfork");
229
230 if (pr_pid == 0)
231 {
232 close (pipes[1]);
233 if (pipes[0] != STDIN_FILENO)
234 {
235 if (dup2 (pipes[0], STDIN_FILENO) < 0)
236 pfatal_with_name ("dup2");
237 close (pipes[0]);
238 }
239
240 execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, NULL);
241 pfatal_with_name (PR_PROGRAM);
242 }
243 else
244 {
245 close (pipes[0]);
246 outfile = fdopen (pipes[1], "w");
247 if (!outfile)
248 pfatal_with_name ("fdopen");
249 }
250 # else /* ! HAVE_FORK */
251 char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10);
252 char *p;
253 char const *a = name;
254 sprintf (command, "%s -f -h ", PR_PROGRAM);
255 p = command + strlen (command);
256 SYSTEM_QUOTE_ARG (p, a);
257 *p = 0;
258 outfile = popen (command, "w");
259 if (!outfile)
260 pfatal_with_name (command);
261 free (command);
262 # endif /* ! HAVE_FORK */
263 #else
264 fatal ("This port does not support the --paginate option to diff.");
265 #endif
266 }
267 else
268 {
269
270 /* If -l was not specified, output the diff straight to `stdout'. */
271
272 /* If handling multiple files (because scanning a directory),
273 print which files the following output is about. */
274 if (current_depth > 0)
275 printf_output ("%s\n", name);
276 }
277
278 free (name);
279
280 /* A special header is needed at the beginning of context output. */
281 switch (output_style)
282 {
283 case OUTPUT_CONTEXT:
284 print_context_header (files, 0);
285 break;
286
287 case OUTPUT_UNIFIED:
288 print_context_header (files, 1);
289 break;
290
291 default:
292 break;
293 }
294 }
295
296 /* Call after the end of output of diffs for one file.
297 If -l was given, close OUTFILE and get rid of the `pr' subfork. */
298
299 void
finish_output()300 finish_output ()
301 {
302 if (paginate_flag && outfile != 0 && outfile != stdout)
303 {
304 #ifdef PR_PROGRAM
305 int wstatus, w;
306 if (ferror (outfile))
307 fatal ("write error");
308 # if ! HAVE_FORK
309 wstatus = pclose (outfile);
310 # else /* HAVE_FORK */
311 if (fclose (outfile) != 0)
312 pfatal_with_name ("write error");
313 while ((w = waitpid (pr_pid, &wstatus, 0)) < 0 && errno == EINTR)
314 ;
315 if (w < 0)
316 pfatal_with_name ("waitpid");
317 # endif /* HAVE_FORK */
318 if (wstatus != 0)
319 fatal ("subsidiary pr failed");
320 #else
321 fatal ("internal error in finish_output");
322 #endif
323 }
324
325 output_in_progress = 0;
326 }
327
328 /* Write something to the output file. */
329
330 void
write_output(text,len)331 write_output (text, len)
332 char const *text;
333 size_t len;
334 {
335 if (callbacks && callbacks->write_output)
336 (*callbacks->write_output) (text, len);
337 else if (len == 1)
338 putc (*text, outfile);
339 else
340 fwrite (text, sizeof (char), len, outfile);
341 }
342
343 /* Printf something to the output file. */
344
345 #if __STDC__
346 #define VA_START(args, lastarg) va_start(args, lastarg)
347 #else /* ! __STDC__ */
348 #define VA_START(args, lastarg) va_start(args)
349 #endif /* __STDC__ */
350
351 void
352 #if __STDC__
printf_output(const char * format,...)353 printf_output (const char *format, ...)
354 #else
355 printf_output (format, va_alist)
356 char const *format;
357 va_dcl
358 #endif
359 {
360 va_list args;
361
362 VA_START (args, format);
363 if (callbacks && callbacks->write_output)
364 {
365 /* We implement our own limited printf-like functionality (%s, %d,
366 and %c only). Callers who want something fancier can use
367 sprintf. */
368 const char *p = format;
369 char *q;
370 char *str;
371 int num;
372 int ch;
373 char buf[100];
374
375 while ((q = strchr (p, '%')) != NULL)
376 {
377 static const char msg[] =
378 "\ninternal error: bad % in printf_output\n";
379 (*callbacks->write_output) (p, q - p);
380
381 switch (q[1])
382 {
383 case 's':
384 str = va_arg (args, char *);
385 (*callbacks->write_output) (str, strlen (str));
386 break;
387 case 'd':
388 num = va_arg (args, int);
389 sprintf (buf, "%d", num);
390 (*callbacks->write_output) (buf, strlen (buf));
391 break;
392 case 'c':
393 ch = va_arg (args, int);
394 buf[0] = ch;
395 (*callbacks->write_output) (buf, 1);
396 break;
397 default:
398 (*callbacks->write_output) (msg, sizeof (msg) - 1);
399 /* Don't just keep going, because q + 1 might point to the
400 terminating '\0'. */
401 goto out;
402 }
403 p = q + 2;
404 }
405 (*callbacks->write_output) (p, strlen (p));
406 }
407 else
408 vfprintf (outfile, format, args);
409 out:
410 va_end (args);
411 }
412
413 /* Flush the output file. */
414
415 void
flush_output()416 flush_output ()
417 {
418 if (callbacks && callbacks->flush_output)
419 (*callbacks->flush_output) ();
420 else
421 fflush (outfile);
422 }
423
424 /* Compare two lines (typically one from each input file)
425 according to the command line options.
426 For efficiency, this is invoked only when the lines do not match exactly
427 but an option like -i might cause us to ignore the difference.
428 Return nonzero if the lines differ. */
429
430 int
line_cmp(s1,s2)431 line_cmp (s1, s2)
432 char const *s1, *s2;
433 {
434 register unsigned char const *t1 = (unsigned char const *) s1;
435 register unsigned char const *t2 = (unsigned char const *) s2;
436
437 while (1)
438 {
439 register unsigned char c1 = *t1++;
440 register unsigned char c2 = *t2++;
441
442 /* Test for exact char equality first, since it's a common case. */
443 if (c1 != c2)
444 {
445 /* Ignore horizontal white space if -b or -w is specified. */
446
447 if (ignore_all_space_flag)
448 {
449 /* For -w, just skip past any white space. */
450 while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
451 while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
452 }
453 else if (ignore_space_change_flag)
454 {
455 /* For -b, advance past any sequence of white space in line 1
456 and consider it just one Space, or nothing at all
457 if it is at the end of the line. */
458 if (ISSPACE (c1))
459 {
460 while (c1 != '\n')
461 {
462 c1 = *t1++;
463 if (! ISSPACE (c1))
464 {
465 --t1;
466 c1 = ' ';
467 break;
468 }
469 }
470 }
471
472 /* Likewise for line 2. */
473 if (ISSPACE (c2))
474 {
475 while (c2 != '\n')
476 {
477 c2 = *t2++;
478 if (! ISSPACE (c2))
479 {
480 --t2;
481 c2 = ' ';
482 break;
483 }
484 }
485 }
486
487 if (c1 != c2)
488 {
489 /* If we went too far when doing the simple test
490 for equality, go back to the first non-white-space
491 character in both sides and try again. */
492 if (c2 == ' ' && c1 != '\n'
493 && (unsigned char const *) s1 + 1 < t1
494 && ISSPACE(t1[-2]))
495 {
496 --t1;
497 continue;
498 }
499 if (c1 == ' ' && c2 != '\n'
500 && (unsigned char const *) s2 + 1 < t2
501 && ISSPACE(t2[-2]))
502 {
503 --t2;
504 continue;
505 }
506 }
507 }
508
509 /* Lowercase all letters if -i is specified. */
510
511 if (ignore_case_flag)
512 {
513 if (ISUPPER (c1))
514 c1 = tolower (c1);
515 if (ISUPPER (c2))
516 c2 = tolower (c2);
517 }
518
519 if (c1 != c2)
520 break;
521 }
522 if (c1 == '\n')
523 return 0;
524 }
525
526 return (1);
527 }
528
529 /* Find the consecutive changes at the start of the script START.
530 Return the last link before the first gap. */
531
532 struct change *
find_change(start)533 find_change (start)
534 struct change *start;
535 {
536 return start;
537 }
538
539 struct change *
find_reverse_change(start)540 find_reverse_change (start)
541 struct change *start;
542 {
543 return start;
544 }
545
546 /* Divide SCRIPT into pieces by calling HUNKFUN and
547 print each piece with PRINTFUN.
548 Both functions take one arg, an edit script.
549
550 HUNKFUN is called with the tail of the script
551 and returns the last link that belongs together with the start
552 of the tail.
553
554 PRINTFUN takes a subscript which belongs together (with a null
555 link at the end) and prints it. */
556
557 void
print_script(script,hunkfun,printfun)558 print_script (script, hunkfun, printfun)
559 struct change *script;
560 struct change * (*hunkfun) PARAMS((struct change *));
561 void (*printfun) PARAMS((struct change *));
562 {
563 struct change *next = script;
564
565 while (next)
566 {
567 struct change *this, *end;
568
569 /* Find a set of changes that belong together. */
570 this = next;
571 end = (*hunkfun) (next);
572
573 /* Disconnect them from the rest of the changes,
574 making them a hunk, and remember the rest for next iteration. */
575 next = end->link;
576 end->link = 0;
577 #ifdef DEBUG
578 debug_script (this);
579 #endif
580
581 /* Print this hunk. */
582 (*printfun) (this);
583
584 /* Reconnect the script so it will all be freed properly. */
585 end->link = next;
586 }
587 }
588
589 /* Print the text of a single line LINE,
590 flagging it with the characters in LINE_FLAG (which say whether
591 the line is inserted, deleted, changed, etc.). */
592
593 void
print_1_line(line_flag,line)594 print_1_line (line_flag, line)
595 char const *line_flag;
596 char const * const *line;
597 {
598 char const *text = line[0], *limit = line[1]; /* Help the compiler. */
599 char const *flag_format = 0;
600
601 /* If -T was specified, use a Tab between the line-flag and the text.
602 Otherwise use a Space (as Unix diff does).
603 Print neither space nor tab if line-flags are empty. */
604
605 if (line_flag && *line_flag)
606 {
607 flag_format = tab_align_flag ? "%s\t" : "%s ";
608 printf_output (flag_format, line_flag);
609 }
610
611 output_1_line (text, limit, flag_format, line_flag);
612
613 if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
614 printf_output ("\n\\ No newline at end of file\n");
615 }
616
617 /* Output a line from TEXT up to LIMIT. Without -t, output verbatim.
618 With -t, expand white space characters to spaces, and if FLAG_FORMAT
619 is nonzero, output it with argument LINE_FLAG after every
620 internal carriage return, so that tab stops continue to line up. */
621
622 void
output_1_line(text,limit,flag_format,line_flag)623 output_1_line (text, limit, flag_format, line_flag)
624 char const *text, *limit, *flag_format, *line_flag;
625 {
626 if (!tab_expand_flag)
627 write_output (text, limit - text);
628 else
629 {
630 register unsigned char c;
631 register char const *t = text;
632 register unsigned column = 0;
633 /* CC is used to avoid taking the address of the register
634 variable C. */
635 char cc;
636
637 while (t < limit)
638 switch ((c = *t++))
639 {
640 case '\t':
641 {
642 unsigned spaces = TAB_WIDTH - column % TAB_WIDTH;
643 column += spaces;
644 do
645 write_output (" ", 1);
646 while (--spaces);
647 }
648 break;
649
650 case '\r':
651 write_output ("\r", 1);
652 if (flag_format && t < limit && *t != '\n')
653 printf_output (flag_format, line_flag);
654 column = 0;
655 break;
656
657 case '\b':
658 if (column == 0)
659 continue;
660 column--;
661 write_output ("\b", 1);
662 break;
663
664 default:
665 if (ISPRINT (c))
666 column++;
667 cc = c;
668 write_output (&cc, 1);
669 break;
670 }
671 }
672 }
673
674 int
change_letter(inserts,deletes)675 change_letter (inserts, deletes)
676 int inserts, deletes;
677 {
678 if (!inserts)
679 return 'd';
680 else if (!deletes)
681 return 'a';
682 else
683 return 'c';
684 }
685
686 /* Translate an internal line number (an index into diff's table of lines)
687 into an actual line number in the input file.
688 The internal line number is LNUM. FILE points to the data on the file.
689
690 Internal line numbers count from 0 starting after the prefix.
691 Actual line numbers count from 1 within the entire file. */
692
693 int
translate_line_number(file,lnum)694 translate_line_number (file, lnum)
695 struct file_data const *file;
696 int lnum;
697 {
698 return lnum + file->prefix_lines + 1;
699 }
700
701 void
translate_range(file,a,b,aptr,bptr)702 translate_range (file, a, b, aptr, bptr)
703 struct file_data const *file;
704 int a, b;
705 int *aptr, *bptr;
706 {
707 *aptr = translate_line_number (file, a - 1) + 1;
708 *bptr = translate_line_number (file, b + 1) - 1;
709 }
710
711 /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
712 If the two numbers are identical, print just one number.
713
714 Args A and B are internal line numbers.
715 We print the translated (real) line numbers. */
716
717 void
print_number_range(sepchar,file,a,b)718 print_number_range (sepchar, file, a, b)
719 int sepchar;
720 struct file_data *file;
721 int a, b;
722 {
723 int trans_a, trans_b;
724 translate_range (file, a, b, &trans_a, &trans_b);
725
726 /* Note: we can have B < A in the case of a range of no lines.
727 In this case, we should print the line number before the range,
728 which is B. */
729 if (trans_b > trans_a)
730 printf_output ("%d%c%d", trans_a, sepchar, trans_b);
731 else
732 printf_output ("%d", trans_b);
733 }
734
735 /* Look at a hunk of edit script and report the range of lines in each file
736 that it applies to. HUNK is the start of the hunk, which is a chain
737 of `struct change'. The first and last line numbers of file 0 are stored in
738 *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
739 Note that these are internal line numbers that count from 0.
740
741 If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
742
743 Also set *DELETES nonzero if any lines of file 0 are deleted
744 and set *INSERTS nonzero if any lines of file 1 are inserted.
745 If only ignorable lines are inserted or deleted, both are
746 set to 0. */
747
748 void
analyze_hunk(hunk,first0,last0,first1,last1,deletes,inserts)749 analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
750 struct change *hunk;
751 int *first0, *last0, *first1, *last1;
752 int *deletes, *inserts;
753 {
754 int l0, l1, show_from, show_to;
755 int i;
756 int trivial = ignore_blank_lines_flag || ignore_regexp_list;
757 struct change *next;
758
759 show_from = show_to = 0;
760
761 *first0 = hunk->line0;
762 *first1 = hunk->line1;
763
764 next = hunk;
765 do
766 {
767 l0 = next->line0 + next->deleted - 1;
768 l1 = next->line1 + next->inserted - 1;
769 show_from += next->deleted;
770 show_to += next->inserted;
771
772 for (i = next->line0; i <= l0 && trivial; i++)
773 if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n')
774 {
775 struct regexp_list *r;
776 char const *line = files[0].linbuf[i];
777 int len = files[0].linbuf[i + 1] - line;
778
779 for (r = ignore_regexp_list; r; r = r->next)
780 if (0 <= re_search (&r->buf, line, len, 0, len, 0))
781 break; /* Found a match. Ignore this line. */
782 /* If we got all the way through the regexp list without
783 finding a match, then it's nontrivial. */
784 if (!r)
785 trivial = 0;
786 }
787
788 for (i = next->line1; i <= l1 && trivial; i++)
789 if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n')
790 {
791 struct regexp_list *r;
792 char const *line = files[1].linbuf[i];
793 int len = files[1].linbuf[i + 1] - line;
794
795 for (r = ignore_regexp_list; r; r = r->next)
796 if (0 <= re_search (&r->buf, line, len, 0, len, 0))
797 break; /* Found a match. Ignore this line. */
798 /* If we got all the way through the regexp list without
799 finding a match, then it's nontrivial. */
800 if (!r)
801 trivial = 0;
802 }
803 }
804 while ((next = next->link) != 0);
805
806 *last0 = l0;
807 *last1 = l1;
808
809 /* If all inserted or deleted lines are ignorable,
810 tell the caller to ignore this hunk. */
811
812 if (trivial)
813 show_from = show_to = 0;
814
815 *deletes = show_from;
816 *inserts = show_to;
817 }
818
819 /* Concatenate three strings, returning a newly malloc'd string. */
820
821 char *
concat(s1,s2,s3)822 concat (s1, s2, s3)
823 char const *s1, *s2, *s3;
824 {
825 size_t len = strlen (s1) + strlen (s2) + strlen (s3);
826 char *new = xmalloc (len + 1);
827 sprintf (new, "%s%s%s", s1, s2, s3);
828 return new;
829 }
830
831 /* Yield the newly malloc'd pathname
832 of the file in DIR whose filename is FILE. */
833
834 char *
dir_file_pathname(dir,file)835 dir_file_pathname (dir, file)
836 char const *dir, *file;
837 {
838 char const *p = filename_lastdirchar (dir);
839 return concat (dir, "/" + (p && !p[1]), file);
840 }
841
842 void
debug_script(sp)843 debug_script (sp)
844 struct change *sp;
845 {
846 fflush (stdout);
847 for (; sp; sp = sp->link)
848 fprintf (stderr, "%3d %3d delete %d insert %d\n",
849 sp->line0, sp->line1, sp->deleted, sp->inserted);
850 fflush (stderr);
851 }
852