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