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 *
option_list(optionvec,count)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
ck_atoi(str,out)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
excluded_filename(f)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
add_exclude(pattern)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
add_exclude_file(name)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
diff_run(argc,argv,out,callbacks_arg)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;
252e77048c1Stholo
253e77048c1Stholo /* Set the jump buffer, so that diff may abort execution without
254e77048c1Stholo terminating the process. */
255e77048c1Stholo val = setjmp (diff_abort_buf);
256e77048c1Stholo if (val != 0)
257e77048c1Stholo {
258e77048c1Stholo optind = optind_old;
259e77048c1Stholo if (opened_file)
260e77048c1Stholo fclose (outfile);
261e77048c1Stholo return val;
262e77048c1Stholo }
263e77048c1Stholo
264e77048c1Stholo /* 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
add_regexp(reglist,pattern)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
try_help(reason)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
check_output(file)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.",
778*43c1707eStholo " -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
usage()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
specify_format(var,value)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
specify_style(style)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 *
filetype(st)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
compare_files(dir0,name0,dir1,name1,depth)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)
1149e77048c1Stholo {
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 }
1157e77048c1Stholo }
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
initialize_main(argcp,argvp)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