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