12286d8edStholo /* GNU DIFF entry routine. 2b2346922Stholo Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc. 32286d8edStholo 42286d8edStholo This file is part of GNU DIFF. 52286d8edStholo 62286d8edStholo GNU DIFF is free software; you can redistribute it and/or modify 72286d8edStholo it under the terms of the GNU General Public License as published by 82286d8edStholo the Free Software Foundation; either version 2, or (at your option) 92286d8edStholo any later version. 102286d8edStholo 112286d8edStholo GNU DIFF is distributed in the hope that it will be useful, 122286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of 132286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 142286d8edStholo GNU General Public License for more details. 152286d8edStholo 16c71bc7e2Stholo */ 172286d8edStholo 182286d8edStholo /* GNU DIFF was written by Mike Haertel, David Hayes, 192286d8edStholo Richard Stallman, Len Tower, and Paul Eggert. */ 202286d8edStholo 212286d8edStholo #define GDIFF_MAIN 222286d8edStholo #include "diff.h" 232286d8edStholo #include <signal.h> 242286d8edStholo #include "getopt.h" 252286d8edStholo #include "fnmatch.h" 262286d8edStholo 272286d8edStholo #ifndef DEFAULT_WIDTH 282286d8edStholo #define DEFAULT_WIDTH 130 292286d8edStholo #endif 302286d8edStholo 312286d8edStholo #ifndef GUTTER_WIDTH_MINIMUM 322286d8edStholo #define GUTTER_WIDTH_MINIMUM 3 332286d8edStholo #endif 342286d8edStholo 352286d8edStholo /* diff.c has a real initialize_main function. */ 362286d8edStholo #ifdef initialize_main 372286d8edStholo #undef initialize_main 382286d8edStholo #endif 392286d8edStholo 402286d8edStholo static char const *filetype PARAMS((struct stat const *)); 412286d8edStholo static char *option_list PARAMS((char **, int)); 422286d8edStholo static int add_exclude_file PARAMS((char const *)); 432286d8edStholo static int ck_atoi PARAMS((char const *, int *)); 442286d8edStholo static int compare_files PARAMS((char const *, char const *, char const *, char const *, int)); 452286d8edStholo static int specify_format PARAMS((char **, char *)); 462286d8edStholo static void add_exclude PARAMS((char const *)); 472286d8edStholo static void add_regexp PARAMS((struct regexp_list **, char const *)); 482286d8edStholo static void specify_style PARAMS((enum output_style)); 492286d8edStholo static int try_help PARAMS((char const *)); 502286d8edStholo static void check_output PARAMS((FILE *)); 512286d8edStholo static void usage PARAMS((void)); 522286d8edStholo static void initialize_main PARAMS((int *, char ***)); 532286d8edStholo 542286d8edStholo /* Nonzero for -r: if comparing two directories, 552286d8edStholo compare their common subdirectories recursively. */ 562286d8edStholo 572286d8edStholo static int recursive; 582286d8edStholo 592286d8edStholo /* For debugging: don't do discard_confusing_lines. */ 602286d8edStholo 612286d8edStholo int no_discards; 622286d8edStholo 632286d8edStholo #if HAVE_SETMODE 642286d8edStholo /* I/O mode: nonzero only if using binary input/output. */ 652286d8edStholo static int binary_I_O; 662286d8edStholo #endif 672286d8edStholo 682286d8edStholo /* Return a string containing the command options with which diff was invoked. 692286d8edStholo Spaces appear between what were separate ARGV-elements. 702286d8edStholo There is a space at the beginning but none at the end. 712286d8edStholo If there were no options, the result is an empty string. 722286d8edStholo 732286d8edStholo Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT, 742286d8edStholo the length of that vector. */ 752286d8edStholo 762286d8edStholo static char * 772286d8edStholo option_list (optionvec, count) 782286d8edStholo char **optionvec; /* Was `vector', but that collides on Alliant. */ 792286d8edStholo int count; 802286d8edStholo { 812286d8edStholo int i; 822286d8edStholo size_t length = 0; 832286d8edStholo char *result; 842286d8edStholo 852286d8edStholo for (i = 0; i < count; i++) 862286d8edStholo length += strlen (optionvec[i]) + 1; 872286d8edStholo 882286d8edStholo result = xmalloc (length + 1); 892286d8edStholo result[0] = 0; 902286d8edStholo 912286d8edStholo for (i = 0; i < count; i++) 922286d8edStholo { 932286d8edStholo strcat (result, " "); 942286d8edStholo strcat (result, optionvec[i]); 952286d8edStholo } 962286d8edStholo 972286d8edStholo return result; 982286d8edStholo } 992286d8edStholo 1002286d8edStholo /* Convert STR to a positive integer, storing the result in *OUT. 1012286d8edStholo If STR is not a valid integer, return -1 (otherwise 0). */ 1022286d8edStholo static int 1032286d8edStholo ck_atoi (str, out) 1042286d8edStholo char const *str; 1052286d8edStholo int *out; 1062286d8edStholo { 1072286d8edStholo char const *p; 1082286d8edStholo for (p = str; *p; p++) 1092286d8edStholo if (*p < '0' || *p > '9') 1102286d8edStholo return -1; 1112286d8edStholo 1122286d8edStholo *out = atoi (optarg); 1132286d8edStholo return 0; 1142286d8edStholo } 1152286d8edStholo 1162286d8edStholo /* Keep track of excluded file name patterns. */ 1172286d8edStholo 1182286d8edStholo static char const **exclude; 1192286d8edStholo static int exclude_alloc, exclude_count; 1202286d8edStholo 1212286d8edStholo int 1222286d8edStholo excluded_filename (f) 1232286d8edStholo char const *f; 1242286d8edStholo { 1252286d8edStholo int i; 1262286d8edStholo for (i = 0; i < exclude_count; i++) 1272286d8edStholo if (fnmatch (exclude[i], f, 0) == 0) 1282286d8edStholo return 1; 1292286d8edStholo return 0; 1302286d8edStholo } 1312286d8edStholo 1322286d8edStholo static void 1332286d8edStholo add_exclude (pattern) 1342286d8edStholo char const *pattern; 1352286d8edStholo { 1362286d8edStholo if (exclude_alloc <= exclude_count) 1372286d8edStholo exclude = (char const **) 1382286d8edStholo (exclude_alloc == 0 1392286d8edStholo ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude)) 1402286d8edStholo : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude))); 1412286d8edStholo 1422286d8edStholo exclude[exclude_count++] = pattern; 1432286d8edStholo } 1442286d8edStholo 1452286d8edStholo static int 1462286d8edStholo add_exclude_file (name) 1472286d8edStholo char const *name; 1482286d8edStholo { 1492286d8edStholo struct file_data f; 1502286d8edStholo char *p, *q, *lim; 1512286d8edStholo 1522286d8edStholo f.name = optarg; 1532286d8edStholo f.desc = (strcmp (optarg, "-") == 0 1542286d8edStholo ? STDIN_FILENO 1552286d8edStholo : open (optarg, O_RDONLY, 0)); 1562286d8edStholo if (f.desc < 0 || fstat (f.desc, &f.stat) != 0) 1572286d8edStholo return -1; 1582286d8edStholo 1592286d8edStholo sip (&f, 1); 1602286d8edStholo slurp (&f); 1612286d8edStholo 1622286d8edStholo for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q) 1632286d8edStholo { 1642286d8edStholo q = (char *) memchr (p, '\n', lim - p); 1652286d8edStholo if (!q) 1662286d8edStholo q = lim; 1672286d8edStholo *q++ = 0; 1682286d8edStholo add_exclude (p); 1692286d8edStholo } 1702286d8edStholo 1712286d8edStholo return close (f.desc); 1722286d8edStholo } 1732286d8edStholo 1742286d8edStholo /* The numbers 129- that appear in the fourth element of some entries 1752286d8edStholo tell the big switch in `diff_run' how to process those options. */ 1762286d8edStholo 1772286d8edStholo static struct option const longopts[] = 1782286d8edStholo { 1792286d8edStholo {"ignore-blank-lines", 0, 0, 'B'}, 1802286d8edStholo {"context", 2, 0, 'C'}, 1812286d8edStholo {"ifdef", 1, 0, 'D'}, 1822286d8edStholo {"show-function-line", 1, 0, 'F'}, 1832286d8edStholo {"speed-large-files", 0, 0, 'H'}, 1842286d8edStholo {"ignore-matching-lines", 1, 0, 'I'}, 1852286d8edStholo {"label", 1, 0, 'L'}, 1862286d8edStholo {"file-label", 1, 0, 'L'}, /* An alias, no longer recommended */ 1872286d8edStholo {"new-file", 0, 0, 'N'}, 1882286d8edStholo {"entire-new-file", 0, 0, 'N'}, /* An alias, no longer recommended */ 1892286d8edStholo {"unidirectional-new-file", 0, 0, 'P'}, 1902286d8edStholo {"starting-file", 1, 0, 'S'}, 1912286d8edStholo {"initial-tab", 0, 0, 'T'}, 1922286d8edStholo {"width", 1, 0, 'W'}, 1932286d8edStholo {"text", 0, 0, 'a'}, 1942286d8edStholo {"ascii", 0, 0, 'a'}, /* An alias, no longer recommended */ 1952286d8edStholo {"ignore-space-change", 0, 0, 'b'}, 1962286d8edStholo {"minimal", 0, 0, 'd'}, 1972286d8edStholo {"ed", 0, 0, 'e'}, 1982286d8edStholo {"forward-ed", 0, 0, 'f'}, 1992286d8edStholo {"ignore-case", 0, 0, 'i'}, 2002286d8edStholo {"paginate", 0, 0, 'l'}, 2012286d8edStholo {"print", 0, 0, 'l'}, /* An alias, no longer recommended */ 2022286d8edStholo {"rcs", 0, 0, 'n'}, 2032286d8edStholo {"show-c-function", 0, 0, 'p'}, 2042286d8edStholo {"brief", 0, 0, 'q'}, 2052286d8edStholo {"recursive", 0, 0, 'r'}, 2062286d8edStholo {"report-identical-files", 0, 0, 's'}, 2072286d8edStholo {"expand-tabs", 0, 0, 't'}, 2082286d8edStholo {"version", 0, 0, 'v'}, 2092286d8edStholo {"ignore-all-space", 0, 0, 'w'}, 2102286d8edStholo {"exclude", 1, 0, 'x'}, 2112286d8edStholo {"exclude-from", 1, 0, 'X'}, 2122286d8edStholo {"side-by-side", 0, 0, 'y'}, 2132286d8edStholo {"unified", 2, 0, 'U'}, 2142286d8edStholo {"left-column", 0, 0, 129}, 2152286d8edStholo {"suppress-common-lines", 0, 0, 130}, 2162286d8edStholo {"sdiff-merge-assist", 0, 0, 131}, 2172286d8edStholo {"old-line-format", 1, 0, 132}, 2182286d8edStholo {"new-line-format", 1, 0, 133}, 2192286d8edStholo {"unchanged-line-format", 1, 0, 134}, 2202286d8edStholo {"line-format", 1, 0, 135}, 2212286d8edStholo {"old-group-format", 1, 0, 136}, 2222286d8edStholo {"new-group-format", 1, 0, 137}, 2232286d8edStholo {"unchanged-group-format", 1, 0, 138}, 2242286d8edStholo {"changed-group-format", 1, 0, 139}, 2252286d8edStholo {"horizon-lines", 1, 0, 140}, 2262286d8edStholo {"help", 0, 0, 141}, 2272286d8edStholo {"binary", 0, 0, 142}, 2282286d8edStholo {0, 0, 0, 0} 2292286d8edStholo }; 2302286d8edStholo 2312286d8edStholo int 232b2346922Stholo diff_run (argc, argv, out, callbacks_arg) 2332286d8edStholo int argc; 2342286d8edStholo char *argv[]; 2352286d8edStholo char *out; 236b2346922Stholo const struct diff_callbacks *callbacks_arg; 2372286d8edStholo { 2382286d8edStholo int val; 2392286d8edStholo int c; 2402286d8edStholo int prev = -1; 2412286d8edStholo int width = DEFAULT_WIDTH; 2422286d8edStholo int show_c_function = 0; 2432286d8edStholo int optind_old; 244b2346922Stholo int opened_file = 0; 245b2346922Stholo 246b2346922Stholo callbacks = callbacks_arg; 2472286d8edStholo 2482286d8edStholo /* Do our initializations. */ 2492286d8edStholo initialize_main (&argc, &argv); 2502286d8edStholo optind_old = optind; 2512286d8edStholo optind = 0; 252*e77048c1Stholo 253*e77048c1Stholo /* Set the jump buffer, so that diff may abort execution without 254*e77048c1Stholo terminating the process. */ 255*e77048c1Stholo val = setjmp (diff_abort_buf); 256*e77048c1Stholo if (val != 0) 257*e77048c1Stholo { 258*e77048c1Stholo optind = optind_old; 259*e77048c1Stholo if (opened_file) 260*e77048c1Stholo fclose (outfile); 261*e77048c1Stholo return val; 262*e77048c1Stholo } 263*e77048c1Stholo 264*e77048c1Stholo /* Decode the options. */ 2652286d8edStholo while ((c = getopt_long (argc, argv, 2662286d8edStholo "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y", 2672286d8edStholo longopts, 0)) != EOF) 2682286d8edStholo { 2692286d8edStholo switch (c) 2702286d8edStholo { 2712286d8edStholo /* All digits combine in decimal to specify the context-size. */ 2722286d8edStholo case '1': 2732286d8edStholo case '2': 2742286d8edStholo case '3': 2752286d8edStholo case '4': 2762286d8edStholo case '5': 2772286d8edStholo case '6': 2782286d8edStholo case '7': 2792286d8edStholo case '8': 2802286d8edStholo case '9': 2812286d8edStholo case '0': 2822286d8edStholo if (context == -1) 2832286d8edStholo context = 0; 2842286d8edStholo /* If a context length has already been specified, 2852286d8edStholo more digits allowed only if they follow right after the others. 2862286d8edStholo Reject two separate runs of digits, or digits after -C. */ 2872286d8edStholo else if (prev < '0' || prev > '9') 2882286d8edStholo fatal ("context length specified twice"); 2892286d8edStholo 2902286d8edStholo context = context * 10 + c - '0'; 2912286d8edStholo break; 2922286d8edStholo 2932286d8edStholo case 'a': 2942286d8edStholo /* Treat all files as text files; never treat as binary. */ 2952286d8edStholo always_text_flag = 1; 2962286d8edStholo break; 2972286d8edStholo 2982286d8edStholo case 'b': 2992286d8edStholo /* Ignore changes in amount of white space. */ 3002286d8edStholo ignore_space_change_flag = 1; 3012286d8edStholo ignore_some_changes = 1; 3022286d8edStholo ignore_some_line_changes = 1; 3032286d8edStholo break; 3042286d8edStholo 3052286d8edStholo case 'B': 3062286d8edStholo /* Ignore changes affecting only blank lines. */ 3072286d8edStholo ignore_blank_lines_flag = 1; 3082286d8edStholo ignore_some_changes = 1; 3092286d8edStholo break; 3102286d8edStholo 3112286d8edStholo case 'C': /* +context[=lines] */ 3122286d8edStholo case 'U': /* +unified[=lines] */ 3132286d8edStholo if (optarg) 3142286d8edStholo { 3152286d8edStholo if (context >= 0) 3162286d8edStholo fatal ("context length specified twice"); 3172286d8edStholo 3182286d8edStholo if (ck_atoi (optarg, &context)) 3192286d8edStholo fatal ("invalid context length argument"); 3202286d8edStholo } 3212286d8edStholo 3222286d8edStholo /* Falls through. */ 3232286d8edStholo case 'c': 3242286d8edStholo /* Make context-style output. */ 3252286d8edStholo specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); 3262286d8edStholo break; 3272286d8edStholo 3282286d8edStholo case 'd': 3292286d8edStholo /* Don't discard lines. This makes things slower (sometimes much 3302286d8edStholo slower) but will find a guaranteed minimal set of changes. */ 3312286d8edStholo no_discards = 1; 3322286d8edStholo break; 3332286d8edStholo 3342286d8edStholo case 'D': 3352286d8edStholo /* Make merged #ifdef output. */ 3362286d8edStholo specify_style (OUTPUT_IFDEF); 3372286d8edStholo { 3382286d8edStholo int i, err = 0; 3392286d8edStholo static char const C_ifdef_group_formats[] = 3402286d8edStholo "#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"; 3412286d8edStholo char *b = xmalloc (sizeof (C_ifdef_group_formats) 3422286d8edStholo + 7 * strlen(optarg) - 14 /* 7*"%s" */ 3432286d8edStholo - 8 /* 5*"%%" + 3*"%c" */); 3442286d8edStholo sprintf (b, C_ifdef_group_formats, 3452286d8edStholo optarg, optarg, 0, 3462286d8edStholo optarg, optarg, 0, 0, 3472286d8edStholo optarg, optarg, optarg); 3482286d8edStholo for (i = 0; i < 4; i++) 3492286d8edStholo { 3502286d8edStholo err |= specify_format (&group_format[i], b); 3512286d8edStholo b += strlen (b) + 1; 3522286d8edStholo } 3532286d8edStholo if (err) 3542286d8edStholo diff_error ("conflicting #ifdef formats", 0, 0); 3552286d8edStholo } 3562286d8edStholo break; 3572286d8edStholo 3582286d8edStholo case 'e': 3592286d8edStholo /* Make output that is a valid `ed' script. */ 3602286d8edStholo specify_style (OUTPUT_ED); 3612286d8edStholo break; 3622286d8edStholo 3632286d8edStholo case 'f': 3642286d8edStholo /* Make output that looks vaguely like an `ed' script 3652286d8edStholo but has changes in the order they appear in the file. */ 3662286d8edStholo specify_style (OUTPUT_FORWARD_ED); 3672286d8edStholo break; 3682286d8edStholo 3692286d8edStholo case 'F': 3702286d8edStholo /* Show, for each set of changes, the previous line that 3712286d8edStholo matches the specified regexp. Currently affects only 3722286d8edStholo context-style output. */ 3732286d8edStholo add_regexp (&function_regexp_list, optarg); 3742286d8edStholo break; 3752286d8edStholo 3762286d8edStholo case 'h': 3772286d8edStholo /* Split the files into chunks of around 1500 lines 3782286d8edStholo for faster processing. Usually does not change the result. 3792286d8edStholo 3802286d8edStholo This currently has no effect. */ 3812286d8edStholo break; 3822286d8edStholo 3832286d8edStholo case 'H': 3842286d8edStholo /* Turn on heuristics that speed processing of large files 3852286d8edStholo with a small density of changes. */ 3862286d8edStholo heuristic = 1; 3872286d8edStholo break; 3882286d8edStholo 3892286d8edStholo case 'i': 3902286d8edStholo /* Ignore changes in case. */ 3912286d8edStholo ignore_case_flag = 1; 3922286d8edStholo ignore_some_changes = 1; 3932286d8edStholo ignore_some_line_changes = 1; 3942286d8edStholo break; 3952286d8edStholo 3962286d8edStholo case 'I': 3972286d8edStholo /* Ignore changes affecting only lines that match the 3982286d8edStholo specified regexp. */ 3992286d8edStholo add_regexp (&ignore_regexp_list, optarg); 4002286d8edStholo ignore_some_changes = 1; 4012286d8edStholo break; 4022286d8edStholo 4032286d8edStholo case 'l': 4042286d8edStholo /* Pass the output through `pr' to paginate it. */ 4052286d8edStholo paginate_flag = 1; 4062286d8edStholo #if !defined(SIGCHLD) && defined(SIGCLD) 4072286d8edStholo #define SIGCHLD SIGCLD 4082286d8edStholo #endif 4092286d8edStholo #ifdef SIGCHLD 4102286d8edStholo /* Pagination requires forking and waiting, and 4112286d8edStholo System V fork+wait does not work if SIGCHLD is ignored. */ 4122286d8edStholo signal (SIGCHLD, SIG_DFL); 4132286d8edStholo #endif 4142286d8edStholo break; 4152286d8edStholo 4162286d8edStholo case 'L': 4172286d8edStholo /* Specify file labels for `-c' output headers. */ 4182286d8edStholo if (!file_label[0]) 4192286d8edStholo file_label[0] = optarg; 4202286d8edStholo else if (!file_label[1]) 4212286d8edStholo file_label[1] = optarg; 4222286d8edStholo else 4232286d8edStholo fatal ("too many file label options"); 4242286d8edStholo break; 4252286d8edStholo 4262286d8edStholo case 'n': 4272286d8edStholo /* Output RCS-style diffs, like `-f' except that each command 4282286d8edStholo specifies the number of lines affected. */ 4292286d8edStholo specify_style (OUTPUT_RCS); 4302286d8edStholo break; 4312286d8edStholo 4322286d8edStholo case 'N': 4332286d8edStholo /* When comparing directories, if a file appears only in one 4342286d8edStholo directory, treat it as present but empty in the other. */ 4352286d8edStholo entire_new_file_flag = 1; 4362286d8edStholo break; 4372286d8edStholo 4382286d8edStholo case 'p': 4392286d8edStholo /* Make context-style output and show name of last C function. */ 4402286d8edStholo show_c_function = 1; 4412286d8edStholo add_regexp (&function_regexp_list, "^[_a-zA-Z$]"); 4422286d8edStholo break; 4432286d8edStholo 4442286d8edStholo case 'P': 4452286d8edStholo /* When comparing directories, if a file appears only in 4462286d8edStholo the second directory of the two, 4472286d8edStholo treat it as present but empty in the other. */ 4482286d8edStholo unidirectional_new_file_flag = 1; 4492286d8edStholo break; 4502286d8edStholo 4512286d8edStholo case 'q': 4522286d8edStholo no_details_flag = 1; 4532286d8edStholo break; 4542286d8edStholo 4552286d8edStholo case 'r': 4562286d8edStholo /* When comparing directories, 4572286d8edStholo recursively compare any subdirectories found. */ 4582286d8edStholo recursive = 1; 4592286d8edStholo break; 4602286d8edStholo 4612286d8edStholo case 's': 4622286d8edStholo /* Print a message if the files are the same. */ 4632286d8edStholo print_file_same_flag = 1; 4642286d8edStholo break; 4652286d8edStholo 4662286d8edStholo case 'S': 4672286d8edStholo /* When comparing directories, start with the specified 4682286d8edStholo file name. This is used for resuming an aborted comparison. */ 4692286d8edStholo dir_start_file = optarg; 4702286d8edStholo break; 4712286d8edStholo 4722286d8edStholo case 't': 4732286d8edStholo /* Expand tabs to spaces in the output so that it preserves 4742286d8edStholo the alignment of the input files. */ 4752286d8edStholo tab_expand_flag = 1; 4762286d8edStholo break; 4772286d8edStholo 4782286d8edStholo case 'T': 4792286d8edStholo /* Use a tab in the output, rather than a space, before the 4802286d8edStholo text of an input line, so as to keep the proper alignment 4812286d8edStholo in the input line without changing the characters in it. */ 4822286d8edStholo tab_align_flag = 1; 4832286d8edStholo break; 4842286d8edStholo 4852286d8edStholo case 'u': 4862286d8edStholo /* Output the context diff in unidiff format. */ 4872286d8edStholo specify_style (OUTPUT_UNIFIED); 4882286d8edStholo break; 4892286d8edStholo 4902286d8edStholo case 'v': 491b2346922Stholo if (callbacks && callbacks->write_stdout) 492b2346922Stholo { 493b2346922Stholo (*callbacks->write_stdout) ("diff - GNU diffutils version "); 494b2346922Stholo (*callbacks->write_stdout) (diff_version_string); 495b2346922Stholo (*callbacks->write_stdout) ("\n"); 496b2346922Stholo } 497b2346922Stholo else 4982286d8edStholo printf ("diff - GNU diffutils version %s\n", diff_version_string); 4992286d8edStholo return 0; 5002286d8edStholo 5012286d8edStholo case 'w': 5022286d8edStholo /* Ignore horizontal white space when comparing lines. */ 5032286d8edStholo ignore_all_space_flag = 1; 5042286d8edStholo ignore_some_changes = 1; 5052286d8edStholo ignore_some_line_changes = 1; 5062286d8edStholo break; 5072286d8edStholo 5082286d8edStholo case 'x': 5092286d8edStholo add_exclude (optarg); 5102286d8edStholo break; 5112286d8edStholo 5122286d8edStholo case 'X': 5132286d8edStholo if (add_exclude_file (optarg) != 0) 5142286d8edStholo pfatal_with_name (optarg); 5152286d8edStholo break; 5162286d8edStholo 5172286d8edStholo case 'y': 5182286d8edStholo /* Use side-by-side (sdiff-style) columnar output. */ 5192286d8edStholo specify_style (OUTPUT_SDIFF); 5202286d8edStholo break; 5212286d8edStholo 5222286d8edStholo case 'W': 5232286d8edStholo /* Set the line width for OUTPUT_SDIFF. */ 5242286d8edStholo if (ck_atoi (optarg, &width) || width <= 0) 5252286d8edStholo fatal ("column width must be a positive integer"); 5262286d8edStholo break; 5272286d8edStholo 5282286d8edStholo case 129: 5292286d8edStholo sdiff_left_only = 1; 5302286d8edStholo break; 5312286d8edStholo 5322286d8edStholo case 130: 5332286d8edStholo sdiff_skip_common_lines = 1; 5342286d8edStholo break; 5352286d8edStholo 5362286d8edStholo case 131: 5372286d8edStholo /* sdiff-style columns output. */ 5382286d8edStholo specify_style (OUTPUT_SDIFF); 5392286d8edStholo sdiff_help_sdiff = 1; 5402286d8edStholo break; 5412286d8edStholo 5422286d8edStholo case 132: 5432286d8edStholo case 133: 5442286d8edStholo case 134: 5452286d8edStholo specify_style (OUTPUT_IFDEF); 5462286d8edStholo if (specify_format (&line_format[c - 132], optarg) != 0) 5472286d8edStholo diff_error ("conflicting line format", 0, 0); 5482286d8edStholo break; 5492286d8edStholo 5502286d8edStholo case 135: 5512286d8edStholo specify_style (OUTPUT_IFDEF); 5522286d8edStholo { 5532286d8edStholo int i, err = 0; 5542286d8edStholo for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) 5552286d8edStholo err |= specify_format (&line_format[i], optarg); 5562286d8edStholo if (err) 5572286d8edStholo diff_error ("conflicting line format", 0, 0); 5582286d8edStholo } 5592286d8edStholo break; 5602286d8edStholo 5612286d8edStholo case 136: 5622286d8edStholo case 137: 5632286d8edStholo case 138: 5642286d8edStholo case 139: 5652286d8edStholo specify_style (OUTPUT_IFDEF); 5662286d8edStholo if (specify_format (&group_format[c - 136], optarg) != 0) 5672286d8edStholo diff_error ("conflicting group format", 0, 0); 5682286d8edStholo break; 5692286d8edStholo 5702286d8edStholo case 140: 5712286d8edStholo if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0) 5722286d8edStholo fatal ("horizon must be a nonnegative integer"); 5732286d8edStholo break; 5742286d8edStholo 5752286d8edStholo case 141: 5762286d8edStholo usage (); 577b2346922Stholo if (! callbacks || ! callbacks->write_stdout) 5782286d8edStholo check_output (stdout); 5792286d8edStholo return 0; 5802286d8edStholo 5812286d8edStholo case 142: 5822286d8edStholo /* Use binary I/O when reading and writing data. 5832286d8edStholo On Posix hosts, this has no effect. */ 5842286d8edStholo #if HAVE_SETMODE 5852286d8edStholo binary_I_O = 1; 5862286d8edStholo # if 0 5872286d8edStholo /* Because this code is leftover from pre-library days, 5882286d8edStholo there is no way to set stdout back to the default mode 5892286d8edStholo when we are done. As it turns out, I think the only 5902286d8edStholo parts of CVS that pass out == NULL, and thus cause diff 5912286d8edStholo to write to stdout, are "cvs diff" and "cvs rdiff". So 5922286d8edStholo I'm not going to worry about this too much yet. */ 5932286d8edStholo setmode (STDOUT_FILENO, O_BINARY); 5942286d8edStholo # else 5952286d8edStholo if (out == NULL) 5962286d8edStholo error (0, 0, "warning: did not set stdout to binary mode"); 5972286d8edStholo # endif 5982286d8edStholo #endif 5992286d8edStholo break; 6002286d8edStholo 6012286d8edStholo default: 6022286d8edStholo return try_help (0); 6032286d8edStholo } 6042286d8edStholo prev = c; 6052286d8edStholo } 6062286d8edStholo 6072286d8edStholo if (argc - optind != 2) 6082286d8edStholo return try_help (argc - optind < 2 ? "missing operand" : "extra operand"); 6092286d8edStholo 6102286d8edStholo { 6112286d8edStholo /* 6122286d8edStholo * We maximize first the half line width, and then the gutter width, 6132286d8edStholo * according to the following constraints: 6142286d8edStholo * 1. Two half lines plus a gutter must fit in a line. 6152286d8edStholo * 2. If the half line width is nonzero: 6162286d8edStholo * a. The gutter width is at least GUTTER_WIDTH_MINIMUM. 6172286d8edStholo * b. If tabs are not expanded to spaces, 6182286d8edStholo * a half line plus a gutter is an integral number of tabs, 6192286d8edStholo * so that tabs in the right column line up. 6202286d8edStholo */ 6212286d8edStholo int t = tab_expand_flag ? 1 : TAB_WIDTH; 6222286d8edStholo int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t) * t; 6232286d8edStholo sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)), 6242286d8edStholo sdiff_column2_offset = sdiff_half_width ? off : width; 6252286d8edStholo } 6262286d8edStholo 6272286d8edStholo if (show_c_function && output_style != OUTPUT_UNIFIED) 6282286d8edStholo specify_style (OUTPUT_CONTEXT); 6292286d8edStholo 6302286d8edStholo if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED) 6312286d8edStholo context = 0; 6322286d8edStholo else if (context == -1) 6332286d8edStholo /* Default amount of context for -c. */ 6342286d8edStholo context = 3; 6352286d8edStholo 6362286d8edStholo if (output_style == OUTPUT_IFDEF) 6372286d8edStholo { 6382286d8edStholo /* Format arrays are char *, not char const *, 6392286d8edStholo because integer formats are temporarily modified. 6402286d8edStholo But it is safe to assign a constant like "%=" to a format array, 6412286d8edStholo since "%=" does not format any integers. */ 6422286d8edStholo int i; 6432286d8edStholo for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) 6442286d8edStholo if (!line_format[i]) 6452286d8edStholo line_format[i] = "%l\n"; 6462286d8edStholo if (!group_format[OLD]) 6472286d8edStholo group_format[OLD] 6482286d8edStholo = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<"; 6492286d8edStholo if (!group_format[NEW]) 6502286d8edStholo group_format[NEW] 6512286d8edStholo = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>"; 6522286d8edStholo if (!group_format[UNCHANGED]) 6532286d8edStholo group_format[UNCHANGED] = "%="; 6542286d8edStholo if (!group_format[CHANGED]) 6552286d8edStholo group_format[CHANGED] = concat (group_format[OLD], 6562286d8edStholo group_format[NEW], ""); 6572286d8edStholo } 6582286d8edStholo 6592286d8edStholo no_diff_means_no_output = 6602286d8edStholo (output_style == OUTPUT_IFDEF ? 6612286d8edStholo (!*group_format[UNCHANGED] 6622286d8edStholo || (strcmp (group_format[UNCHANGED], "%=") == 0 6632286d8edStholo && !*line_format[UNCHANGED])) 6642286d8edStholo : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1); 6652286d8edStholo 6662286d8edStholo switch_string = option_list (argv + 1, optind - 1); 6672286d8edStholo 668b2346922Stholo if (callbacks && callbacks->write_output) 669b2346922Stholo { 670b2346922Stholo if (out != NULL) 671b2346922Stholo { 672b2346922Stholo diff_error ("write callback with output file", 0, 0); 673b2346922Stholo return 2; 674b2346922Stholo } 675b2346922Stholo } 676b2346922Stholo else 677b2346922Stholo { 6782286d8edStholo if (out == NULL) 6792286d8edStholo outfile = stdout; 6802286d8edStholo else 6812286d8edStholo { 6822286d8edStholo #if HAVE_SETMODE 6832286d8edStholo /* A diff which is full of ^Z and such isn't going to work 6842286d8edStholo very well in text mode. */ 6852286d8edStholo if (binary_I_O) 6862286d8edStholo outfile = fopen (out, "wb"); 6872286d8edStholo else 6882286d8edStholo #endif 6892286d8edStholo outfile = fopen (out, "w"); 6902286d8edStholo if (outfile == NULL) 6912286d8edStholo { 6922286d8edStholo perror_with_name ("could not open output file"); 6932286d8edStholo return 2; 6942286d8edStholo } 695b2346922Stholo opened_file = 1; 696b2346922Stholo } 6972286d8edStholo } 6982286d8edStholo 6992286d8edStholo val = compare_files (0, argv[optind], 0, argv[optind + 1], 0); 7002286d8edStholo 7012286d8edStholo /* Print any messages that were saved up for last. */ 7022286d8edStholo print_message_queue (); 7032286d8edStholo 7042286d8edStholo free (switch_string); 7052286d8edStholo 7062286d8edStholo optind = optind_old; 707b2346922Stholo 708b2346922Stholo if (! callbacks || ! callbacks->write_output) 7092286d8edStholo check_output (outfile); 710b2346922Stholo 711b2346922Stholo if (opened_file) 7122286d8edStholo if (fclose (outfile) != 0) 713b2346922Stholo perror_with_name ("close error on output file"); 714b2346922Stholo 7152286d8edStholo return val; 7162286d8edStholo } 7172286d8edStholo 7182286d8edStholo /* Add the compiled form of regexp PATTERN to REGLIST. */ 7192286d8edStholo 7202286d8edStholo static void 7212286d8edStholo add_regexp (reglist, pattern) 7222286d8edStholo struct regexp_list **reglist; 7232286d8edStholo char const *pattern; 7242286d8edStholo { 7252286d8edStholo struct regexp_list *r; 7262286d8edStholo char const *m; 7272286d8edStholo 7282286d8edStholo r = (struct regexp_list *) xmalloc (sizeof (*r)); 7292286d8edStholo bzero (r, sizeof (*r)); 7302286d8edStholo r->buf.fastmap = xmalloc (256); 7312286d8edStholo m = re_compile_pattern (pattern, strlen (pattern), &r->buf); 7322286d8edStholo if (m != 0) 7332286d8edStholo diff_error ("%s: %s", pattern, m); 7342286d8edStholo 7352286d8edStholo /* Add to the start of the list, since it's easier than the end. */ 7362286d8edStholo r->next = *reglist; 7372286d8edStholo *reglist = r; 7382286d8edStholo } 7392286d8edStholo 7402286d8edStholo static int 7412286d8edStholo try_help (reason) 7422286d8edStholo char const *reason; 7432286d8edStholo { 7442286d8edStholo if (reason) 7452286d8edStholo diff_error ("%s", reason, 0); 7462286d8edStholo diff_error ("Try `%s --help' for more information.", diff_program_name, 0); 7472286d8edStholo return 2; 7482286d8edStholo } 7492286d8edStholo 7502286d8edStholo static void 7512286d8edStholo check_output (file) 7522286d8edStholo FILE *file; 7532286d8edStholo { 7542286d8edStholo if (ferror (file) || fflush (file) != 0) 7552286d8edStholo fatal ("write error"); 7562286d8edStholo } 7572286d8edStholo 7582286d8edStholo static char const * const option_help[] = { 7592286d8edStholo "-i --ignore-case Consider upper- and lower-case to be the same.", 7602286d8edStholo "-w --ignore-all-space Ignore all white space.", 7612286d8edStholo "-b --ignore-space-change Ignore changes in the amount of white space.", 7622286d8edStholo "-B --ignore-blank-lines Ignore changes whose lines are all blank.", 7632286d8edStholo "-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.", 7642286d8edStholo #if HAVE_SETMODE 7652286d8edStholo "--binary Read and write data in binary mode.", 7662286d8edStholo #endif 7672286d8edStholo "-a --text Treat all files as text.\n", 7682286d8edStholo "-c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.", 7692286d8edStholo "-u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.", 7702286d8edStholo " -NUM Use NUM context lines.", 7712286d8edStholo " -L LABEL --label LABEL Use LABEL instead of file name.", 7722286d8edStholo " -p --show-c-function Show which C function each change is in.", 7732286d8edStholo " -F RE --show-function-line=RE Show the most recent line matching RE.", 7742286d8edStholo "-q --brief Output only whether files differ.", 7752286d8edStholo "-e --ed Output an ed script.", 7762286d8edStholo "-n --rcs Output an RCS format diff.", 7772286d8edStholo "-y --side-by-side Output in two columns.", 7782286d8edStholo " -w NUM --width=NUM Output at most NUM (default 130) characters per line.", 7792286d8edStholo " --left-column Output only the left column of common lines.", 7802286d8edStholo " --suppress-common-lines Do not output common lines.", 7812286d8edStholo "-DNAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.", 7822286d8edStholo "--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.", 7832286d8edStholo "--line-format=LFMT Similar, but format all input lines with LFMT.", 7842286d8edStholo "--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.", 7852286d8edStholo " LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.", 7862286d8edStholo " GFMT may contain:", 7872286d8edStholo " %< lines from FILE1", 7882286d8edStholo " %> lines from FILE2", 7892286d8edStholo " %= lines common to FILE1 and FILE2", 7902286d8edStholo " %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER", 7912286d8edStholo " LETTERs are as follows for new group, lower case for old group:", 7922286d8edStholo " F first line number", 7932286d8edStholo " L last line number", 7942286d8edStholo " N number of lines = L-F+1", 7952286d8edStholo " E F-1", 7962286d8edStholo " M L+1", 7972286d8edStholo " LFMT may contain:", 7982286d8edStholo " %L contents of line", 7992286d8edStholo " %l contents of line, excluding any trailing newline", 8002286d8edStholo " %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number", 8012286d8edStholo " Either GFMT or LFMT may contain:", 8022286d8edStholo " %% %", 8032286d8edStholo " %c'C' the single character C", 8042286d8edStholo " %c'\\OOO' the character with octal code OOO\n", 8052286d8edStholo "-l --paginate Pass the output through `pr' to paginate it.", 8062286d8edStholo "-t --expand-tabs Expand tabs to spaces in output.", 8072286d8edStholo "-T --initial-tab Make tabs line up by prepending a tab.\n", 8082286d8edStholo "-r --recursive Recursively compare any subdirectories found.", 8092286d8edStholo "-N --new-file Treat absent files as empty.", 8102286d8edStholo "-P --unidirectional-new-file Treat absent first files as empty.", 8112286d8edStholo "-s --report-identical-files Report when two files are the same.", 8122286d8edStholo "-x PAT --exclude=PAT Exclude files that match PAT.", 8132286d8edStholo "-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE.", 8142286d8edStholo "-S FILE --starting-file=FILE Start with FILE when comparing directories.\n", 8152286d8edStholo "--horizon-lines=NUM Keep NUM lines of the common prefix and suffix.", 8162286d8edStholo "-d --minimal Try hard to find a smaller set of changes.", 8172286d8edStholo "-H --speed-large-files Assume large files and many scattered small changes.\n", 8182286d8edStholo "-v --version Output version info.", 8192286d8edStholo "--help Output this help.", 8202286d8edStholo 0 8212286d8edStholo }; 8222286d8edStholo 8232286d8edStholo static void 8242286d8edStholo usage () 8252286d8edStholo { 8262286d8edStholo char const * const *p; 8272286d8edStholo 828b2346922Stholo if (callbacks && callbacks->write_stdout) 829b2346922Stholo { 830b2346922Stholo (*callbacks->write_stdout) ("Usage: "); 831b2346922Stholo (*callbacks->write_stdout) (diff_program_name); 832b2346922Stholo (*callbacks->write_stdout) (" [OPTION]... FILE1 FILE2\n\n"); 833b2346922Stholo for (p = option_help; *p; p++) 834b2346922Stholo { 835b2346922Stholo (*callbacks->write_stdout) (" "); 836b2346922Stholo (*callbacks->write_stdout) (*p); 837b2346922Stholo (*callbacks->write_stdout) ("\n"); 838b2346922Stholo } 839b2346922Stholo (*callbacks->write_stdout) 840b2346922Stholo ("\nIf FILE1 or FILE2 is `-', read standard input.\n"); 841b2346922Stholo } 842b2346922Stholo else 843b2346922Stholo { 8442286d8edStholo printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", diff_program_name); 8452286d8edStholo for (p = option_help; *p; p++) 8462286d8edStholo printf (" %s\n", *p); 8472286d8edStholo printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n"); 8482286d8edStholo } 849b2346922Stholo } 8502286d8edStholo 8512286d8edStholo static int 8522286d8edStholo specify_format (var, value) 8532286d8edStholo char **var; 8542286d8edStholo char *value; 8552286d8edStholo { 8562286d8edStholo int err = *var ? strcmp (*var, value) : 0; 8572286d8edStholo *var = value; 8582286d8edStholo return err; 8592286d8edStholo } 8602286d8edStholo 8612286d8edStholo static void 8622286d8edStholo specify_style (style) 8632286d8edStholo enum output_style style; 8642286d8edStholo { 8652286d8edStholo if (output_style != OUTPUT_NORMAL 8662286d8edStholo && output_style != style) 8672286d8edStholo diff_error ("conflicting specifications of output style", 0, 0); 8682286d8edStholo output_style = style; 8692286d8edStholo } 8702286d8edStholo 8712286d8edStholo static char const * 8722286d8edStholo filetype (st) 8732286d8edStholo struct stat const *st; 8742286d8edStholo { 8752286d8edStholo /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats. 8762286d8edStholo To keep diagnostics grammatical, the returned string must start 8772286d8edStholo with a consonant. */ 8782286d8edStholo 8792286d8edStholo if (S_ISREG (st->st_mode)) 8802286d8edStholo { 8812286d8edStholo if (st->st_size == 0) 8822286d8edStholo return "regular empty file"; 8832286d8edStholo /* Posix.2 section 5.14.2 seems to suggest that we must read the file 8842286d8edStholo and guess whether it's C, Fortran, etc., but this is somewhat useless 8852286d8edStholo and doesn't reflect historical practice. We're allowed to guess 8862286d8edStholo wrong, so we don't bother to read the file. */ 8872286d8edStholo return "regular file"; 8882286d8edStholo } 8892286d8edStholo if (S_ISDIR (st->st_mode)) return "directory"; 8902286d8edStholo 8912286d8edStholo /* other Posix.1 file types */ 8922286d8edStholo #ifdef S_ISBLK 8932286d8edStholo if (S_ISBLK (st->st_mode)) return "block special file"; 8942286d8edStholo #endif 8952286d8edStholo #ifdef S_ISCHR 8962286d8edStholo if (S_ISCHR (st->st_mode)) return "character special file"; 8972286d8edStholo #endif 8982286d8edStholo #ifdef S_ISFIFO 8992286d8edStholo if (S_ISFIFO (st->st_mode)) return "fifo"; 9002286d8edStholo #endif 9012286d8edStholo 9022286d8edStholo /* other Posix.1b file types */ 9032286d8edStholo #ifdef S_TYPEISMQ 9042286d8edStholo if (S_TYPEISMQ (st)) return "message queue"; 9052286d8edStholo #endif 9062286d8edStholo #ifdef S_TYPEISSEM 9072286d8edStholo if (S_TYPEISSEM (st)) return "semaphore"; 9082286d8edStholo #endif 9092286d8edStholo #ifdef S_TYPEISSHM 9102286d8edStholo if (S_TYPEISSHM (st)) return "shared memory object"; 9112286d8edStholo #endif 9122286d8edStholo 9132286d8edStholo /* other popular file types */ 9142286d8edStholo /* S_ISLNK is impossible with `fstat' and `stat'. */ 9152286d8edStholo #ifdef S_ISSOCK 9162286d8edStholo if (S_ISSOCK (st->st_mode)) return "socket"; 9172286d8edStholo #endif 9182286d8edStholo 9192286d8edStholo return "weird file"; 9202286d8edStholo } 9212286d8edStholo 9222286d8edStholo /* Compare two files (or dirs) with specified names 9232286d8edStholo DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion. 9242286d8edStholo (if DIR0 is 0, then the name is just NAME0, etc.) 9252286d8edStholo This is self-contained; it opens the files and closes them. 9262286d8edStholo 9272286d8edStholo Value is 0 if files are the same, 1 if different, 9282286d8edStholo 2 if there is a problem opening them. */ 9292286d8edStholo 9302286d8edStholo static int 9312286d8edStholo compare_files (dir0, name0, dir1, name1, depth) 9322286d8edStholo char const *dir0, *dir1; 9332286d8edStholo char const *name0, *name1; 9342286d8edStholo int depth; 9352286d8edStholo { 9362286d8edStholo struct file_data inf[2]; 9372286d8edStholo register int i; 9382286d8edStholo int val; 9392286d8edStholo int same_files; 9402286d8edStholo int failed = 0; 9412286d8edStholo char *free0 = 0, *free1 = 0; 9422286d8edStholo 9432286d8edStholo /* If this is directory comparison, perhaps we have a file 9442286d8edStholo that exists only in one of the directories. 9452286d8edStholo If so, just print a message to that effect. */ 9462286d8edStholo 9472286d8edStholo if (! ((name0 != 0 && name1 != 0) 9482286d8edStholo || (unidirectional_new_file_flag && name1 != 0) 9492286d8edStholo || entire_new_file_flag)) 9502286d8edStholo { 9512286d8edStholo char const *name = name0 == 0 ? name1 : name0; 9522286d8edStholo char const *dir = name0 == 0 ? dir1 : dir0; 9532286d8edStholo message ("Only in %s: %s\n", dir, name); 9542286d8edStholo /* Return 1 so that diff_dirs will return 1 ("some files differ"). */ 9552286d8edStholo return 1; 9562286d8edStholo } 9572286d8edStholo 9582286d8edStholo bzero (inf, sizeof (inf)); 9592286d8edStholo 9602286d8edStholo /* Mark any nonexistent file with -1 in the desc field. */ 9612286d8edStholo /* Mark unopened files (e.g. directories) with -2. */ 9622286d8edStholo 9632286d8edStholo inf[0].desc = name0 == 0 ? -1 : -2; 9642286d8edStholo inf[1].desc = name1 == 0 ? -1 : -2; 9652286d8edStholo 9662286d8edStholo /* Now record the full name of each file, including nonexistent ones. */ 9672286d8edStholo 9682286d8edStholo if (name0 == 0) 9692286d8edStholo name0 = name1; 9702286d8edStholo if (name1 == 0) 9712286d8edStholo name1 = name0; 9722286d8edStholo 9732286d8edStholo inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0)); 9742286d8edStholo inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1)); 9752286d8edStholo 9762286d8edStholo /* Stat the files. Record whether they are directories. */ 9772286d8edStholo 9782286d8edStholo for (i = 0; i <= 1; i++) 9792286d8edStholo { 9802286d8edStholo if (inf[i].desc != -1) 9812286d8edStholo { 9822286d8edStholo int stat_result; 9832286d8edStholo 9842286d8edStholo if (i && filename_cmp (inf[i].name, inf[0].name) == 0) 9852286d8edStholo { 9862286d8edStholo inf[i].stat = inf[0].stat; 9872286d8edStholo stat_result = 0; 9882286d8edStholo } 9892286d8edStholo else if (strcmp (inf[i].name, "-") == 0) 9902286d8edStholo { 9912286d8edStholo inf[i].desc = STDIN_FILENO; 9922286d8edStholo stat_result = fstat (STDIN_FILENO, &inf[i].stat); 9932286d8edStholo if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode)) 9942286d8edStholo { 9952286d8edStholo off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR); 9962286d8edStholo if (pos == -1) 9972286d8edStholo stat_result = -1; 9982286d8edStholo else 9992286d8edStholo { 10002286d8edStholo if (pos <= inf[i].stat.st_size) 10012286d8edStholo inf[i].stat.st_size -= pos; 10022286d8edStholo else 10032286d8edStholo inf[i].stat.st_size = 0; 10042286d8edStholo /* Posix.2 4.17.6.1.4 requires current time for stdin. */ 10052286d8edStholo time (&inf[i].stat.st_mtime); 10062286d8edStholo } 10072286d8edStholo } 10082286d8edStholo } 10092286d8edStholo else 10102286d8edStholo stat_result = stat (inf[i].name, &inf[i].stat); 10112286d8edStholo 10122286d8edStholo if (stat_result != 0) 10132286d8edStholo { 10142286d8edStholo perror_with_name (inf[i].name); 10152286d8edStholo failed = 1; 10162286d8edStholo } 10172286d8edStholo else 10182286d8edStholo { 10192286d8edStholo inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0; 10202286d8edStholo if (inf[1 - i].desc == -1) 10212286d8edStholo { 10222286d8edStholo inf[1 - i].dir_p = inf[i].dir_p; 10232286d8edStholo inf[1 - i].stat.st_mode = inf[i].stat.st_mode; 10242286d8edStholo } 10252286d8edStholo } 10262286d8edStholo } 10272286d8edStholo } 10282286d8edStholo 10292286d8edStholo if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p) 10302286d8edStholo { 10312286d8edStholo /* If one is a directory, and it was specified in the command line, 10322286d8edStholo use the file in that dir with the other file's basename. */ 10332286d8edStholo 10342286d8edStholo int fnm_arg = inf[0].dir_p; 10352286d8edStholo int dir_arg = 1 - fnm_arg; 10362286d8edStholo char const *fnm = inf[fnm_arg].name; 10372286d8edStholo char const *dir = inf[dir_arg].name; 10382286d8edStholo char const *p = filename_lastdirchar (fnm); 10392286d8edStholo char const *filename = inf[dir_arg].name 10402286d8edStholo = dir_file_pathname (dir, p ? p + 1 : fnm); 10412286d8edStholo 10422286d8edStholo if (strcmp (fnm, "-") == 0) 10432286d8edStholo fatal ("can't compare - to a directory"); 10442286d8edStholo 10452286d8edStholo if (stat (filename, &inf[dir_arg].stat) != 0) 10462286d8edStholo { 10472286d8edStholo perror_with_name (filename); 10482286d8edStholo failed = 1; 10492286d8edStholo } 10502286d8edStholo else 10512286d8edStholo inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode); 10522286d8edStholo } 10532286d8edStholo 10542286d8edStholo if (failed) 10552286d8edStholo { 10562286d8edStholo 10572286d8edStholo /* If either file should exist but does not, return 2. */ 10582286d8edStholo 10592286d8edStholo val = 2; 10602286d8edStholo 10612286d8edStholo } 10622286d8edStholo else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1 10632286d8edStholo && 0 < same_file (&inf[0].stat, &inf[1].stat)) 10642286d8edStholo && no_diff_means_no_output) 10652286d8edStholo { 10662286d8edStholo /* The two named files are actually the same physical file. 10672286d8edStholo We know they are identical without actually reading them. */ 10682286d8edStholo 10692286d8edStholo val = 0; 10702286d8edStholo } 10712286d8edStholo else if (inf[0].dir_p & inf[1].dir_p) 10722286d8edStholo { 10732286d8edStholo if (output_style == OUTPUT_IFDEF) 10742286d8edStholo fatal ("-D option not supported with directories"); 10752286d8edStholo 10762286d8edStholo /* If both are directories, compare the files in them. */ 10772286d8edStholo 10782286d8edStholo if (depth > 0 && !recursive) 10792286d8edStholo { 10802286d8edStholo /* But don't compare dir contents one level down 10812286d8edStholo unless -r was specified. */ 10822286d8edStholo message ("Common subdirectories: %s and %s\n", 10832286d8edStholo inf[0].name, inf[1].name); 10842286d8edStholo val = 0; 10852286d8edStholo } 10862286d8edStholo else 10872286d8edStholo { 10882286d8edStholo val = diff_dirs (inf, compare_files, depth); 10892286d8edStholo } 10902286d8edStholo 10912286d8edStholo } 10922286d8edStholo else if ((inf[0].dir_p | inf[1].dir_p) 10932286d8edStholo || (depth > 0 10942286d8edStholo && (! S_ISREG (inf[0].stat.st_mode) 10952286d8edStholo || ! S_ISREG (inf[1].stat.st_mode)))) 10962286d8edStholo { 10972286d8edStholo /* Perhaps we have a subdirectory that exists only in one directory. 10982286d8edStholo If so, just print a message to that effect. */ 10992286d8edStholo 11002286d8edStholo if (inf[0].desc == -1 || inf[1].desc == -1) 11012286d8edStholo { 11022286d8edStholo if ((inf[0].dir_p | inf[1].dir_p) 11032286d8edStholo && recursive 11042286d8edStholo && (entire_new_file_flag 11052286d8edStholo || (unidirectional_new_file_flag && inf[0].desc == -1))) 11062286d8edStholo val = diff_dirs (inf, compare_files, depth); 11072286d8edStholo else 11082286d8edStholo { 11092286d8edStholo char const *dir = (inf[0].desc == -1) ? dir1 : dir0; 11102286d8edStholo /* See Posix.2 section 4.17.6.1.1 for this format. */ 11112286d8edStholo message ("Only in %s: %s\n", dir, name0); 11122286d8edStholo val = 1; 11132286d8edStholo } 11142286d8edStholo } 11152286d8edStholo else 11162286d8edStholo { 11172286d8edStholo /* We have two files that are not to be compared. */ 11182286d8edStholo 11192286d8edStholo /* See Posix.2 section 4.17.6.1.1 for this format. */ 11202286d8edStholo message5 ("File %s is a %s while file %s is a %s\n", 11212286d8edStholo inf[0].name, filetype (&inf[0].stat), 11222286d8edStholo inf[1].name, filetype (&inf[1].stat)); 11232286d8edStholo 11242286d8edStholo /* This is a difference. */ 11252286d8edStholo val = 1; 11262286d8edStholo } 11272286d8edStholo } 11282286d8edStholo else if ((no_details_flag & ~ignore_some_changes) 11292286d8edStholo && inf[0].stat.st_size != inf[1].stat.st_size 11302286d8edStholo && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode)) 11312286d8edStholo && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode))) 11322286d8edStholo { 11332286d8edStholo message ("Files %s and %s differ\n", inf[0].name, inf[1].name); 11342286d8edStholo val = 1; 11352286d8edStholo } 11362286d8edStholo else 11372286d8edStholo { 11382286d8edStholo /* Both exist and neither is a directory. */ 11392286d8edStholo 11402286d8edStholo /* Open the files and record their descriptors. */ 11412286d8edStholo 11422286d8edStholo if (inf[0].desc == -2) 11432286d8edStholo if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0) 11442286d8edStholo { 11452286d8edStholo perror_with_name (inf[0].name); 11462286d8edStholo failed = 1; 11472286d8edStholo } 11482286d8edStholo if (inf[1].desc == -2) 1149*e77048c1Stholo { 11502286d8edStholo if (same_files) 11512286d8edStholo inf[1].desc = inf[0].desc; 11522286d8edStholo else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0) 11532286d8edStholo { 11542286d8edStholo perror_with_name (inf[1].name); 11552286d8edStholo failed = 1; 11562286d8edStholo } 1157*e77048c1Stholo } 11582286d8edStholo 11592286d8edStholo #if HAVE_SETMODE 11602286d8edStholo if (binary_I_O) 11612286d8edStholo for (i = 0; i <= 1; i++) 11622286d8edStholo if (0 <= inf[i].desc) 11632286d8edStholo setmode (inf[i].desc, O_BINARY); 11642286d8edStholo #endif 11652286d8edStholo 11662286d8edStholo /* Compare the files, if no error was found. */ 11672286d8edStholo 11682286d8edStholo val = failed ? 2 : diff_2_files (inf, depth); 11692286d8edStholo 11702286d8edStholo /* Close the file descriptors. */ 11712286d8edStholo 11722286d8edStholo if (inf[0].desc >= 0 && close (inf[0].desc) != 0) 11732286d8edStholo { 11742286d8edStholo perror_with_name (inf[0].name); 11752286d8edStholo val = 2; 11762286d8edStholo } 11772286d8edStholo if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc 11782286d8edStholo && close (inf[1].desc) != 0) 11792286d8edStholo { 11802286d8edStholo perror_with_name (inf[1].name); 11812286d8edStholo val = 2; 11822286d8edStholo } 11832286d8edStholo } 11842286d8edStholo 11852286d8edStholo /* Now the comparison has been done, if no error prevented it, 11862286d8edStholo and VAL is the value this function will return. */ 11872286d8edStholo 11882286d8edStholo if (val == 0 && !inf[0].dir_p) 11892286d8edStholo { 11902286d8edStholo if (print_file_same_flag) 11912286d8edStholo message ("Files %s and %s are identical\n", 11922286d8edStholo inf[0].name, inf[1].name); 11932286d8edStholo } 11942286d8edStholo else 1195b2346922Stholo flush_output (); 11962286d8edStholo 11972286d8edStholo if (free0) 11982286d8edStholo free (free0); 11992286d8edStholo if (free1) 12002286d8edStholo free (free1); 12012286d8edStholo 12022286d8edStholo return val; 12032286d8edStholo } 12042286d8edStholo 12052286d8edStholo /* Initialize status variables and flag variables used in libdiff, 12062286d8edStholo to permit repeated calls to diff_run. */ 12072286d8edStholo 12082286d8edStholo static void 12092286d8edStholo initialize_main (argcp, argvp) 12102286d8edStholo int *argcp; 12112286d8edStholo char ***argvp; 12122286d8edStholo { 12132286d8edStholo /* These variables really must be reset each time diff_run is called. */ 12142286d8edStholo output_style = OUTPUT_NORMAL; 12152286d8edStholo context = -1; 12162286d8edStholo file_label[0] = NULL; 12172286d8edStholo file_label[1] = NULL; 12182286d8edStholo diff_program_name = (*argvp)[0]; 12192286d8edStholo outfile = NULL; 12202286d8edStholo 12212286d8edStholo /* Reset these also, just for safety's sake. (If one invocation turns 12222286d8edStholo on ignore_case_flag, it must be turned off before diff_run is called 12232286d8edStholo again. But it is possible to make many diffs before encountering 12242286d8edStholo such a problem. */ 12252286d8edStholo recursive = 0; 12262286d8edStholo no_discards = 0; 12272286d8edStholo #if HAVE_SETMODE 12282286d8edStholo binary_I_O = 0; 12292286d8edStholo #endif 12302286d8edStholo no_diff_means_no_output = 0; 12312286d8edStholo always_text_flag = 0; 12322286d8edStholo horizon_lines = 0; 12332286d8edStholo ignore_space_change_flag = 0; 12342286d8edStholo ignore_all_space_flag = 0; 12352286d8edStholo ignore_blank_lines_flag = 0; 12362286d8edStholo ignore_some_line_changes = 0; 12372286d8edStholo ignore_some_changes = 0; 12382286d8edStholo ignore_case_flag = 0; 12392286d8edStholo function_regexp_list = NULL; 12402286d8edStholo ignore_regexp_list = NULL; 12412286d8edStholo no_details_flag = 0; 12422286d8edStholo print_file_same_flag = 0; 12432286d8edStholo tab_align_flag = 0; 12442286d8edStholo tab_expand_flag = 0; 12452286d8edStholo dir_start_file = NULL; 12462286d8edStholo entire_new_file_flag = 0; 12472286d8edStholo unidirectional_new_file_flag = 0; 12482286d8edStholo paginate_flag = 0; 12492286d8edStholo bzero (group_format, sizeof (group_format)); 12502286d8edStholo bzero (line_format, sizeof (line_format)); 12512286d8edStholo sdiff_help_sdiff = 0; 12522286d8edStholo sdiff_left_only = 0; 12532286d8edStholo sdiff_skip_common_lines = 0; 12542286d8edStholo sdiff_half_width = 0; 12552286d8edStholo sdiff_column2_offset = 0; 12562286d8edStholo switch_string = NULL; 12572286d8edStholo heuristic = 0; 12582286d8edStholo bzero (files, sizeof (files)); 12592286d8edStholo } 1260