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