xref: /openbsd-src/gnu/usr.bin/cvs/diff/diff.c (revision b2346922a76a50a89e33beab4ebbc0950de8a8df)
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