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