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