1 /* GNU DIFF entry routine. 2 Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc. 3 4 This file is part of GNU DIFF. 5 6 GNU DIFF is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU DIFF is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 */ 17 18 /* GNU DIFF was written by Mike Haertel, David Hayes, 19 Richard Stallman, Len Tower, and Paul Eggert. */ 20 21 #define GDIFF_MAIN 22 #include "diff.h" 23 #include <signal.h> 24 #include "getopt.h" 25 #include "fnmatch.h" 26 27 #ifndef DEFAULT_WIDTH 28 #define DEFAULT_WIDTH 130 29 #endif 30 31 #ifndef GUTTER_WIDTH_MINIMUM 32 #define GUTTER_WIDTH_MINIMUM 3 33 #endif 34 35 /* diff.c has a real initialize_main function. */ 36 #ifdef initialize_main 37 #undef initialize_main 38 #endif 39 40 static char const *filetype PARAMS((struct stat const *)); 41 static char *option_list PARAMS((char **, int)); 42 static int add_exclude_file PARAMS((char const *)); 43 static int ck_atoi PARAMS((char const *, int *)); 44 static int compare_files PARAMS((char const *, char const *, char const *, char const *, int)); 45 static int specify_format PARAMS((char **, char *)); 46 static void add_exclude PARAMS((char const *)); 47 static void add_regexp PARAMS((struct regexp_list **, char const *)); 48 static void specify_style PARAMS((enum output_style)); 49 static int try_help PARAMS((char const *)); 50 static void check_output PARAMS((FILE *)); 51 static void usage PARAMS((void)); 52 static void initialize_main PARAMS((int *, char ***)); 53 54 /* Nonzero for -r: if comparing two directories, 55 compare their common subdirectories recursively. */ 56 57 static int recursive; 58 59 /* For debugging: don't do discard_confusing_lines. */ 60 61 int no_discards; 62 63 #if HAVE_SETMODE 64 /* I/O mode: nonzero only if using binary input/output. */ 65 static int binary_I_O; 66 #endif 67 68 /* Return a string containing the command options with which diff was invoked. 69 Spaces appear between what were separate ARGV-elements. 70 There is a space at the beginning but none at the end. 71 If there were no options, the result is an empty string. 72 73 Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT, 74 the length of that vector. */ 75 76 static char * 77 option_list (optionvec, count) 78 char **optionvec; /* Was `vector', but that collides on Alliant. */ 79 int count; 80 { 81 int i; 82 size_t length = 0; 83 char *result; 84 85 for (i = 0; i < count; i++) 86 length += strlen (optionvec[i]) + 1; 87 88 result = xmalloc (length + 1); 89 result[0] = 0; 90 91 for (i = 0; i < count; i++) 92 { 93 strcat (result, " "); 94 strcat (result, optionvec[i]); 95 } 96 97 return result; 98 } 99 100 /* Convert STR to a positive integer, storing the result in *OUT. 101 If STR is not a valid integer, return -1 (otherwise 0). */ 102 static int 103 ck_atoi (str, out) 104 char const *str; 105 int *out; 106 { 107 char const *p; 108 for (p = str; *p; p++) 109 if (*p < '0' || *p > '9') 110 return -1; 111 112 *out = atoi (optarg); 113 return 0; 114 } 115 116 /* Keep track of excluded file name patterns. */ 117 118 static char const **exclude; 119 static int exclude_alloc, exclude_count; 120 121 int 122 excluded_filename (f) 123 char const *f; 124 { 125 int i; 126 for (i = 0; i < exclude_count; i++) 127 if (fnmatch (exclude[i], f, 0) == 0) 128 return 1; 129 return 0; 130 } 131 132 static void 133 add_exclude (pattern) 134 char const *pattern; 135 { 136 if (exclude_alloc <= exclude_count) 137 exclude = (char const **) 138 (exclude_alloc == 0 139 ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude)) 140 : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude))); 141 142 exclude[exclude_count++] = pattern; 143 } 144 145 static int 146 add_exclude_file (name) 147 char const *name; 148 { 149 struct file_data f; 150 char *p, *q, *lim; 151 152 f.name = optarg; 153 f.desc = (strcmp (optarg, "-") == 0 154 ? STDIN_FILENO 155 : open (optarg, O_RDONLY, 0)); 156 if (f.desc < 0 || fstat (f.desc, &f.stat) != 0) 157 return -1; 158 159 sip (&f, 1); 160 slurp (&f); 161 162 for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q) 163 { 164 q = (char *) memchr (p, '\n', lim - p); 165 if (!q) 166 q = lim; 167 *q++ = 0; 168 add_exclude (p); 169 } 170 171 return close (f.desc); 172 } 173 174 /* The numbers 129- that appear in the fourth element of some entries 175 tell the big switch in `diff_run' how to process those options. */ 176 177 static struct option const longopts[] = 178 { 179 {"ignore-blank-lines", 0, 0, 'B'}, 180 {"context", 2, 0, 'C'}, 181 {"ifdef", 1, 0, 'D'}, 182 {"show-function-line", 1, 0, 'F'}, 183 {"speed-large-files", 0, 0, 'H'}, 184 {"ignore-matching-lines", 1, 0, 'I'}, 185 {"label", 1, 0, 'L'}, 186 {"file-label", 1, 0, 'L'}, /* An alias, no longer recommended */ 187 {"new-file", 0, 0, 'N'}, 188 {"entire-new-file", 0, 0, 'N'}, /* An alias, no longer recommended */ 189 {"unidirectional-new-file", 0, 0, 'P'}, 190 {"starting-file", 1, 0, 'S'}, 191 {"initial-tab", 0, 0, 'T'}, 192 {"width", 1, 0, 'W'}, 193 {"text", 0, 0, 'a'}, 194 {"ascii", 0, 0, 'a'}, /* An alias, no longer recommended */ 195 {"ignore-space-change", 0, 0, 'b'}, 196 {"minimal", 0, 0, 'd'}, 197 {"ed", 0, 0, 'e'}, 198 {"forward-ed", 0, 0, 'f'}, 199 {"ignore-case", 0, 0, 'i'}, 200 {"paginate", 0, 0, 'l'}, 201 {"print", 0, 0, 'l'}, /* An alias, no longer recommended */ 202 {"rcs", 0, 0, 'n'}, 203 {"show-c-function", 0, 0, 'p'}, 204 {"brief", 0, 0, 'q'}, 205 {"recursive", 0, 0, 'r'}, 206 {"report-identical-files", 0, 0, 's'}, 207 {"expand-tabs", 0, 0, 't'}, 208 {"version", 0, 0, 'v'}, 209 {"ignore-all-space", 0, 0, 'w'}, 210 {"exclude", 1, 0, 'x'}, 211 {"exclude-from", 1, 0, 'X'}, 212 {"side-by-side", 0, 0, 'y'}, 213 {"unified", 2, 0, 'U'}, 214 {"left-column", 0, 0, 129}, 215 {"suppress-common-lines", 0, 0, 130}, 216 {"sdiff-merge-assist", 0, 0, 131}, 217 {"old-line-format", 1, 0, 132}, 218 {"new-line-format", 1, 0, 133}, 219 {"unchanged-line-format", 1, 0, 134}, 220 {"line-format", 1, 0, 135}, 221 {"old-group-format", 1, 0, 136}, 222 {"new-group-format", 1, 0, 137}, 223 {"unchanged-group-format", 1, 0, 138}, 224 {"changed-group-format", 1, 0, 139}, 225 {"horizon-lines", 1, 0, 140}, 226 {"help", 0, 0, 141}, 227 {"binary", 0, 0, 142}, 228 {0, 0, 0, 0} 229 }; 230 231 int 232 diff_run (argc, argv, out, callbacks_arg) 233 int argc; 234 char *argv[]; 235 char *out; 236 const struct diff_callbacks *callbacks_arg; 237 { 238 int val; 239 int c; 240 int prev = -1; 241 int width = DEFAULT_WIDTH; 242 int show_c_function = 0; 243 int optind_old; 244 int opened_file = 0; 245 246 callbacks = callbacks_arg; 247 248 /* Do our initializations. */ 249 initialize_main (&argc, &argv); 250 251 /* Decode the options. */ 252 253 optind_old = optind; 254 optind = 0; 255 while ((c = getopt_long (argc, argv, 256 "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y", 257 longopts, 0)) != EOF) 258 { 259 switch (c) 260 { 261 /* All digits combine in decimal to specify the context-size. */ 262 case '1': 263 case '2': 264 case '3': 265 case '4': 266 case '5': 267 case '6': 268 case '7': 269 case '8': 270 case '9': 271 case '0': 272 if (context == -1) 273 context = 0; 274 /* If a context length has already been specified, 275 more digits allowed only if they follow right after the others. 276 Reject two separate runs of digits, or digits after -C. */ 277 else if (prev < '0' || prev > '9') 278 fatal ("context length specified twice"); 279 280 context = context * 10 + c - '0'; 281 break; 282 283 case 'a': 284 /* Treat all files as text files; never treat as binary. */ 285 always_text_flag = 1; 286 break; 287 288 case 'b': 289 /* Ignore changes in amount of white space. */ 290 ignore_space_change_flag = 1; 291 ignore_some_changes = 1; 292 ignore_some_line_changes = 1; 293 break; 294 295 case 'B': 296 /* Ignore changes affecting only blank lines. */ 297 ignore_blank_lines_flag = 1; 298 ignore_some_changes = 1; 299 break; 300 301 case 'C': /* +context[=lines] */ 302 case 'U': /* +unified[=lines] */ 303 if (optarg) 304 { 305 if (context >= 0) 306 fatal ("context length specified twice"); 307 308 if (ck_atoi (optarg, &context)) 309 fatal ("invalid context length argument"); 310 } 311 312 /* Falls through. */ 313 case 'c': 314 /* Make context-style output. */ 315 specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); 316 break; 317 318 case 'd': 319 /* Don't discard lines. This makes things slower (sometimes much 320 slower) but will find a guaranteed minimal set of changes. */ 321 no_discards = 1; 322 break; 323 324 case 'D': 325 /* Make merged #ifdef output. */ 326 specify_style (OUTPUT_IFDEF); 327 { 328 int i, err = 0; 329 static char const C_ifdef_group_formats[] = 330 "#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n"; 331 char *b = xmalloc (sizeof (C_ifdef_group_formats) 332 + 7 * strlen(optarg) - 14 /* 7*"%s" */ 333 - 8 /* 5*"%%" + 3*"%c" */); 334 sprintf (b, C_ifdef_group_formats, 335 optarg, optarg, 0, 336 optarg, optarg, 0, 0, 337 optarg, optarg, optarg); 338 for (i = 0; i < 4; i++) 339 { 340 err |= specify_format (&group_format[i], b); 341 b += strlen (b) + 1; 342 } 343 if (err) 344 diff_error ("conflicting #ifdef formats", 0, 0); 345 } 346 break; 347 348 case 'e': 349 /* Make output that is a valid `ed' script. */ 350 specify_style (OUTPUT_ED); 351 break; 352 353 case 'f': 354 /* Make output that looks vaguely like an `ed' script 355 but has changes in the order they appear in the file. */ 356 specify_style (OUTPUT_FORWARD_ED); 357 break; 358 359 case 'F': 360 /* Show, for each set of changes, the previous line that 361 matches the specified regexp. Currently affects only 362 context-style output. */ 363 add_regexp (&function_regexp_list, optarg); 364 break; 365 366 case 'h': 367 /* Split the files into chunks of around 1500 lines 368 for faster processing. Usually does not change the result. 369 370 This currently has no effect. */ 371 break; 372 373 case 'H': 374 /* Turn on heuristics that speed processing of large files 375 with a small density of changes. */ 376 heuristic = 1; 377 break; 378 379 case 'i': 380 /* Ignore changes in case. */ 381 ignore_case_flag = 1; 382 ignore_some_changes = 1; 383 ignore_some_line_changes = 1; 384 break; 385 386 case 'I': 387 /* Ignore changes affecting only lines that match the 388 specified regexp. */ 389 add_regexp (&ignore_regexp_list, optarg); 390 ignore_some_changes = 1; 391 break; 392 393 case 'l': 394 /* Pass the output through `pr' to paginate it. */ 395 paginate_flag = 1; 396 #if !defined(SIGCHLD) && defined(SIGCLD) 397 #define SIGCHLD SIGCLD 398 #endif 399 #ifdef SIGCHLD 400 /* Pagination requires forking and waiting, and 401 System V fork+wait does not work if SIGCHLD is ignored. */ 402 signal (SIGCHLD, SIG_DFL); 403 #endif 404 break; 405 406 case 'L': 407 /* Specify file labels for `-c' output headers. */ 408 if (!file_label[0]) 409 file_label[0] = optarg; 410 else if (!file_label[1]) 411 file_label[1] = optarg; 412 else 413 fatal ("too many file label options"); 414 break; 415 416 case 'n': 417 /* Output RCS-style diffs, like `-f' except that each command 418 specifies the number of lines affected. */ 419 specify_style (OUTPUT_RCS); 420 break; 421 422 case 'N': 423 /* When comparing directories, if a file appears only in one 424 directory, treat it as present but empty in the other. */ 425 entire_new_file_flag = 1; 426 break; 427 428 case 'p': 429 /* Make context-style output and show name of last C function. */ 430 show_c_function = 1; 431 add_regexp (&function_regexp_list, "^[_a-zA-Z$]"); 432 break; 433 434 case 'P': 435 /* When comparing directories, if a file appears only in 436 the second directory of the two, 437 treat it as present but empty in the other. */ 438 unidirectional_new_file_flag = 1; 439 break; 440 441 case 'q': 442 no_details_flag = 1; 443 break; 444 445 case 'r': 446 /* When comparing directories, 447 recursively compare any subdirectories found. */ 448 recursive = 1; 449 break; 450 451 case 's': 452 /* Print a message if the files are the same. */ 453 print_file_same_flag = 1; 454 break; 455 456 case 'S': 457 /* When comparing directories, start with the specified 458 file name. This is used for resuming an aborted comparison. */ 459 dir_start_file = optarg; 460 break; 461 462 case 't': 463 /* Expand tabs to spaces in the output so that it preserves 464 the alignment of the input files. */ 465 tab_expand_flag = 1; 466 break; 467 468 case 'T': 469 /* Use a tab in the output, rather than a space, before the 470 text of an input line, so as to keep the proper alignment 471 in the input line without changing the characters in it. */ 472 tab_align_flag = 1; 473 break; 474 475 case 'u': 476 /* Output the context diff in unidiff format. */ 477 specify_style (OUTPUT_UNIFIED); 478 break; 479 480 case 'v': 481 if (callbacks && callbacks->write_stdout) 482 { 483 (*callbacks->write_stdout) ("diff - GNU diffutils version "); 484 (*callbacks->write_stdout) (diff_version_string); 485 (*callbacks->write_stdout) ("\n"); 486 } 487 else 488 printf ("diff - GNU diffutils version %s\n", diff_version_string); 489 return 0; 490 491 case 'w': 492 /* Ignore horizontal white space when comparing lines. */ 493 ignore_all_space_flag = 1; 494 ignore_some_changes = 1; 495 ignore_some_line_changes = 1; 496 break; 497 498 case 'x': 499 add_exclude (optarg); 500 break; 501 502 case 'X': 503 if (add_exclude_file (optarg) != 0) 504 pfatal_with_name (optarg); 505 break; 506 507 case 'y': 508 /* Use side-by-side (sdiff-style) columnar output. */ 509 specify_style (OUTPUT_SDIFF); 510 break; 511 512 case 'W': 513 /* Set the line width for OUTPUT_SDIFF. */ 514 if (ck_atoi (optarg, &width) || width <= 0) 515 fatal ("column width must be a positive integer"); 516 break; 517 518 case 129: 519 sdiff_left_only = 1; 520 break; 521 522 case 130: 523 sdiff_skip_common_lines = 1; 524 break; 525 526 case 131: 527 /* sdiff-style columns output. */ 528 specify_style (OUTPUT_SDIFF); 529 sdiff_help_sdiff = 1; 530 break; 531 532 case 132: 533 case 133: 534 case 134: 535 specify_style (OUTPUT_IFDEF); 536 if (specify_format (&line_format[c - 132], optarg) != 0) 537 diff_error ("conflicting line format", 0, 0); 538 break; 539 540 case 135: 541 specify_style (OUTPUT_IFDEF); 542 { 543 int i, err = 0; 544 for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) 545 err |= specify_format (&line_format[i], optarg); 546 if (err) 547 diff_error ("conflicting line format", 0, 0); 548 } 549 break; 550 551 case 136: 552 case 137: 553 case 138: 554 case 139: 555 specify_style (OUTPUT_IFDEF); 556 if (specify_format (&group_format[c - 136], optarg) != 0) 557 diff_error ("conflicting group format", 0, 0); 558 break; 559 560 case 140: 561 if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0) 562 fatal ("horizon must be a nonnegative integer"); 563 break; 564 565 case 141: 566 usage (); 567 if (! callbacks || ! callbacks->write_stdout) 568 check_output (stdout); 569 return 0; 570 571 case 142: 572 /* Use binary I/O when reading and writing data. 573 On Posix hosts, this has no effect. */ 574 #if HAVE_SETMODE 575 binary_I_O = 1; 576 # if 0 577 /* Because this code is leftover from pre-library days, 578 there is no way to set stdout back to the default mode 579 when we are done. As it turns out, I think the only 580 parts of CVS that pass out == NULL, and thus cause diff 581 to write to stdout, are "cvs diff" and "cvs rdiff". So 582 I'm not going to worry about this too much yet. */ 583 setmode (STDOUT_FILENO, O_BINARY); 584 # else 585 if (out == NULL) 586 error (0, 0, "warning: did not set stdout to binary mode"); 587 # endif 588 #endif 589 break; 590 591 default: 592 return try_help (0); 593 } 594 prev = c; 595 } 596 597 if (argc - optind != 2) 598 return try_help (argc - optind < 2 ? "missing operand" : "extra operand"); 599 600 { 601 /* 602 * We maximize first the half line width, and then the gutter width, 603 * according to the following constraints: 604 * 1. Two half lines plus a gutter must fit in a line. 605 * 2. If the half line width is nonzero: 606 * a. The gutter width is at least GUTTER_WIDTH_MINIMUM. 607 * b. If tabs are not expanded to spaces, 608 * a half line plus a gutter is an integral number of tabs, 609 * so that tabs in the right column line up. 610 */ 611 int t = tab_expand_flag ? 1 : TAB_WIDTH; 612 int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t) * t; 613 sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)), 614 sdiff_column2_offset = sdiff_half_width ? off : width; 615 } 616 617 if (show_c_function && output_style != OUTPUT_UNIFIED) 618 specify_style (OUTPUT_CONTEXT); 619 620 if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED) 621 context = 0; 622 else if (context == -1) 623 /* Default amount of context for -c. */ 624 context = 3; 625 626 if (output_style == OUTPUT_IFDEF) 627 { 628 /* Format arrays are char *, not char const *, 629 because integer formats are temporarily modified. 630 But it is safe to assign a constant like "%=" to a format array, 631 since "%=" does not format any integers. */ 632 int i; 633 for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) 634 if (!line_format[i]) 635 line_format[i] = "%l\n"; 636 if (!group_format[OLD]) 637 group_format[OLD] 638 = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<"; 639 if (!group_format[NEW]) 640 group_format[NEW] 641 = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>"; 642 if (!group_format[UNCHANGED]) 643 group_format[UNCHANGED] = "%="; 644 if (!group_format[CHANGED]) 645 group_format[CHANGED] = concat (group_format[OLD], 646 group_format[NEW], ""); 647 } 648 649 no_diff_means_no_output = 650 (output_style == OUTPUT_IFDEF ? 651 (!*group_format[UNCHANGED] 652 || (strcmp (group_format[UNCHANGED], "%=") == 0 653 && !*line_format[UNCHANGED])) 654 : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1); 655 656 switch_string = option_list (argv + 1, optind - 1); 657 658 if (callbacks && callbacks->write_output) 659 { 660 if (out != NULL) 661 { 662 diff_error ("write callback with output file", 0, 0); 663 return 2; 664 } 665 } 666 else 667 { 668 if (out == NULL) 669 outfile = stdout; 670 else 671 { 672 #if HAVE_SETMODE 673 /* A diff which is full of ^Z and such isn't going to work 674 very well in text mode. */ 675 if (binary_I_O) 676 outfile = fopen (out, "wb"); 677 else 678 #endif 679 outfile = fopen (out, "w"); 680 if (outfile == NULL) 681 { 682 perror_with_name ("could not open output file"); 683 return 2; 684 } 685 opened_file = 1; 686 } 687 } 688 689 /* Set the jump buffer, so that diff may abort execution without 690 terminating the process. */ 691 val = setjmp (diff_abort_buf); 692 if (val != 0) 693 { 694 optind = optind_old; 695 if (opened_file) 696 fclose (outfile); 697 return val; 698 } 699 700 val = compare_files (0, argv[optind], 0, argv[optind + 1], 0); 701 702 /* Print any messages that were saved up for last. */ 703 print_message_queue (); 704 705 free (switch_string); 706 707 optind = optind_old; 708 709 if (! callbacks || ! callbacks->write_output) 710 check_output (outfile); 711 712 if (opened_file) 713 if (fclose (outfile) != 0) 714 perror_with_name ("close error on output file"); 715 716 return val; 717 } 718 719 /* Add the compiled form of regexp PATTERN to REGLIST. */ 720 721 static void 722 add_regexp (reglist, pattern) 723 struct regexp_list **reglist; 724 char const *pattern; 725 { 726 struct regexp_list *r; 727 char const *m; 728 729 r = (struct regexp_list *) xmalloc (sizeof (*r)); 730 bzero (r, sizeof (*r)); 731 r->buf.fastmap = xmalloc (256); 732 m = re_compile_pattern (pattern, strlen (pattern), &r->buf); 733 if (m != 0) 734 diff_error ("%s: %s", pattern, m); 735 736 /* Add to the start of the list, since it's easier than the end. */ 737 r->next = *reglist; 738 *reglist = r; 739 } 740 741 static int 742 try_help (reason) 743 char const *reason; 744 { 745 if (reason) 746 diff_error ("%s", reason, 0); 747 diff_error ("Try `%s --help' for more information.", diff_program_name, 0); 748 return 2; 749 } 750 751 static void 752 check_output (file) 753 FILE *file; 754 { 755 if (ferror (file) || fflush (file) != 0) 756 fatal ("write error"); 757 } 758 759 static char const * const option_help[] = { 760 "-i --ignore-case Consider upper- and lower-case to be the same.", 761 "-w --ignore-all-space Ignore all white space.", 762 "-b --ignore-space-change Ignore changes in the amount of white space.", 763 "-B --ignore-blank-lines Ignore changes whose lines are all blank.", 764 "-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.", 765 #if HAVE_SETMODE 766 "--binary Read and write data in binary mode.", 767 #endif 768 "-a --text Treat all files as text.\n", 769 "-c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.", 770 "-u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.", 771 " -NUM Use NUM context lines.", 772 " -L LABEL --label LABEL Use LABEL instead of file name.", 773 " -p --show-c-function Show which C function each change is in.", 774 " -F RE --show-function-line=RE Show the most recent line matching RE.", 775 "-q --brief Output only whether files differ.", 776 "-e --ed Output an ed script.", 777 "-n --rcs Output an RCS format diff.", 778 "-y --side-by-side Output in two columns.", 779 " -w NUM --width=NUM Output at most NUM (default 130) characters per line.", 780 " --left-column Output only the left column of common lines.", 781 " --suppress-common-lines Do not output common lines.", 782 "-DNAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.", 783 "--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.", 784 "--line-format=LFMT Similar, but format all input lines with LFMT.", 785 "--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.", 786 " LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.", 787 " GFMT may contain:", 788 " %< lines from FILE1", 789 " %> lines from FILE2", 790 " %= lines common to FILE1 and FILE2", 791 " %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER", 792 " LETTERs are as follows for new group, lower case for old group:", 793 " F first line number", 794 " L last line number", 795 " N number of lines = L-F+1", 796 " E F-1", 797 " M L+1", 798 " LFMT may contain:", 799 " %L contents of line", 800 " %l contents of line, excluding any trailing newline", 801 " %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number", 802 " Either GFMT or LFMT may contain:", 803 " %% %", 804 " %c'C' the single character C", 805 " %c'\\OOO' the character with octal code OOO\n", 806 "-l --paginate Pass the output through `pr' to paginate it.", 807 "-t --expand-tabs Expand tabs to spaces in output.", 808 "-T --initial-tab Make tabs line up by prepending a tab.\n", 809 "-r --recursive Recursively compare any subdirectories found.", 810 "-N --new-file Treat absent files as empty.", 811 "-P --unidirectional-new-file Treat absent first files as empty.", 812 "-s --report-identical-files Report when two files are the same.", 813 "-x PAT --exclude=PAT Exclude files that match PAT.", 814 "-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE.", 815 "-S FILE --starting-file=FILE Start with FILE when comparing directories.\n", 816 "--horizon-lines=NUM Keep NUM lines of the common prefix and suffix.", 817 "-d --minimal Try hard to find a smaller set of changes.", 818 "-H --speed-large-files Assume large files and many scattered small changes.\n", 819 "-v --version Output version info.", 820 "--help Output this help.", 821 0 822 }; 823 824 static void 825 usage () 826 { 827 char const * const *p; 828 829 if (callbacks && callbacks->write_stdout) 830 { 831 (*callbacks->write_stdout) ("Usage: "); 832 (*callbacks->write_stdout) (diff_program_name); 833 (*callbacks->write_stdout) (" [OPTION]... FILE1 FILE2\n\n"); 834 for (p = option_help; *p; p++) 835 { 836 (*callbacks->write_stdout) (" "); 837 (*callbacks->write_stdout) (*p); 838 (*callbacks->write_stdout) ("\n"); 839 } 840 (*callbacks->write_stdout) 841 ("\nIf FILE1 or FILE2 is `-', read standard input.\n"); 842 } 843 else 844 { 845 printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", diff_program_name); 846 for (p = option_help; *p; p++) 847 printf (" %s\n", *p); 848 printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n"); 849 } 850 } 851 852 static int 853 specify_format (var, value) 854 char **var; 855 char *value; 856 { 857 int err = *var ? strcmp (*var, value) : 0; 858 *var = value; 859 return err; 860 } 861 862 static void 863 specify_style (style) 864 enum output_style style; 865 { 866 if (output_style != OUTPUT_NORMAL 867 && output_style != style) 868 diff_error ("conflicting specifications of output style", 0, 0); 869 output_style = style; 870 } 871 872 static char const * 873 filetype (st) 874 struct stat const *st; 875 { 876 /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats. 877 To keep diagnostics grammatical, the returned string must start 878 with a consonant. */ 879 880 if (S_ISREG (st->st_mode)) 881 { 882 if (st->st_size == 0) 883 return "regular empty file"; 884 /* Posix.2 section 5.14.2 seems to suggest that we must read the file 885 and guess whether it's C, Fortran, etc., but this is somewhat useless 886 and doesn't reflect historical practice. We're allowed to guess 887 wrong, so we don't bother to read the file. */ 888 return "regular file"; 889 } 890 if (S_ISDIR (st->st_mode)) return "directory"; 891 892 /* other Posix.1 file types */ 893 #ifdef S_ISBLK 894 if (S_ISBLK (st->st_mode)) return "block special file"; 895 #endif 896 #ifdef S_ISCHR 897 if (S_ISCHR (st->st_mode)) return "character special file"; 898 #endif 899 #ifdef S_ISFIFO 900 if (S_ISFIFO (st->st_mode)) return "fifo"; 901 #endif 902 903 /* other Posix.1b file types */ 904 #ifdef S_TYPEISMQ 905 if (S_TYPEISMQ (st)) return "message queue"; 906 #endif 907 #ifdef S_TYPEISSEM 908 if (S_TYPEISSEM (st)) return "semaphore"; 909 #endif 910 #ifdef S_TYPEISSHM 911 if (S_TYPEISSHM (st)) return "shared memory object"; 912 #endif 913 914 /* other popular file types */ 915 /* S_ISLNK is impossible with `fstat' and `stat'. */ 916 #ifdef S_ISSOCK 917 if (S_ISSOCK (st->st_mode)) return "socket"; 918 #endif 919 920 return "weird file"; 921 } 922 923 /* Compare two files (or dirs) with specified names 924 DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion. 925 (if DIR0 is 0, then the name is just NAME0, etc.) 926 This is self-contained; it opens the files and closes them. 927 928 Value is 0 if files are the same, 1 if different, 929 2 if there is a problem opening them. */ 930 931 static int 932 compare_files (dir0, name0, dir1, name1, depth) 933 char const *dir0, *dir1; 934 char const *name0, *name1; 935 int depth; 936 { 937 struct file_data inf[2]; 938 register int i; 939 int val; 940 int same_files; 941 int failed = 0; 942 char *free0 = 0, *free1 = 0; 943 944 /* If this is directory comparison, perhaps we have a file 945 that exists only in one of the directories. 946 If so, just print a message to that effect. */ 947 948 if (! ((name0 != 0 && name1 != 0) 949 || (unidirectional_new_file_flag && name1 != 0) 950 || entire_new_file_flag)) 951 { 952 char const *name = name0 == 0 ? name1 : name0; 953 char const *dir = name0 == 0 ? dir1 : dir0; 954 message ("Only in %s: %s\n", dir, name); 955 /* Return 1 so that diff_dirs will return 1 ("some files differ"). */ 956 return 1; 957 } 958 959 bzero (inf, sizeof (inf)); 960 961 /* Mark any nonexistent file with -1 in the desc field. */ 962 /* Mark unopened files (e.g. directories) with -2. */ 963 964 inf[0].desc = name0 == 0 ? -1 : -2; 965 inf[1].desc = name1 == 0 ? -1 : -2; 966 967 /* Now record the full name of each file, including nonexistent ones. */ 968 969 if (name0 == 0) 970 name0 = name1; 971 if (name1 == 0) 972 name1 = name0; 973 974 inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0)); 975 inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1)); 976 977 /* Stat the files. Record whether they are directories. */ 978 979 for (i = 0; i <= 1; i++) 980 { 981 if (inf[i].desc != -1) 982 { 983 int stat_result; 984 985 if (i && filename_cmp (inf[i].name, inf[0].name) == 0) 986 { 987 inf[i].stat = inf[0].stat; 988 stat_result = 0; 989 } 990 else if (strcmp (inf[i].name, "-") == 0) 991 { 992 inf[i].desc = STDIN_FILENO; 993 stat_result = fstat (STDIN_FILENO, &inf[i].stat); 994 if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode)) 995 { 996 off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR); 997 if (pos == -1) 998 stat_result = -1; 999 else 1000 { 1001 if (pos <= inf[i].stat.st_size) 1002 inf[i].stat.st_size -= pos; 1003 else 1004 inf[i].stat.st_size = 0; 1005 /* Posix.2 4.17.6.1.4 requires current time for stdin. */ 1006 time (&inf[i].stat.st_mtime); 1007 } 1008 } 1009 } 1010 else 1011 stat_result = stat (inf[i].name, &inf[i].stat); 1012 1013 if (stat_result != 0) 1014 { 1015 perror_with_name (inf[i].name); 1016 failed = 1; 1017 } 1018 else 1019 { 1020 inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0; 1021 if (inf[1 - i].desc == -1) 1022 { 1023 inf[1 - i].dir_p = inf[i].dir_p; 1024 inf[1 - i].stat.st_mode = inf[i].stat.st_mode; 1025 } 1026 } 1027 } 1028 } 1029 1030 if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p) 1031 { 1032 /* If one is a directory, and it was specified in the command line, 1033 use the file in that dir with the other file's basename. */ 1034 1035 int fnm_arg = inf[0].dir_p; 1036 int dir_arg = 1 - fnm_arg; 1037 char const *fnm = inf[fnm_arg].name; 1038 char const *dir = inf[dir_arg].name; 1039 char const *p = filename_lastdirchar (fnm); 1040 char const *filename = inf[dir_arg].name 1041 = dir_file_pathname (dir, p ? p + 1 : fnm); 1042 1043 if (strcmp (fnm, "-") == 0) 1044 fatal ("can't compare - to a directory"); 1045 1046 if (stat (filename, &inf[dir_arg].stat) != 0) 1047 { 1048 perror_with_name (filename); 1049 failed = 1; 1050 } 1051 else 1052 inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode); 1053 } 1054 1055 if (failed) 1056 { 1057 1058 /* If either file should exist but does not, return 2. */ 1059 1060 val = 2; 1061 1062 } 1063 else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1 1064 && 0 < same_file (&inf[0].stat, &inf[1].stat)) 1065 && no_diff_means_no_output) 1066 { 1067 /* The two named files are actually the same physical file. 1068 We know they are identical without actually reading them. */ 1069 1070 val = 0; 1071 } 1072 else if (inf[0].dir_p & inf[1].dir_p) 1073 { 1074 if (output_style == OUTPUT_IFDEF) 1075 fatal ("-D option not supported with directories"); 1076 1077 /* If both are directories, compare the files in them. */ 1078 1079 if (depth > 0 && !recursive) 1080 { 1081 /* But don't compare dir contents one level down 1082 unless -r was specified. */ 1083 message ("Common subdirectories: %s and %s\n", 1084 inf[0].name, inf[1].name); 1085 val = 0; 1086 } 1087 else 1088 { 1089 val = diff_dirs (inf, compare_files, depth); 1090 } 1091 1092 } 1093 else if ((inf[0].dir_p | inf[1].dir_p) 1094 || (depth > 0 1095 && (! S_ISREG (inf[0].stat.st_mode) 1096 || ! S_ISREG (inf[1].stat.st_mode)))) 1097 { 1098 /* Perhaps we have a subdirectory that exists only in one directory. 1099 If so, just print a message to that effect. */ 1100 1101 if (inf[0].desc == -1 || inf[1].desc == -1) 1102 { 1103 if ((inf[0].dir_p | inf[1].dir_p) 1104 && recursive 1105 && (entire_new_file_flag 1106 || (unidirectional_new_file_flag && inf[0].desc == -1))) 1107 val = diff_dirs (inf, compare_files, depth); 1108 else 1109 { 1110 char const *dir = (inf[0].desc == -1) ? dir1 : dir0; 1111 /* See Posix.2 section 4.17.6.1.1 for this format. */ 1112 message ("Only in %s: %s\n", dir, name0); 1113 val = 1; 1114 } 1115 } 1116 else 1117 { 1118 /* We have two files that are not to be compared. */ 1119 1120 /* See Posix.2 section 4.17.6.1.1 for this format. */ 1121 message5 ("File %s is a %s while file %s is a %s\n", 1122 inf[0].name, filetype (&inf[0].stat), 1123 inf[1].name, filetype (&inf[1].stat)); 1124 1125 /* This is a difference. */ 1126 val = 1; 1127 } 1128 } 1129 else if ((no_details_flag & ~ignore_some_changes) 1130 && inf[0].stat.st_size != inf[1].stat.st_size 1131 && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode)) 1132 && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode))) 1133 { 1134 message ("Files %s and %s differ\n", inf[0].name, inf[1].name); 1135 val = 1; 1136 } 1137 else 1138 { 1139 /* Both exist and neither is a directory. */ 1140 1141 /* Open the files and record their descriptors. */ 1142 1143 if (inf[0].desc == -2) 1144 if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0) 1145 { 1146 perror_with_name (inf[0].name); 1147 failed = 1; 1148 } 1149 if (inf[1].desc == -2) 1150 if (same_files) 1151 inf[1].desc = inf[0].desc; 1152 else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0) 1153 { 1154 perror_with_name (inf[1].name); 1155 failed = 1; 1156 } 1157 1158 #if HAVE_SETMODE 1159 if (binary_I_O) 1160 for (i = 0; i <= 1; i++) 1161 if (0 <= inf[i].desc) 1162 setmode (inf[i].desc, O_BINARY); 1163 #endif 1164 1165 /* Compare the files, if no error was found. */ 1166 1167 val = failed ? 2 : diff_2_files (inf, depth); 1168 1169 /* Close the file descriptors. */ 1170 1171 if (inf[0].desc >= 0 && close (inf[0].desc) != 0) 1172 { 1173 perror_with_name (inf[0].name); 1174 val = 2; 1175 } 1176 if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc 1177 && close (inf[1].desc) != 0) 1178 { 1179 perror_with_name (inf[1].name); 1180 val = 2; 1181 } 1182 } 1183 1184 /* Now the comparison has been done, if no error prevented it, 1185 and VAL is the value this function will return. */ 1186 1187 if (val == 0 && !inf[0].dir_p) 1188 { 1189 if (print_file_same_flag) 1190 message ("Files %s and %s are identical\n", 1191 inf[0].name, inf[1].name); 1192 } 1193 else 1194 flush_output (); 1195 1196 if (free0) 1197 free (free0); 1198 if (free1) 1199 free (free1); 1200 1201 return val; 1202 } 1203 1204 /* Initialize status variables and flag variables used in libdiff, 1205 to permit repeated calls to diff_run. */ 1206 1207 static void 1208 initialize_main (argcp, argvp) 1209 int *argcp; 1210 char ***argvp; 1211 { 1212 /* These variables really must be reset each time diff_run is called. */ 1213 output_style = OUTPUT_NORMAL; 1214 context = -1; 1215 file_label[0] = NULL; 1216 file_label[1] = NULL; 1217 diff_program_name = (*argvp)[0]; 1218 outfile = NULL; 1219 1220 /* Reset these also, just for safety's sake. (If one invocation turns 1221 on ignore_case_flag, it must be turned off before diff_run is called 1222 again. But it is possible to make many diffs before encountering 1223 such a problem. */ 1224 recursive = 0; 1225 no_discards = 0; 1226 #if HAVE_SETMODE 1227 binary_I_O = 0; 1228 #endif 1229 no_diff_means_no_output = 0; 1230 always_text_flag = 0; 1231 horizon_lines = 0; 1232 ignore_space_change_flag = 0; 1233 ignore_all_space_flag = 0; 1234 ignore_blank_lines_flag = 0; 1235 ignore_some_line_changes = 0; 1236 ignore_some_changes = 0; 1237 ignore_case_flag = 0; 1238 function_regexp_list = NULL; 1239 ignore_regexp_list = NULL; 1240 no_details_flag = 0; 1241 print_file_same_flag = 0; 1242 tab_align_flag = 0; 1243 tab_expand_flag = 0; 1244 dir_start_file = NULL; 1245 entire_new_file_flag = 0; 1246 unidirectional_new_file_flag = 0; 1247 paginate_flag = 0; 1248 bzero (group_format, sizeof (group_format)); 1249 bzero (line_format, sizeof (line_format)); 1250 sdiff_help_sdiff = 0; 1251 sdiff_left_only = 0; 1252 sdiff_skip_common_lines = 0; 1253 sdiff_half_width = 0; 1254 sdiff_column2_offset = 0; 1255 switch_string = NULL; 1256 heuristic = 0; 1257 bzero (files, sizeof (files)); 1258 } 1259