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