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