1170754Sdelphij/* Support routines for GNU DIFF. 2170754Sdelphij 3170754Sdelphij Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002, 4170754Sdelphij 2004 Free Software Foundation, Inc. 5170754Sdelphij 6170754Sdelphij This file is part of GNU DIFF. 7170754Sdelphij 8170754Sdelphij GNU DIFF is free software; you can redistribute it and/or modify 9170754Sdelphij it under the terms of the GNU General Public License as published by 10170754Sdelphij the Free Software Foundation; either version 2, or (at your option) 11170754Sdelphij any later version. 12170754Sdelphij 13170754Sdelphij GNU DIFF is distributed in the hope that it will be useful, 14170754Sdelphij but WITHOUT ANY WARRANTY; without even the implied warranty of 15170754Sdelphij MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16170754Sdelphij GNU General Public License for more details. 17170754Sdelphij 18170754Sdelphij You should have received a copy of the GNU General Public License 19170754Sdelphij along with this program; see the file COPYING. 20170754Sdelphij If not, write to the Free Software Foundation, 21170754Sdelphij 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22170754Sdelphij 23170754Sdelphij#include "diff.h" 24170754Sdelphij#include <dirname.h> 25170754Sdelphij#include <error.h> 26170754Sdelphij#include <quotesys.h> 27170754Sdelphij#include <xalloc.h> 28170754Sdelphij 29170754Sdelphijchar const pr_program[] = PR_PROGRAM; 30170754Sdelphij 31170754Sdelphij/* Queue up one-line messages to be printed at the end, 32170754Sdelphij when -l is specified. Each message is recorded with a `struct msg'. */ 33170754Sdelphij 34170754Sdelphijstruct msg 35170754Sdelphij{ 36170754Sdelphij struct msg *next; 37170754Sdelphij char args[1]; /* Format + 4 args, each '\0' terminated, concatenated. */ 38170754Sdelphij}; 39170754Sdelphij 40170754Sdelphij/* Head of the chain of queues messages. */ 41170754Sdelphij 42170754Sdelphijstatic struct msg *msg_chain; 43170754Sdelphij 44170754Sdelphij/* Tail of the chain of queues messages. */ 45170754Sdelphij 46170754Sdelphijstatic struct msg **msg_chain_end = &msg_chain; 47170754Sdelphij 48170754Sdelphij/* Use when a system call returns non-zero status. 49170754Sdelphij NAME should normally be the file name. */ 50170754Sdelphij 51170754Sdelphijvoid 52170754Sdelphijperror_with_name (char const *name) 53170754Sdelphij{ 54170754Sdelphij error (0, errno, "%s", name); 55170754Sdelphij} 56170754Sdelphij 57170754Sdelphij/* Use when a system call returns non-zero status and that is fatal. */ 58170754Sdelphij 59170754Sdelphijvoid 60170754Sdelphijpfatal_with_name (char const *name) 61170754Sdelphij{ 62170754Sdelphij int e = errno; 63170754Sdelphij print_message_queue (); 64170754Sdelphij error (EXIT_TROUBLE, e, "%s", name); 65170754Sdelphij abort (); 66170754Sdelphij} 67170754Sdelphij 68170754Sdelphij/* Print an error message containing MSGID, then exit. */ 69170754Sdelphij 70170754Sdelphijvoid 71170754Sdelphijfatal (char const *msgid) 72170754Sdelphij{ 73170754Sdelphij print_message_queue (); 74170754Sdelphij error (EXIT_TROUBLE, 0, "%s", _(msgid)); 75170754Sdelphij abort (); 76170754Sdelphij} 77170754Sdelphij 78170754Sdelphij/* Like printf, except if -l in effect then save the message and print later. 79170754Sdelphij This is used for things like "Only in ...". */ 80170754Sdelphij 81170754Sdelphijvoid 82170754Sdelphijmessage (char const *format_msgid, char const *arg1, char const *arg2) 83170754Sdelphij{ 84170754Sdelphij message5 (format_msgid, arg1, arg2, 0, 0); 85170754Sdelphij} 86170754Sdelphij 87170754Sdelphijvoid 88170754Sdelphijmessage5 (char const *format_msgid, char const *arg1, char const *arg2, 89170754Sdelphij char const *arg3, char const *arg4) 90170754Sdelphij{ 91170754Sdelphij if (paginate) 92170754Sdelphij { 93170754Sdelphij char *p; 94170754Sdelphij char const *arg[5]; 95170754Sdelphij int i; 96170754Sdelphij size_t size[5]; 97170754Sdelphij size_t total_size = offsetof (struct msg, args); 98170754Sdelphij struct msg *new; 99170754Sdelphij 100170754Sdelphij arg[0] = format_msgid; 101170754Sdelphij arg[1] = arg1; 102170754Sdelphij arg[2] = arg2; 103170754Sdelphij arg[3] = arg3 ? arg3 : ""; 104170754Sdelphij arg[4] = arg4 ? arg4 : ""; 105170754Sdelphij 106170754Sdelphij for (i = 0; i < 5; i++) 107170754Sdelphij total_size += size[i] = strlen (arg[i]) + 1; 108170754Sdelphij 109170754Sdelphij new = xmalloc (total_size); 110170754Sdelphij 111170754Sdelphij for (i = 0, p = new->args; i < 5; p += size[i++]) 112170754Sdelphij memcpy (p, arg[i], size[i]); 113170754Sdelphij 114170754Sdelphij *msg_chain_end = new; 115170754Sdelphij new->next = 0; 116170754Sdelphij msg_chain_end = &new->next; 117170754Sdelphij } 118170754Sdelphij else 119170754Sdelphij { 120170754Sdelphij if (sdiff_merge_assist) 121170754Sdelphij putchar (' '); 122170754Sdelphij printf (_(format_msgid), arg1, arg2, arg3, arg4); 123170754Sdelphij } 124170754Sdelphij} 125170754Sdelphij 126170754Sdelphij/* Output all the messages that were saved up by calls to `message'. */ 127170754Sdelphij 128170754Sdelphijvoid 129170754Sdelphijprint_message_queue (void) 130170754Sdelphij{ 131170754Sdelphij char const *arg[5]; 132170754Sdelphij int i; 133170754Sdelphij struct msg *m = msg_chain; 134170754Sdelphij 135170754Sdelphij while (m) 136170754Sdelphij { 137170754Sdelphij struct msg *next = m->next; 138170754Sdelphij arg[0] = m->args; 139170754Sdelphij for (i = 0; i < 4; i++) 140170754Sdelphij arg[i + 1] = arg[i] + strlen (arg[i]) + 1; 141170754Sdelphij printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]); 142170754Sdelphij free (m); 143170754Sdelphij m = next; 144170754Sdelphij } 145170754Sdelphij} 146170754Sdelphij 147170754Sdelphij/* Call before outputting the results of comparing files NAME0 and NAME1 148170754Sdelphij to set up OUTFILE, the stdio stream for the output to go to. 149170754Sdelphij 150170754Sdelphij Usually, OUTFILE is just stdout. But when -l was specified 151170754Sdelphij we fork off a `pr' and make OUTFILE a pipe to it. 152170754Sdelphij `pr' then outputs to our stdout. */ 153170754Sdelphij 154170754Sdelphijstatic char const *current_name0; 155170754Sdelphijstatic char const *current_name1; 156170754Sdelphijstatic bool currently_recursive; 157170754Sdelphij 158170754Sdelphijvoid 159170754Sdelphijsetup_output (char const *name0, char const *name1, bool recursive) 160170754Sdelphij{ 161170754Sdelphij current_name0 = name0; 162170754Sdelphij current_name1 = name1; 163170754Sdelphij currently_recursive = recursive; 164170754Sdelphij outfile = 0; 165170754Sdelphij} 166170754Sdelphij 167170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK 168170754Sdelphijstatic pid_t pr_pid; 169170754Sdelphij#endif 170170754Sdelphij 171170754Sdelphijvoid 172170754Sdelphijbegin_output (void) 173170754Sdelphij{ 174170754Sdelphij char *name; 175170754Sdelphij 176170754Sdelphij if (outfile != 0) 177170754Sdelphij return; 178170754Sdelphij 179170754Sdelphij /* Construct the header of this piece of diff. */ 180170754Sdelphij name = xmalloc (strlen (current_name0) + strlen (current_name1) 181170754Sdelphij + strlen (switch_string) + 7); 182170754Sdelphij 183170754Sdelphij /* POSIX 1003.1-2001 specifies this format. But there are some bugs in 184170754Sdelphij the standard: it says that we must print only the last component 185170754Sdelphij of the pathnames, and it requires two spaces after "diff" if 186170754Sdelphij there are no options. These requirements are silly and do not 187170754Sdelphij match historical practice. */ 188170754Sdelphij sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1); 189170754Sdelphij 190170754Sdelphij if (paginate) 191170754Sdelphij { 192170754Sdelphij if (fflush (stdout) != 0) 193170754Sdelphij pfatal_with_name (_("write failed")); 194170754Sdelphij 195170754Sdelphij /* Make OUTFILE a pipe to a subsidiary `pr'. */ 196170754Sdelphij { 197170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK 198170754Sdelphij int pipes[2]; 199170754Sdelphij 200170754Sdelphij if (pipe (pipes) != 0) 201170754Sdelphij pfatal_with_name ("pipe"); 202170754Sdelphij 203170754Sdelphij pr_pid = vfork (); 204170754Sdelphij if (pr_pid < 0) 205170754Sdelphij pfatal_with_name ("fork"); 206170754Sdelphij 207170754Sdelphij if (pr_pid == 0) 208170754Sdelphij { 209170754Sdelphij close (pipes[1]); 210170754Sdelphij if (pipes[0] != STDIN_FILENO) 211170754Sdelphij { 212170754Sdelphij if (dup2 (pipes[0], STDIN_FILENO) < 0) 213170754Sdelphij pfatal_with_name ("dup2"); 214170754Sdelphij close (pipes[0]); 215170754Sdelphij } 216170754Sdelphij 217170754Sdelphij execl (pr_program, pr_program, "-h", name, (char *) 0); 218170754Sdelphij _exit (errno == ENOENT ? 127 : 126); 219170754Sdelphij } 220170754Sdelphij else 221170754Sdelphij { 222170754Sdelphij close (pipes[0]); 223170754Sdelphij outfile = fdopen (pipes[1], "w"); 224170754Sdelphij if (!outfile) 225170754Sdelphij pfatal_with_name ("fdopen"); 226170754Sdelphij } 227170754Sdelphij#else 228170754Sdelphij char *command = xmalloc (sizeof pr_program - 1 + 7 229170754Sdelphij + quote_system_arg ((char *) 0, name) + 1); 230170754Sdelphij char *p; 231170754Sdelphij sprintf (command, "%s -f -h ", pr_program); 232170754Sdelphij p = command + sizeof pr_program - 1 + 7; 233170754Sdelphij p += quote_system_arg (p, name); 234170754Sdelphij *p = 0; 235170754Sdelphij errno = 0; 236170754Sdelphij outfile = popen (command, "w"); 237170754Sdelphij if (!outfile) 238170754Sdelphij pfatal_with_name (command); 239170754Sdelphij free (command); 240170754Sdelphij#endif 241170754Sdelphij } 242170754Sdelphij } 243170754Sdelphij else 244170754Sdelphij { 245170754Sdelphij 246170754Sdelphij /* If -l was not specified, output the diff straight to `stdout'. */ 247170754Sdelphij 248170754Sdelphij outfile = stdout; 249170754Sdelphij 250170754Sdelphij /* If handling multiple files (because scanning a directory), 251170754Sdelphij print which files the following output is about. */ 252170754Sdelphij if (currently_recursive) 253170754Sdelphij printf ("%s\n", name); 254170754Sdelphij } 255170754Sdelphij 256170754Sdelphij free (name); 257170754Sdelphij 258170754Sdelphij /* A special header is needed at the beginning of context output. */ 259170754Sdelphij switch (output_style) 260170754Sdelphij { 261170754Sdelphij case OUTPUT_CONTEXT: 262170754Sdelphij print_context_header (files, false); 263170754Sdelphij break; 264170754Sdelphij 265170754Sdelphij case OUTPUT_UNIFIED: 266170754Sdelphij print_context_header (files, true); 267170754Sdelphij break; 268170754Sdelphij 269170754Sdelphij default: 270170754Sdelphij break; 271170754Sdelphij } 272170754Sdelphij} 273170754Sdelphij 274170754Sdelphij/* Call after the end of output of diffs for one file. 275170754Sdelphij Close OUTFILE and get rid of the `pr' subfork. */ 276170754Sdelphij 277170754Sdelphijvoid 278170754Sdelphijfinish_output (void) 279170754Sdelphij{ 280170754Sdelphij if (outfile != 0 && outfile != stdout) 281170754Sdelphij { 282170754Sdelphij int status; 283170754Sdelphij int wstatus; 284170754Sdelphij int werrno = 0; 285170754Sdelphij if (ferror (outfile)) 286170754Sdelphij fatal ("write failed"); 287170754Sdelphij#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 288170754Sdelphij wstatus = pclose (outfile); 289170754Sdelphij if (wstatus == -1) 290170754Sdelphij werrno = errno; 291170754Sdelphij#else 292170754Sdelphij if (fclose (outfile) != 0) 293170754Sdelphij pfatal_with_name (_("write failed")); 294170754Sdelphij if (waitpid (pr_pid, &wstatus, 0) < 0) 295170754Sdelphij pfatal_with_name ("waitpid"); 296170754Sdelphij#endif 297170754Sdelphij status = (! werrno && WIFEXITED (wstatus) 298170754Sdelphij ? WEXITSTATUS (wstatus) 299170754Sdelphij : INT_MAX); 300170754Sdelphij if (status) 301170754Sdelphij error (EXIT_TROUBLE, werrno, 302170754Sdelphij _(status == 126 303170754Sdelphij ? "subsidiary program `%s' could not be invoked" 304170754Sdelphij : status == 127 305170754Sdelphij ? "subsidiary program `%s' not found" 306170754Sdelphij : status == INT_MAX 307170754Sdelphij ? "subsidiary program `%s' failed" 308170754Sdelphij : "subsidiary program `%s' failed (exit status %d)"), 309170754Sdelphij pr_program, status); 310170754Sdelphij } 311170754Sdelphij 312170754Sdelphij outfile = 0; 313170754Sdelphij} 314170754Sdelphij 315170754Sdelphij/* Compare two lines (typically one from each input file) 316170754Sdelphij according to the command line options. 317170754Sdelphij For efficiency, this is invoked only when the lines do not match exactly 318170754Sdelphij but an option like -i might cause us to ignore the difference. 319170754Sdelphij Return nonzero if the lines differ. */ 320170754Sdelphij 321170754Sdelphijbool 322170754Sdelphijlines_differ (char const *s1, char const *s2) 323170754Sdelphij{ 324170754Sdelphij register char const *t1 = s1; 325170754Sdelphij register char const *t2 = s2; 326170754Sdelphij size_t column = 0; 327170754Sdelphij 328170754Sdelphij while (1) 329170754Sdelphij { 330170754Sdelphij register unsigned char c1 = *t1++; 331170754Sdelphij register unsigned char c2 = *t2++; 332170754Sdelphij 333170754Sdelphij /* Test for exact char equality first, since it's a common case. */ 334170754Sdelphij if (c1 != c2) 335170754Sdelphij { 336170754Sdelphij switch (ignore_white_space) 337170754Sdelphij { 338170754Sdelphij case IGNORE_ALL_SPACE: 339170754Sdelphij /* For -w, just skip past any white space. */ 340170754Sdelphij while (isspace (c1) && c1 != '\n') c1 = *t1++; 341170754Sdelphij while (isspace (c2) && c2 != '\n') c2 = *t2++; 342170754Sdelphij break; 343170754Sdelphij 344170754Sdelphij case IGNORE_SPACE_CHANGE: 345170754Sdelphij /* For -b, advance past any sequence of white space in 346170754Sdelphij line 1 and consider it just one space, or nothing at 347170754Sdelphij all if it is at the end of the line. */ 348170754Sdelphij if (isspace (c1)) 349170754Sdelphij { 350170754Sdelphij while (c1 != '\n') 351170754Sdelphij { 352170754Sdelphij c1 = *t1++; 353170754Sdelphij if (! isspace (c1)) 354170754Sdelphij { 355170754Sdelphij --t1; 356170754Sdelphij c1 = ' '; 357170754Sdelphij break; 358170754Sdelphij } 359170754Sdelphij } 360170754Sdelphij } 361170754Sdelphij 362170754Sdelphij /* Likewise for line 2. */ 363170754Sdelphij if (isspace (c2)) 364170754Sdelphij { 365170754Sdelphij while (c2 != '\n') 366170754Sdelphij { 367170754Sdelphij c2 = *t2++; 368170754Sdelphij if (! isspace (c2)) 369170754Sdelphij { 370170754Sdelphij --t2; 371170754Sdelphij c2 = ' '; 372170754Sdelphij break; 373170754Sdelphij } 374170754Sdelphij } 375170754Sdelphij } 376170754Sdelphij 377170754Sdelphij if (c1 != c2) 378170754Sdelphij { 379170754Sdelphij /* If we went too far when doing the simple test 380170754Sdelphij for equality, go back to the first non-white-space 381170754Sdelphij character in both sides and try again. */ 382170754Sdelphij if (c2 == ' ' && c1 != '\n' 383170754Sdelphij && s1 + 1 < t1 384170754Sdelphij && isspace ((unsigned char) t1[-2])) 385170754Sdelphij { 386170754Sdelphij --t1; 387170754Sdelphij continue; 388170754Sdelphij } 389170754Sdelphij if (c1 == ' ' && c2 != '\n' 390170754Sdelphij && s2 + 1 < t2 391170754Sdelphij && isspace ((unsigned char) t2[-2])) 392170754Sdelphij { 393170754Sdelphij --t2; 394170754Sdelphij continue; 395170754Sdelphij } 396170754Sdelphij } 397170754Sdelphij 398170754Sdelphij break; 399170754Sdelphij 400170754Sdelphij case IGNORE_TAB_EXPANSION: 401170754Sdelphij if ((c1 == ' ' && c2 == '\t') 402170754Sdelphij || (c1 == '\t' && c2 == ' ')) 403170754Sdelphij { 404170754Sdelphij size_t column2 = column; 405170754Sdelphij for (;; c1 = *t1++) 406170754Sdelphij { 407170754Sdelphij if (c1 == ' ') 408170754Sdelphij column++; 409170754Sdelphij else if (c1 == '\t') 410170754Sdelphij column += tabsize - column % tabsize; 411170754Sdelphij else 412170754Sdelphij break; 413170754Sdelphij } 414170754Sdelphij for (;; c2 = *t2++) 415170754Sdelphij { 416170754Sdelphij if (c2 == ' ') 417170754Sdelphij column2++; 418170754Sdelphij else if (c2 == '\t') 419170754Sdelphij column2 += tabsize - column2 % tabsize; 420170754Sdelphij else 421170754Sdelphij break; 422170754Sdelphij } 423170754Sdelphij if (column != column2) 424170754Sdelphij return true; 425170754Sdelphij } 426170754Sdelphij break; 427170754Sdelphij 428170754Sdelphij case IGNORE_NO_WHITE_SPACE: 429170754Sdelphij break; 430170754Sdelphij } 431170754Sdelphij 432170754Sdelphij /* Lowercase all letters if -i is specified. */ 433170754Sdelphij 434170754Sdelphij if (ignore_case) 435170754Sdelphij { 436170754Sdelphij c1 = tolower (c1); 437170754Sdelphij c2 = tolower (c2); 438170754Sdelphij } 439170754Sdelphij 440170754Sdelphij if (c1 != c2) 441170754Sdelphij break; 442170754Sdelphij } 443170754Sdelphij if (c1 == '\n') 444170754Sdelphij return false; 445170754Sdelphij 446170754Sdelphij column += c1 == '\t' ? tabsize - column % tabsize : 1; 447170754Sdelphij } 448170754Sdelphij 449170754Sdelphij return true; 450170754Sdelphij} 451170754Sdelphij 452170754Sdelphij/* Find the consecutive changes at the start of the script START. 453170754Sdelphij Return the last link before the first gap. */ 454170754Sdelphij 455170754Sdelphijstruct change * 456170754Sdelphijfind_change (struct change *start) 457170754Sdelphij{ 458170754Sdelphij return start; 459170754Sdelphij} 460170754Sdelphij 461170754Sdelphijstruct change * 462170754Sdelphijfind_reverse_change (struct change *start) 463170754Sdelphij{ 464170754Sdelphij return start; 465170754Sdelphij} 466170754Sdelphij 467170754Sdelphij/* Divide SCRIPT into pieces by calling HUNKFUN and 468170754Sdelphij print each piece with PRINTFUN. 469170754Sdelphij Both functions take one arg, an edit script. 470170754Sdelphij 471170754Sdelphij HUNKFUN is called with the tail of the script 472170754Sdelphij and returns the last link that belongs together with the start 473170754Sdelphij of the tail. 474170754Sdelphij 475170754Sdelphij PRINTFUN takes a subscript which belongs together (with a null 476170754Sdelphij link at the end) and prints it. */ 477170754Sdelphij 478170754Sdelphijvoid 479170754Sdelphijprint_script (struct change *script, 480170754Sdelphij struct change * (*hunkfun) (struct change *), 481170754Sdelphij void (*printfun) (struct change *)) 482170754Sdelphij{ 483170754Sdelphij struct change *next = script; 484170754Sdelphij 485170754Sdelphij while (next) 486170754Sdelphij { 487170754Sdelphij struct change *this, *end; 488170754Sdelphij 489170754Sdelphij /* Find a set of changes that belong together. */ 490170754Sdelphij this = next; 491170754Sdelphij end = (*hunkfun) (next); 492170754Sdelphij 493170754Sdelphij /* Disconnect them from the rest of the changes, 494170754Sdelphij making them a hunk, and remember the rest for next iteration. */ 495170754Sdelphij next = end->link; 496170754Sdelphij end->link = 0; 497170754Sdelphij#ifdef DEBUG 498170754Sdelphij debug_script (this); 499170754Sdelphij#endif 500170754Sdelphij 501170754Sdelphij /* Print this hunk. */ 502170754Sdelphij (*printfun) (this); 503170754Sdelphij 504170754Sdelphij /* Reconnect the script so it will all be freed properly. */ 505170754Sdelphij end->link = next; 506170754Sdelphij } 507170754Sdelphij} 508170754Sdelphij 509170754Sdelphij/* Print the text of a single line LINE, 510170754Sdelphij flagging it with the characters in LINE_FLAG (which say whether 511170754Sdelphij the line is inserted, deleted, changed, etc.). */ 512170754Sdelphij 513170754Sdelphijvoid 514170754Sdelphijprint_1_line (char const *line_flag, char const *const *line) 515170754Sdelphij{ 516170754Sdelphij char const *base = line[0], *limit = line[1]; /* Help the compiler. */ 517170754Sdelphij FILE *out = outfile; /* Help the compiler some more. */ 518170754Sdelphij char const *flag_format = 0; 519170754Sdelphij 520170754Sdelphij /* If -T was specified, use a Tab between the line-flag and the text. 521170754Sdelphij Otherwise use a Space (as Unix diff does). 522170754Sdelphij Print neither space nor tab if line-flags are empty. */ 523170754Sdelphij 524170754Sdelphij if (line_flag && *line_flag) 525170754Sdelphij { 526170754Sdelphij flag_format = initial_tab ? "%s\t" : "%s "; 527170754Sdelphij fprintf (out, flag_format, line_flag); 528170754Sdelphij } 529170754Sdelphij 530170754Sdelphij output_1_line (base, limit, flag_format, line_flag); 531170754Sdelphij 532170754Sdelphij if ((!line_flag || line_flag[0]) && limit[-1] != '\n') 533170754Sdelphij fprintf (out, "\n\\ %s\n", _("No newline at end of file")); 534170754Sdelphij} 535170754Sdelphij 536170754Sdelphij/* Output a line from BASE up to LIMIT. 537170754Sdelphij With -t, expand white space characters to spaces, and if FLAG_FORMAT 538170754Sdelphij is nonzero, output it with argument LINE_FLAG after every 539170754Sdelphij internal carriage return, so that tab stops continue to line up. */ 540170754Sdelphij 541170754Sdelphijvoid 542170754Sdelphijoutput_1_line (char const *base, char const *limit, char const *flag_format, 543170754Sdelphij char const *line_flag) 544170754Sdelphij{ 545170754Sdelphij if (!expand_tabs) 546170754Sdelphij fwrite (base, sizeof (char), limit - base, outfile); 547170754Sdelphij else 548170754Sdelphij { 549170754Sdelphij register FILE *out = outfile; 550170754Sdelphij register unsigned char c; 551170754Sdelphij register char const *t = base; 552170754Sdelphij register size_t column = 0; 553170754Sdelphij size_t tab_size = tabsize; 554170754Sdelphij 555170754Sdelphij while (t < limit) 556170754Sdelphij switch ((c = *t++)) 557170754Sdelphij { 558170754Sdelphij case '\t': 559170754Sdelphij { 560170754Sdelphij size_t spaces = tab_size - column % tab_size; 561170754Sdelphij column += spaces; 562170754Sdelphij do 563170754Sdelphij putc (' ', out); 564170754Sdelphij while (--spaces); 565170754Sdelphij } 566170754Sdelphij break; 567170754Sdelphij 568170754Sdelphij case '\r': 569170754Sdelphij putc (c, out); 570170754Sdelphij if (flag_format && t < limit && *t != '\n') 571170754Sdelphij fprintf (out, flag_format, line_flag); 572170754Sdelphij column = 0; 573170754Sdelphij break; 574170754Sdelphij 575170754Sdelphij case '\b': 576170754Sdelphij if (column == 0) 577170754Sdelphij continue; 578170754Sdelphij column--; 579170754Sdelphij putc (c, out); 580170754Sdelphij break; 581170754Sdelphij 582170754Sdelphij default: 583170754Sdelphij column += isprint (c) != 0; 584170754Sdelphij putc (c, out); 585170754Sdelphij break; 586170754Sdelphij } 587170754Sdelphij } 588170754Sdelphij} 589170754Sdelphij 590170754Sdelphijchar const change_letter[] = { 0, 'd', 'a', 'c' }; 591170754Sdelphij 592170754Sdelphij/* Translate an internal line number (an index into diff's table of lines) 593170754Sdelphij into an actual line number in the input file. 594170754Sdelphij The internal line number is I. FILE points to the data on the file. 595170754Sdelphij 596170754Sdelphij Internal line numbers count from 0 starting after the prefix. 597170754Sdelphij Actual line numbers count from 1 within the entire file. */ 598170754Sdelphij 599170754Sdelphijlin 600170754Sdelphijtranslate_line_number (struct file_data const *file, lin i) 601170754Sdelphij{ 602170754Sdelphij return i + file->prefix_lines + 1; 603170754Sdelphij} 604170754Sdelphij 605170754Sdelphij/* Translate a line number range. This is always done for printing, 606170754Sdelphij so for convenience translate to long int rather than lin, so that the 607170754Sdelphij caller can use printf with "%ld" without casting. */ 608170754Sdelphij 609170754Sdelphijvoid 610170754Sdelphijtranslate_range (struct file_data const *file, 611170754Sdelphij lin a, lin b, 612170754Sdelphij long int *aptr, long int *bptr) 613170754Sdelphij{ 614170754Sdelphij *aptr = translate_line_number (file, a - 1) + 1; 615170754Sdelphij *bptr = translate_line_number (file, b + 1) - 1; 616170754Sdelphij} 617170754Sdelphij 618170754Sdelphij/* Print a pair of line numbers with SEPCHAR, translated for file FILE. 619170754Sdelphij If the two numbers are identical, print just one number. 620170754Sdelphij 621170754Sdelphij Args A and B are internal line numbers. 622170754Sdelphij We print the translated (real) line numbers. */ 623170754Sdelphij 624170754Sdelphijvoid 625170754Sdelphijprint_number_range (char sepchar, struct file_data *file, lin a, lin b) 626170754Sdelphij{ 627170754Sdelphij long int trans_a, trans_b; 628170754Sdelphij translate_range (file, a, b, &trans_a, &trans_b); 629170754Sdelphij 630170754Sdelphij /* Note: we can have B < A in the case of a range of no lines. 631170754Sdelphij In this case, we should print the line number before the range, 632170754Sdelphij which is B. */ 633170754Sdelphij if (trans_b > trans_a) 634170754Sdelphij fprintf (outfile, "%ld%c%ld", trans_a, sepchar, trans_b); 635170754Sdelphij else 636170754Sdelphij fprintf (outfile, "%ld", trans_b); 637170754Sdelphij} 638170754Sdelphij 639170754Sdelphij/* Look at a hunk of edit script and report the range of lines in each file 640170754Sdelphij that it applies to. HUNK is the start of the hunk, which is a chain 641170754Sdelphij of `struct change'. The first and last line numbers of file 0 are stored in 642170754Sdelphij *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1. 643170754Sdelphij Note that these are internal line numbers that count from 0. 644170754Sdelphij 645170754Sdelphij If no lines from file 0 are deleted, then FIRST0 is LAST0+1. 646170754Sdelphij 647170754Sdelphij Return UNCHANGED if only ignorable lines are inserted or deleted, 648170754Sdelphij OLD if lines of file 0 are deleted, 649170754Sdelphij NEW if lines of file 1 are inserted, 650170754Sdelphij and CHANGED if both kinds of changes are found. */ 651170754Sdelphij 652170754Sdelphijenum changes 653170754Sdelphijanalyze_hunk (struct change *hunk, 654170754Sdelphij lin *first0, lin *last0, 655170754Sdelphij lin *first1, lin *last1) 656170754Sdelphij{ 657170754Sdelphij struct change *next; 658170754Sdelphij lin l0, l1; 659170754Sdelphij lin show_from, show_to; 660170754Sdelphij lin i; 661170754Sdelphij bool trivial = ignore_blank_lines || ignore_regexp.fastmap; 662170754Sdelphij size_t trivial_length = ignore_blank_lines - 1; 663170754Sdelphij /* If 0, ignore zero-length lines; 664170754Sdelphij if SIZE_MAX, do not ignore lines just because of their length. */ 665170754Sdelphij bool skip_leading_white_space = 666170754Sdelphij (ignore_blank_lines && IGNORE_SPACE_CHANGE <= ignore_white_space); 667170754Sdelphij 668170754Sdelphij char const * const *linbuf0 = files[0].linbuf; /* Help the compiler. */ 669170754Sdelphij char const * const *linbuf1 = files[1].linbuf; 670170754Sdelphij 671170754Sdelphij show_from = show_to = 0; 672170754Sdelphij 673170754Sdelphij *first0 = hunk->line0; 674170754Sdelphij *first1 = hunk->line1; 675170754Sdelphij 676170754Sdelphij next = hunk; 677170754Sdelphij do 678170754Sdelphij { 679170754Sdelphij l0 = next->line0 + next->deleted - 1; 680170754Sdelphij l1 = next->line1 + next->inserted - 1; 681170754Sdelphij show_from += next->deleted; 682170754Sdelphij show_to += next->inserted; 683170754Sdelphij 684170754Sdelphij for (i = next->line0; i <= l0 && trivial; i++) 685170754Sdelphij { 686170754Sdelphij char const *line = linbuf0[i]; 687170754Sdelphij char const *newline = linbuf0[i + 1] - 1; 688170754Sdelphij size_t len = newline - line; 689170754Sdelphij char const *p = line; 690170754Sdelphij if (skip_leading_white_space) 691170754Sdelphij while (isspace ((unsigned char) *p) && *p != '\n') 692170754Sdelphij p++; 693170754Sdelphij if (newline - p != trivial_length 694170754Sdelphij && (! ignore_regexp.fastmap 695170754Sdelphij || re_search (&ignore_regexp, line, len, 0, len, 0) < 0)) 696170754Sdelphij trivial = 0; 697170754Sdelphij } 698170754Sdelphij 699170754Sdelphij for (i = next->line1; i <= l1 && trivial; i++) 700170754Sdelphij { 701170754Sdelphij char const *line = linbuf1[i]; 702170754Sdelphij char const *newline = linbuf1[i + 1] - 1; 703170754Sdelphij size_t len = newline - line; 704170754Sdelphij char const *p = line; 705170754Sdelphij if (skip_leading_white_space) 706170754Sdelphij while (isspace ((unsigned char) *p) && *p != '\n') 707170754Sdelphij p++; 708170754Sdelphij if (newline - p != trivial_length 709170754Sdelphij && (! ignore_regexp.fastmap 710170754Sdelphij || re_search (&ignore_regexp, line, len, 0, len, 0) < 0)) 711170754Sdelphij trivial = 0; 712170754Sdelphij } 713170754Sdelphij } 714170754Sdelphij while ((next = next->link) != 0); 715170754Sdelphij 716170754Sdelphij *last0 = l0; 717170754Sdelphij *last1 = l1; 718170754Sdelphij 719170754Sdelphij /* If all inserted or deleted lines are ignorable, 720170754Sdelphij tell the caller to ignore this hunk. */ 721170754Sdelphij 722170754Sdelphij if (trivial) 723170754Sdelphij return UNCHANGED; 724170754Sdelphij 725170754Sdelphij return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED); 726170754Sdelphij} 727170754Sdelphij 728170754Sdelphij/* Concatenate three strings, returning a newly malloc'd string. */ 729170754Sdelphij 730170754Sdelphijchar * 731170754Sdelphijconcat (char const *s1, char const *s2, char const *s3) 732170754Sdelphij{ 733170754Sdelphij char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1); 734170754Sdelphij sprintf (new, "%s%s%s", s1, s2, s3); 735170754Sdelphij return new; 736170754Sdelphij} 737170754Sdelphij 738170754Sdelphij/* Yield a new block of SIZE bytes, initialized to zero. */ 739170754Sdelphij 740170754Sdelphijvoid * 741170754Sdelphijzalloc (size_t size) 742170754Sdelphij{ 743170754Sdelphij void *p = xmalloc (size); 744170754Sdelphij memset (p, 0, size); 745170754Sdelphij return p; 746170754Sdelphij} 747170754Sdelphij 748170754Sdelphij/* Yield the newly malloc'd pathname 749170754Sdelphij of the file in DIR whose filename is FILE. */ 750170754Sdelphij 751170754Sdelphijchar * 752170754Sdelphijdir_file_pathname (char const *dir, char const *file) 753170754Sdelphij{ 754170754Sdelphij char const *base = base_name (dir); 755170754Sdelphij bool omit_slash = !*base || base[strlen (base) - 1] == '/'; 756170754Sdelphij return concat (dir, "/" + omit_slash, file); 757170754Sdelphij} 758170754Sdelphij 759170754Sdelphijvoid 760170754Sdelphijdebug_script (struct change *sp) 761170754Sdelphij{ 762170754Sdelphij fflush (stdout); 763170754Sdelphij 764170754Sdelphij for (; sp; sp = sp->link) 765170754Sdelphij { 766170754Sdelphij long int line0 = sp->line0; 767170754Sdelphij long int line1 = sp->line1; 768170754Sdelphij long int deleted = sp->deleted; 769170754Sdelphij long int inserted = sp->inserted; 770170754Sdelphij fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n", 771170754Sdelphij line0, line1, deleted, inserted); 772170754Sdelphij } 773170754Sdelphij 774170754Sdelphij fflush (stderr); 775170754Sdelphij} 776