12286d8edStholo /* Support routines for GNU DIFF.
2b2346922Stholo Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc.
32286d8edStholo
42286d8edStholo This file is part of GNU DIFF.
52286d8edStholo
62286d8edStholo GNU DIFF is free software; you can redistribute it and/or modify
72286d8edStholo it under the terms of the GNU General Public License as published by
82286d8edStholo the Free Software Foundation; either version 2, or (at your option)
92286d8edStholo any later version.
102286d8edStholo
112286d8edStholo GNU DIFF is distributed in the hope that it will be useful,
122286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
132286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
142286d8edStholo GNU General Public License for more details.
152286d8edStholo
16c71bc7e2Stholo */
172286d8edStholo
182286d8edStholo #include "diff.h"
192286d8edStholo
20c71bc7e2Stholo #if __STDC__
21b2346922Stholo #include <stdarg.h>
22b2346922Stholo #else
23b2346922Stholo #include <varargs.h>
24b2346922Stholo #endif
25b2346922Stholo
26b2346922Stholo #ifndef strerror
27b2346922Stholo extern char *strerror ();
28b2346922Stholo #endif
29b2346922Stholo
302286d8edStholo /* Queue up one-line messages to be printed at the end,
312286d8edStholo when -l is specified. Each message is recorded with a `struct msg'. */
322286d8edStholo
332286d8edStholo struct msg
342286d8edStholo {
352286d8edStholo struct msg *next;
362286d8edStholo char const *format;
372286d8edStholo char const *arg1;
382286d8edStholo char const *arg2;
392286d8edStholo char const *arg3;
402286d8edStholo char const *arg4;
412286d8edStholo };
422286d8edStholo
432286d8edStholo /* Head of the chain of queues messages. */
442286d8edStholo
452286d8edStholo static struct msg *msg_chain;
462286d8edStholo
472286d8edStholo /* Tail of the chain of queues messages. */
482286d8edStholo
492286d8edStholo static struct msg **msg_chain_end = &msg_chain;
502286d8edStholo
512286d8edStholo /* Use when a system call returns non-zero status.
522286d8edStholo TEXT should normally be the file name. */
532286d8edStholo
542286d8edStholo void
perror_with_name(text)552286d8edStholo perror_with_name (text)
562286d8edStholo char const *text;
572286d8edStholo {
582286d8edStholo int e = errno;
59b2346922Stholo
60b2346922Stholo if (callbacks && callbacks->error)
61b2346922Stholo (*callbacks->error) ("%s: %s", text, strerror (e));
62b2346922Stholo else
63b2346922Stholo {
642286d8edStholo fprintf (stderr, "%s: ", diff_program_name);
652286d8edStholo errno = e;
662286d8edStholo perror (text);
672286d8edStholo }
68b2346922Stholo }
692286d8edStholo
702286d8edStholo /* Use when a system call returns non-zero status and that is fatal. */
712286d8edStholo
722286d8edStholo void
pfatal_with_name(text)732286d8edStholo pfatal_with_name (text)
742286d8edStholo char const *text;
752286d8edStholo {
762286d8edStholo int e = errno;
772286d8edStholo print_message_queue ();
78b2346922Stholo if (callbacks && callbacks->error)
79b2346922Stholo (*callbacks->error) ("%s: %s", text, strerror (e));
80b2346922Stholo else
81b2346922Stholo {
822286d8edStholo fprintf (stderr, "%s: ", diff_program_name);
832286d8edStholo errno = e;
842286d8edStholo perror (text);
85b2346922Stholo }
862286d8edStholo DIFF_ABORT (2);
872286d8edStholo }
882286d8edStholo
892286d8edStholo /* Print an error message from the format-string FORMAT
902286d8edStholo with args ARG1 and ARG2. */
912286d8edStholo
922286d8edStholo void
diff_error(format,arg,arg1)932286d8edStholo diff_error (format, arg, arg1)
942286d8edStholo char const *format, *arg, *arg1;
952286d8edStholo {
96b2346922Stholo if (callbacks && callbacks->error)
97b2346922Stholo (*callbacks->error) (format, arg, arg1);
98b2346922Stholo else
99b2346922Stholo {
1002286d8edStholo fprintf (stderr, "%s: ", diff_program_name);
1012286d8edStholo fprintf (stderr, format, arg, arg1);
1022286d8edStholo fprintf (stderr, "\n");
1032286d8edStholo }
104b2346922Stholo }
1052286d8edStholo
1062286d8edStholo /* Print an error message containing the string TEXT, then exit. */
1072286d8edStholo
1082286d8edStholo void
fatal(m)1092286d8edStholo fatal (m)
1102286d8edStholo char const *m;
1112286d8edStholo {
1122286d8edStholo print_message_queue ();
1132286d8edStholo diff_error ("%s", m, 0);
1142286d8edStholo DIFF_ABORT (2);
1152286d8edStholo }
1162286d8edStholo
1172286d8edStholo /* Like printf, except if -l in effect then save the message and print later.
1182286d8edStholo This is used for things like "binary files differ" and "Only in ...". */
1192286d8edStholo
1202286d8edStholo void
message(format,arg1,arg2)1212286d8edStholo message (format, arg1, arg2)
1222286d8edStholo char const *format, *arg1, *arg2;
1232286d8edStholo {
1242286d8edStholo message5 (format, arg1, arg2, 0, 0);
1252286d8edStholo }
1262286d8edStholo
1272286d8edStholo void
message5(format,arg1,arg2,arg3,arg4)1282286d8edStholo message5 (format, arg1, arg2, arg3, arg4)
1292286d8edStholo char const *format, *arg1, *arg2, *arg3, *arg4;
1302286d8edStholo {
1312286d8edStholo if (paginate_flag)
1322286d8edStholo {
1332286d8edStholo struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
1342286d8edStholo new->format = format;
1352286d8edStholo new->arg1 = concat (arg1, "", "");
1362286d8edStholo new->arg2 = concat (arg2, "", "");
1372286d8edStholo new->arg3 = arg3 ? concat (arg3, "", "") : 0;
1382286d8edStholo new->arg4 = arg4 ? concat (arg4, "", "") : 0;
1392286d8edStholo new->next = 0;
1402286d8edStholo *msg_chain_end = new;
1412286d8edStholo msg_chain_end = &new->next;
1422286d8edStholo }
1432286d8edStholo else
1442286d8edStholo {
1452286d8edStholo if (sdiff_help_sdiff)
146b2346922Stholo write_output (" ", 1);
147b2346922Stholo printf_output (format, arg1, arg2, arg3, arg4);
1482286d8edStholo }
1492286d8edStholo }
1502286d8edStholo
1512286d8edStholo /* Output all the messages that were saved up by calls to `message'. */
1522286d8edStholo
1532286d8edStholo void
print_message_queue()1542286d8edStholo print_message_queue ()
1552286d8edStholo {
1562286d8edStholo struct msg *m;
1572286d8edStholo
1582286d8edStholo for (m = msg_chain; m; m = m->next)
159b2346922Stholo printf_output (m->format, m->arg1, m->arg2, m->arg3, m->arg4);
1602286d8edStholo }
1612286d8edStholo
1622286d8edStholo /* Call before outputting the results of comparing files NAME0 and NAME1
1632286d8edStholo to set up OUTFILE, the stdio stream for the output to go to.
1642286d8edStholo
1652286d8edStholo Usually, OUTFILE is just stdout. But when -l was specified
1662286d8edStholo we fork off a `pr' and make OUTFILE a pipe to it.
1672286d8edStholo `pr' then outputs to our stdout. */
1682286d8edStholo
1692286d8edStholo static char const *current_name0;
1702286d8edStholo static char const *current_name1;
1712286d8edStholo static int current_depth;
1722286d8edStholo
1732286d8edStholo static int output_in_progress = 0;
1742286d8edStholo
1752286d8edStholo void
setup_output(name0,name1,depth)1762286d8edStholo setup_output (name0, name1, depth)
1772286d8edStholo char const *name0, *name1;
1782286d8edStholo int depth;
1792286d8edStholo {
1802286d8edStholo current_name0 = name0;
1812286d8edStholo current_name1 = name1;
1822286d8edStholo current_depth = depth;
1832286d8edStholo }
1842286d8edStholo
1852286d8edStholo #if HAVE_FORK && defined (PR_PROGRAM)
1862286d8edStholo static pid_t pr_pid;
1872286d8edStholo #endif
1882286d8edStholo
1892286d8edStholo void
begin_output()1902286d8edStholo begin_output ()
1912286d8edStholo {
1922286d8edStholo char *name;
1932286d8edStholo
1942286d8edStholo if (output_in_progress)
1952286d8edStholo return;
1962286d8edStholo output_in_progress = 1;
1972286d8edStholo
1982286d8edStholo /* Construct the header of this piece of diff. */
1992286d8edStholo name = xmalloc (strlen (current_name0) + strlen (current_name1)
2002286d8edStholo + strlen (switch_string) + 7);
2012286d8edStholo /* Posix.2 section 4.17.6.1.1 specifies this format. But there is a
2022286d8edStholo bug in the first printing (IEEE Std 1003.2-1992 p 251 l 3304):
2032286d8edStholo it says that we must print only the last component of the pathnames.
2042286d8edStholo This requirement is silly and does not match historical practice. */
2052286d8edStholo sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
2062286d8edStholo
207b2346922Stholo if (paginate_flag && callbacks && callbacks->write_output)
208b2346922Stholo fatal ("can't paginate when using library callbacks");
209b2346922Stholo
2102286d8edStholo if (paginate_flag)
2112286d8edStholo {
2122286d8edStholo /* Make OUTFILE a pipe to a subsidiary `pr'. */
2132286d8edStholo
2142286d8edStholo #ifdef PR_PROGRAM
2152286d8edStholo
2162286d8edStholo # if HAVE_FORK
2172286d8edStholo int pipes[2];
2182286d8edStholo
2192286d8edStholo if (pipe (pipes) != 0)
2202286d8edStholo pfatal_with_name ("pipe");
2212286d8edStholo
2222286d8edStholo fflush (stdout);
2232286d8edStholo
2242286d8edStholo pr_pid = vfork ();
2252286d8edStholo if (pr_pid < 0)
2262286d8edStholo pfatal_with_name ("vfork");
2272286d8edStholo
2282286d8edStholo if (pr_pid == 0)
2292286d8edStholo {
2302286d8edStholo close (pipes[1]);
2312286d8edStholo if (pipes[0] != STDIN_FILENO)
2322286d8edStholo {
2332286d8edStholo if (dup2 (pipes[0], STDIN_FILENO) < 0)
2342286d8edStholo pfatal_with_name ("dup2");
2352286d8edStholo close (pipes[0]);
2362286d8edStholo }
2372286d8edStholo
238*c96f6a27Sderaadt execl (PR_PROGRAM, PR_PROGRAM, "-f", "-h", name, (char *)NULL);
2392286d8edStholo pfatal_with_name (PR_PROGRAM);
2402286d8edStholo }
2412286d8edStholo else
2422286d8edStholo {
2432286d8edStholo close (pipes[0]);
2442286d8edStholo outfile = fdopen (pipes[1], "w");
2452286d8edStholo if (!outfile)
2462286d8edStholo pfatal_with_name ("fdopen");
2472286d8edStholo }
2482286d8edStholo # else /* ! HAVE_FORK */
2492286d8edStholo char *command = xmalloc (4 * strlen (name) + strlen (PR_PROGRAM) + 10);
2502286d8edStholo char *p;
2512286d8edStholo char const *a = name;
2522286d8edStholo sprintf (command, "%s -f -h ", PR_PROGRAM);
2532286d8edStholo p = command + strlen (command);
2542286d8edStholo SYSTEM_QUOTE_ARG (p, a);
2552286d8edStholo *p = 0;
2562286d8edStholo outfile = popen (command, "w");
2572286d8edStholo if (!outfile)
2582286d8edStholo pfatal_with_name (command);
2592286d8edStholo free (command);
2602286d8edStholo # endif /* ! HAVE_FORK */
2612286d8edStholo #else
2622286d8edStholo fatal ("This port does not support the --paginate option to diff.");
2632286d8edStholo #endif
2642286d8edStholo }
2652286d8edStholo else
2662286d8edStholo {
2672286d8edStholo
2682286d8edStholo /* If -l was not specified, output the diff straight to `stdout'. */
2692286d8edStholo
2702286d8edStholo /* If handling multiple files (because scanning a directory),
2712286d8edStholo print which files the following output is about. */
2722286d8edStholo if (current_depth > 0)
273b2346922Stholo printf_output ("%s\n", name);
2742286d8edStholo }
2752286d8edStholo
2762286d8edStholo free (name);
2772286d8edStholo
2782286d8edStholo /* A special header is needed at the beginning of context output. */
2792286d8edStholo switch (output_style)
2802286d8edStholo {
2812286d8edStholo case OUTPUT_CONTEXT:
2822286d8edStholo print_context_header (files, 0);
2832286d8edStholo break;
2842286d8edStholo
2852286d8edStholo case OUTPUT_UNIFIED:
2862286d8edStholo print_context_header (files, 1);
2872286d8edStholo break;
2882286d8edStholo
2892286d8edStholo default:
2902286d8edStholo break;
2912286d8edStholo }
2922286d8edStholo }
2932286d8edStholo
2942286d8edStholo /* Call after the end of output of diffs for one file.
2952286d8edStholo If -l was given, close OUTFILE and get rid of the `pr' subfork. */
2962286d8edStholo
2972286d8edStholo void
finish_output()2982286d8edStholo finish_output ()
2992286d8edStholo {
3002286d8edStholo if (paginate_flag && outfile != 0 && outfile != stdout)
3012286d8edStholo {
3022286d8edStholo #ifdef PR_PROGRAM
3032286d8edStholo int wstatus;
3042286d8edStholo if (ferror (outfile))
3052286d8edStholo fatal ("write error");
3062286d8edStholo # if ! HAVE_FORK
3072286d8edStholo wstatus = pclose (outfile);
3082286d8edStholo # else /* HAVE_FORK */
3092286d8edStholo if (fclose (outfile) != 0)
3102286d8edStholo pfatal_with_name ("write error");
3112286d8edStholo if (waitpid (pr_pid, &wstatus, 0) < 0)
3122286d8edStholo pfatal_with_name ("waitpid");
3132286d8edStholo # endif /* HAVE_FORK */
3142286d8edStholo if (wstatus != 0)
3152286d8edStholo fatal ("subsidiary pr failed");
3162286d8edStholo #else
3172286d8edStholo fatal ("internal error in finish_output");
3182286d8edStholo #endif
3192286d8edStholo }
3202286d8edStholo
3212286d8edStholo output_in_progress = 0;
3222286d8edStholo }
323b2346922Stholo
324b2346922Stholo /* Write something to the output file. */
325b2346922Stholo
326b2346922Stholo void
write_output(text,len)327b2346922Stholo write_output (text, len)
328b2346922Stholo char const *text;
329b2346922Stholo size_t len;
330b2346922Stholo {
331b2346922Stholo if (callbacks && callbacks->write_output)
332b2346922Stholo (*callbacks->write_output) (text, len);
333b2346922Stholo else if (len == 1)
334b2346922Stholo putc (*text, outfile);
335b2346922Stholo else
336b2346922Stholo fwrite (text, sizeof (char), len, outfile);
337b2346922Stholo }
338b2346922Stholo
339b2346922Stholo /* Printf something to the output file. */
340b2346922Stholo
341c71bc7e2Stholo #if __STDC__
342b2346922Stholo #define VA_START(args, lastarg) va_start(args, lastarg)
343b2346922Stholo #else /* ! __STDC__ */
344b2346922Stholo #define VA_START(args, lastarg) va_start(args)
345b2346922Stholo #endif /* __STDC__ */
346b2346922Stholo
347b2346922Stholo void
348c71bc7e2Stholo #if __STDC__
printf_output(const char * format,...)349b2346922Stholo printf_output (const char *format, ...)
350b2346922Stholo #else
351b2346922Stholo printf_output (format, va_alist)
352b2346922Stholo char const *format;
353b2346922Stholo va_dcl
354b2346922Stholo #endif
355b2346922Stholo {
356b2346922Stholo va_list args;
357b2346922Stholo
358b2346922Stholo VA_START (args, format);
359b2346922Stholo if (callbacks && callbacks->write_output)
360b2346922Stholo {
361c71bc7e2Stholo /* We implement our own limited printf-like functionality (%s, %d,
362c71bc7e2Stholo and %c only). Callers who want something fancier can use
363c71bc7e2Stholo sprintf. */
364c71bc7e2Stholo const char *p = format;
365c71bc7e2Stholo char *q;
366c71bc7e2Stholo char *str;
367c71bc7e2Stholo int num;
368c71bc7e2Stholo int ch;
369e77048c1Stholo char buf[100];
370b2346922Stholo
371c71bc7e2Stholo while ((q = strchr (p, '%')) != NULL)
372c71bc7e2Stholo {
373c71bc7e2Stholo static const char msg[] =
374c71bc7e2Stholo "\ninternal error: bad % in printf_output\n";
375c71bc7e2Stholo (*callbacks->write_output) (p, q - p);
376c71bc7e2Stholo
377c71bc7e2Stholo switch (q[1])
378c71bc7e2Stholo {
379c71bc7e2Stholo case 's':
380c71bc7e2Stholo str = va_arg (args, char *);
381c71bc7e2Stholo (*callbacks->write_output) (str, strlen (str));
382c71bc7e2Stholo break;
383c71bc7e2Stholo case 'd':
384c71bc7e2Stholo num = va_arg (args, int);
385c71bc7e2Stholo sprintf (buf, "%d", num);
386c71bc7e2Stholo (*callbacks->write_output) (buf, strlen (buf));
387c71bc7e2Stholo break;
388c71bc7e2Stholo case 'c':
389c71bc7e2Stholo ch = va_arg (args, int);
390c71bc7e2Stholo buf[0] = ch;
391c71bc7e2Stholo (*callbacks->write_output) (buf, 1);
392c71bc7e2Stholo break;
393c71bc7e2Stholo default:
394c71bc7e2Stholo (*callbacks->write_output) (msg, sizeof (msg) - 1);
395c71bc7e2Stholo /* Don't just keep going, because q + 1 might point to the
396c71bc7e2Stholo terminating '\0'. */
397c71bc7e2Stholo goto out;
398c71bc7e2Stholo }
399c71bc7e2Stholo p = q + 2;
400c71bc7e2Stholo }
401b2346922Stholo (*callbacks->write_output) (p, strlen (p));
402b2346922Stholo }
403b2346922Stholo else
404b2346922Stholo vfprintf (outfile, format, args);
405c71bc7e2Stholo out:
406b2346922Stholo va_end (args);
407b2346922Stholo }
408b2346922Stholo
409b2346922Stholo /* Flush the output file. */
410b2346922Stholo
411b2346922Stholo void
flush_output()412b2346922Stholo flush_output ()
413b2346922Stholo {
414b2346922Stholo if (callbacks && callbacks->flush_output)
415b2346922Stholo (*callbacks->flush_output) ();
416b2346922Stholo else
417b2346922Stholo fflush (outfile);
418b2346922Stholo }
4192286d8edStholo
4202286d8edStholo /* Compare two lines (typically one from each input file)
4212286d8edStholo according to the command line options.
4222286d8edStholo For efficiency, this is invoked only when the lines do not match exactly
4232286d8edStholo but an option like -i might cause us to ignore the difference.
4242286d8edStholo Return nonzero if the lines differ. */
4252286d8edStholo
4262286d8edStholo int
line_cmp(s1,s2)4272286d8edStholo line_cmp (s1, s2)
4282286d8edStholo char const *s1, *s2;
4292286d8edStholo {
4302286d8edStholo register unsigned char const *t1 = (unsigned char const *) s1;
4312286d8edStholo register unsigned char const *t2 = (unsigned char const *) s2;
4322286d8edStholo
4332286d8edStholo while (1)
4342286d8edStholo {
4352286d8edStholo register unsigned char c1 = *t1++;
4362286d8edStholo register unsigned char c2 = *t2++;
4372286d8edStholo
4382286d8edStholo /* Test for exact char equality first, since it's a common case. */
4392286d8edStholo if (c1 != c2)
4402286d8edStholo {
4412286d8edStholo /* Ignore horizontal white space if -b or -w is specified. */
4422286d8edStholo
4432286d8edStholo if (ignore_all_space_flag)
4442286d8edStholo {
4452286d8edStholo /* For -w, just skip past any white space. */
4462286d8edStholo while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
4472286d8edStholo while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
4482286d8edStholo }
4492286d8edStholo else if (ignore_space_change_flag)
4502286d8edStholo {
4512286d8edStholo /* For -b, advance past any sequence of white space in line 1
4522286d8edStholo and consider it just one Space, or nothing at all
4532286d8edStholo if it is at the end of the line. */
4542286d8edStholo if (ISSPACE (c1))
4552286d8edStholo {
4562286d8edStholo while (c1 != '\n')
4572286d8edStholo {
4582286d8edStholo c1 = *t1++;
4592286d8edStholo if (! ISSPACE (c1))
4602286d8edStholo {
4612286d8edStholo --t1;
4622286d8edStholo c1 = ' ';
4632286d8edStholo break;
4642286d8edStholo }
4652286d8edStholo }
4662286d8edStholo }
4672286d8edStholo
4682286d8edStholo /* Likewise for line 2. */
4692286d8edStholo if (ISSPACE (c2))
4702286d8edStholo {
4712286d8edStholo while (c2 != '\n')
4722286d8edStholo {
4732286d8edStholo c2 = *t2++;
4742286d8edStholo if (! ISSPACE (c2))
4752286d8edStholo {
4762286d8edStholo --t2;
4772286d8edStholo c2 = ' ';
4782286d8edStholo break;
4792286d8edStholo }
4802286d8edStholo }
4812286d8edStholo }
4822286d8edStholo
4832286d8edStholo if (c1 != c2)
4842286d8edStholo {
4852286d8edStholo /* If we went too far when doing the simple test
4862286d8edStholo for equality, go back to the first non-white-space
4872286d8edStholo character in both sides and try again. */
4882286d8edStholo if (c2 == ' ' && c1 != '\n'
4892286d8edStholo && (unsigned char const *) s1 + 1 < t1
4902286d8edStholo && ISSPACE(t1[-2]))
4912286d8edStholo {
4922286d8edStholo --t1;
4932286d8edStholo continue;
4942286d8edStholo }
4952286d8edStholo if (c1 == ' ' && c2 != '\n'
4962286d8edStholo && (unsigned char const *) s2 + 1 < t2
4972286d8edStholo && ISSPACE(t2[-2]))
4982286d8edStholo {
4992286d8edStholo --t2;
5002286d8edStholo continue;
5012286d8edStholo }
5022286d8edStholo }
5032286d8edStholo }
5042286d8edStholo
5052286d8edStholo /* Lowercase all letters if -i is specified. */
5062286d8edStholo
5072286d8edStholo if (ignore_case_flag)
5082286d8edStholo {
5092286d8edStholo if (ISUPPER (c1))
5102286d8edStholo c1 = tolower (c1);
5112286d8edStholo if (ISUPPER (c2))
5122286d8edStholo c2 = tolower (c2);
5132286d8edStholo }
5142286d8edStholo
5152286d8edStholo if (c1 != c2)
5162286d8edStholo break;
5172286d8edStholo }
5182286d8edStholo if (c1 == '\n')
5192286d8edStholo return 0;
5202286d8edStholo }
5212286d8edStholo
5222286d8edStholo return (1);
5232286d8edStholo }
5242286d8edStholo
5252286d8edStholo /* Find the consecutive changes at the start of the script START.
5262286d8edStholo Return the last link before the first gap. */
5272286d8edStholo
5282286d8edStholo struct change *
find_change(start)5292286d8edStholo find_change (start)
5302286d8edStholo struct change *start;
5312286d8edStholo {
5322286d8edStholo return start;
5332286d8edStholo }
5342286d8edStholo
5352286d8edStholo struct change *
find_reverse_change(start)5362286d8edStholo find_reverse_change (start)
5372286d8edStholo struct change *start;
5382286d8edStholo {
5392286d8edStholo return start;
5402286d8edStholo }
5412286d8edStholo
5422286d8edStholo /* Divide SCRIPT into pieces by calling HUNKFUN and
5432286d8edStholo print each piece with PRINTFUN.
5442286d8edStholo Both functions take one arg, an edit script.
5452286d8edStholo
5462286d8edStholo HUNKFUN is called with the tail of the script
5472286d8edStholo and returns the last link that belongs together with the start
5482286d8edStholo of the tail.
5492286d8edStholo
5502286d8edStholo PRINTFUN takes a subscript which belongs together (with a null
5512286d8edStholo link at the end) and prints it. */
5522286d8edStholo
5532286d8edStholo void
print_script(script,hunkfun,printfun)5542286d8edStholo print_script (script, hunkfun, printfun)
5552286d8edStholo struct change *script;
5562286d8edStholo struct change * (*hunkfun) PARAMS((struct change *));
5572286d8edStholo void (*printfun) PARAMS((struct change *));
5582286d8edStholo {
5592286d8edStholo struct change *next = script;
5602286d8edStholo
5612286d8edStholo while (next)
5622286d8edStholo {
5632286d8edStholo struct change *this, *end;
5642286d8edStholo
5652286d8edStholo /* Find a set of changes that belong together. */
5662286d8edStholo this = next;
5672286d8edStholo end = (*hunkfun) (next);
5682286d8edStholo
5692286d8edStholo /* Disconnect them from the rest of the changes,
5702286d8edStholo making them a hunk, and remember the rest for next iteration. */
5712286d8edStholo next = end->link;
5722286d8edStholo end->link = 0;
5732286d8edStholo #ifdef DEBUG
5742286d8edStholo debug_script (this);
5752286d8edStholo #endif
5762286d8edStholo
5772286d8edStholo /* Print this hunk. */
5782286d8edStholo (*printfun) (this);
5792286d8edStholo
5802286d8edStholo /* Reconnect the script so it will all be freed properly. */
5812286d8edStholo end->link = next;
5822286d8edStholo }
5832286d8edStholo }
5842286d8edStholo
5852286d8edStholo /* Print the text of a single line LINE,
5862286d8edStholo flagging it with the characters in LINE_FLAG (which say whether
5872286d8edStholo the line is inserted, deleted, changed, etc.). */
5882286d8edStholo
5892286d8edStholo void
print_1_line(line_flag,line)5902286d8edStholo print_1_line (line_flag, line)
5912286d8edStholo char const *line_flag;
5922286d8edStholo char const * const *line;
5932286d8edStholo {
5942286d8edStholo char const *text = line[0], *limit = line[1]; /* Help the compiler. */
5952286d8edStholo char const *flag_format = 0;
5962286d8edStholo
5972286d8edStholo /* If -T was specified, use a Tab between the line-flag and the text.
5982286d8edStholo Otherwise use a Space (as Unix diff does).
5992286d8edStholo Print neither space nor tab if line-flags are empty. */
6002286d8edStholo
6012286d8edStholo if (line_flag && *line_flag)
6022286d8edStholo {
6032286d8edStholo flag_format = tab_align_flag ? "%s\t" : "%s ";
604b2346922Stholo printf_output (flag_format, line_flag);
6052286d8edStholo }
6062286d8edStholo
6072286d8edStholo output_1_line (text, limit, flag_format, line_flag);
6082286d8edStholo
6092286d8edStholo if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
610b2346922Stholo printf_output ("\n\\ No newline at end of file\n");
6112286d8edStholo }
6122286d8edStholo
6132286d8edStholo /* Output a line from TEXT up to LIMIT. Without -t, output verbatim.
6142286d8edStholo With -t, expand white space characters to spaces, and if FLAG_FORMAT
6152286d8edStholo is nonzero, output it with argument LINE_FLAG after every
6162286d8edStholo internal carriage return, so that tab stops continue to line up. */
6172286d8edStholo
6182286d8edStholo void
output_1_line(text,limit,flag_format,line_flag)6192286d8edStholo output_1_line (text, limit, flag_format, line_flag)
6202286d8edStholo char const *text, *limit, *flag_format, *line_flag;
6212286d8edStholo {
6222286d8edStholo if (!tab_expand_flag)
623b2346922Stholo write_output (text, limit - text);
6242286d8edStholo else
6252286d8edStholo {
6262286d8edStholo register unsigned char c;
6272286d8edStholo register char const *t = text;
6282286d8edStholo register unsigned column = 0;
629b2346922Stholo /* CC is used to avoid taking the address of the register
630b2346922Stholo variable C. */
631b2346922Stholo char cc;
6322286d8edStholo
6332286d8edStholo while (t < limit)
6342286d8edStholo switch ((c = *t++))
6352286d8edStholo {
6362286d8edStholo case '\t':
6372286d8edStholo {
6382286d8edStholo unsigned spaces = TAB_WIDTH - column % TAB_WIDTH;
6392286d8edStholo column += spaces;
6402286d8edStholo do
641b2346922Stholo write_output (" ", 1);
6422286d8edStholo while (--spaces);
6432286d8edStholo }
6442286d8edStholo break;
6452286d8edStholo
6462286d8edStholo case '\r':
647b2346922Stholo write_output ("\r", 1);
6482286d8edStholo if (flag_format && t < limit && *t != '\n')
649b2346922Stholo printf_output (flag_format, line_flag);
6502286d8edStholo column = 0;
6512286d8edStholo break;
6522286d8edStholo
6532286d8edStholo case '\b':
6542286d8edStholo if (column == 0)
6552286d8edStholo continue;
6562286d8edStholo column--;
657b2346922Stholo write_output ("\b", 1);
6582286d8edStholo break;
6592286d8edStholo
6602286d8edStholo default:
6612286d8edStholo if (ISPRINT (c))
6622286d8edStholo column++;
663b2346922Stholo cc = c;
664b2346922Stholo write_output (&cc, 1);
6652286d8edStholo break;
6662286d8edStholo }
6672286d8edStholo }
6682286d8edStholo }
6692286d8edStholo
6702286d8edStholo int
change_letter(inserts,deletes)6712286d8edStholo change_letter (inserts, deletes)
6722286d8edStholo int inserts, deletes;
6732286d8edStholo {
6742286d8edStholo if (!inserts)
6752286d8edStholo return 'd';
6762286d8edStholo else if (!deletes)
6772286d8edStholo return 'a';
6782286d8edStholo else
6792286d8edStholo return 'c';
6802286d8edStholo }
6812286d8edStholo
6822286d8edStholo /* Translate an internal line number (an index into diff's table of lines)
6832286d8edStholo into an actual line number in the input file.
6842286d8edStholo The internal line number is LNUM. FILE points to the data on the file.
6852286d8edStholo
6862286d8edStholo Internal line numbers count from 0 starting after the prefix.
6872286d8edStholo Actual line numbers count from 1 within the entire file. */
6882286d8edStholo
6892286d8edStholo int
translate_line_number(file,lnum)6902286d8edStholo translate_line_number (file, lnum)
6912286d8edStholo struct file_data const *file;
6922286d8edStholo int lnum;
6932286d8edStholo {
6942286d8edStholo return lnum + file->prefix_lines + 1;
6952286d8edStholo }
6962286d8edStholo
6972286d8edStholo void
translate_range(file,a,b,aptr,bptr)6982286d8edStholo translate_range (file, a, b, aptr, bptr)
6992286d8edStholo struct file_data const *file;
7002286d8edStholo int a, b;
7012286d8edStholo int *aptr, *bptr;
7022286d8edStholo {
7032286d8edStholo *aptr = translate_line_number (file, a - 1) + 1;
7042286d8edStholo *bptr = translate_line_number (file, b + 1) - 1;
7052286d8edStholo }
7062286d8edStholo
7072286d8edStholo /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
7082286d8edStholo If the two numbers are identical, print just one number.
7092286d8edStholo
7102286d8edStholo Args A and B are internal line numbers.
7112286d8edStholo We print the translated (real) line numbers. */
7122286d8edStholo
7132286d8edStholo void
print_number_range(sepchar,file,a,b)7142286d8edStholo print_number_range (sepchar, file, a, b)
7152286d8edStholo int sepchar;
7162286d8edStholo struct file_data *file;
7172286d8edStholo int a, b;
7182286d8edStholo {
7192286d8edStholo int trans_a, trans_b;
7202286d8edStholo translate_range (file, a, b, &trans_a, &trans_b);
7212286d8edStholo
7222286d8edStholo /* Note: we can have B < A in the case of a range of no lines.
7232286d8edStholo In this case, we should print the line number before the range,
7242286d8edStholo which is B. */
7252286d8edStholo if (trans_b > trans_a)
726b2346922Stholo printf_output ("%d%c%d", trans_a, sepchar, trans_b);
7272286d8edStholo else
728b2346922Stholo printf_output ("%d", trans_b);
7292286d8edStholo }
7302286d8edStholo
7312286d8edStholo /* Look at a hunk of edit script and report the range of lines in each file
7322286d8edStholo that it applies to. HUNK is the start of the hunk, which is a chain
7332286d8edStholo of `struct change'. The first and last line numbers of file 0 are stored in
7342286d8edStholo *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
7352286d8edStholo Note that these are internal line numbers that count from 0.
7362286d8edStholo
7372286d8edStholo If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
7382286d8edStholo
7392286d8edStholo Also set *DELETES nonzero if any lines of file 0 are deleted
7402286d8edStholo and set *INSERTS nonzero if any lines of file 1 are inserted.
7412286d8edStholo If only ignorable lines are inserted or deleted, both are
7422286d8edStholo set to 0. */
7432286d8edStholo
7442286d8edStholo void
analyze_hunk(hunk,first0,last0,first1,last1,deletes,inserts)7452286d8edStholo analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
7462286d8edStholo struct change *hunk;
7472286d8edStholo int *first0, *last0, *first1, *last1;
7482286d8edStholo int *deletes, *inserts;
7492286d8edStholo {
7502286d8edStholo int l0, l1, show_from, show_to;
7512286d8edStholo int i;
7522286d8edStholo int trivial = ignore_blank_lines_flag || ignore_regexp_list;
7532286d8edStholo struct change *next;
7542286d8edStholo
7552286d8edStholo show_from = show_to = 0;
7562286d8edStholo
7572286d8edStholo *first0 = hunk->line0;
7582286d8edStholo *first1 = hunk->line1;
7592286d8edStholo
7602286d8edStholo next = hunk;
7612286d8edStholo do
7622286d8edStholo {
7632286d8edStholo l0 = next->line0 + next->deleted - 1;
7642286d8edStholo l1 = next->line1 + next->inserted - 1;
7652286d8edStholo show_from += next->deleted;
7662286d8edStholo show_to += next->inserted;
7672286d8edStholo
7682286d8edStholo for (i = next->line0; i <= l0 && trivial; i++)
7692286d8edStholo if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n')
7702286d8edStholo {
7712286d8edStholo struct regexp_list *r;
7722286d8edStholo char const *line = files[0].linbuf[i];
7732286d8edStholo int len = files[0].linbuf[i + 1] - line;
7742286d8edStholo
7752286d8edStholo for (r = ignore_regexp_list; r; r = r->next)
7762286d8edStholo if (0 <= re_search (&r->buf, line, len, 0, len, 0))
7772286d8edStholo break; /* Found a match. Ignore this line. */
7782286d8edStholo /* If we got all the way through the regexp list without
7792286d8edStholo finding a match, then it's nontrivial. */
7802286d8edStholo if (!r)
7812286d8edStholo trivial = 0;
7822286d8edStholo }
7832286d8edStholo
7842286d8edStholo for (i = next->line1; i <= l1 && trivial; i++)
7852286d8edStholo if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n')
7862286d8edStholo {
7872286d8edStholo struct regexp_list *r;
7882286d8edStholo char const *line = files[1].linbuf[i];
7892286d8edStholo int len = files[1].linbuf[i + 1] - line;
7902286d8edStholo
7912286d8edStholo for (r = ignore_regexp_list; r; r = r->next)
7922286d8edStholo if (0 <= re_search (&r->buf, line, len, 0, len, 0))
7932286d8edStholo break; /* Found a match. Ignore this line. */
7942286d8edStholo /* If we got all the way through the regexp list without
7952286d8edStholo finding a match, then it's nontrivial. */
7962286d8edStholo if (!r)
7972286d8edStholo trivial = 0;
7982286d8edStholo }
7992286d8edStholo }
8002286d8edStholo while ((next = next->link) != 0);
8012286d8edStholo
8022286d8edStholo *last0 = l0;
8032286d8edStholo *last1 = l1;
8042286d8edStholo
8052286d8edStholo /* If all inserted or deleted lines are ignorable,
8062286d8edStholo tell the caller to ignore this hunk. */
8072286d8edStholo
8082286d8edStholo if (trivial)
8092286d8edStholo show_from = show_to = 0;
8102286d8edStholo
8112286d8edStholo *deletes = show_from;
8122286d8edStholo *inserts = show_to;
8132286d8edStholo }
8142286d8edStholo
8152286d8edStholo /* Concatenate three strings, returning a newly malloc'd string. */
8162286d8edStholo
8172286d8edStholo char *
concat(s1,s2,s3)8182286d8edStholo concat (s1, s2, s3)
8192286d8edStholo char const *s1, *s2, *s3;
8202286d8edStholo {
8212286d8edStholo size_t len = strlen (s1) + strlen (s2) + strlen (s3);
8222286d8edStholo char *new = xmalloc (len + 1);
8232286d8edStholo sprintf (new, "%s%s%s", s1, s2, s3);
8242286d8edStholo return new;
8252286d8edStholo }
8262286d8edStholo
8272286d8edStholo /* Yield the newly malloc'd pathname
8282286d8edStholo of the file in DIR whose filename is FILE. */
8292286d8edStholo
8302286d8edStholo char *
dir_file_pathname(dir,file)8312286d8edStholo dir_file_pathname (dir, file)
8322286d8edStholo char const *dir, *file;
8332286d8edStholo {
8342286d8edStholo char const *p = filename_lastdirchar (dir);
8352286d8edStholo return concat (dir, "/" + (p && !p[1]), file);
8362286d8edStholo }
8372286d8edStholo
8382286d8edStholo void
debug_script(sp)8392286d8edStholo debug_script (sp)
8402286d8edStholo struct change *sp;
8412286d8edStholo {
8422286d8edStholo fflush (stdout);
8432286d8edStholo for (; sp; sp = sp->link)
8442286d8edStholo fprintf (stderr, "%3d %3d delete %d insert %d\n",
8452286d8edStholo sp->line0, sp->line1, sp->deleted, sp->inserted);
8462286d8edStholo fflush (stderr);
8472286d8edStholo }
848