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