home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / d / diff20.zip / diff-2.0 / diff.c < prev    next >
C/C++ Source or Header  |  1992-09-15  |  22KB  |  838 lines

  1. /* GNU DIFF main routine.
  2.    Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU DIFF was written by Mike Haertel, David Hayes,
  21.    Richard Stallman, Len Tower, and Paul Eggert.  */
  22.  
  23. #define GDIFF_MAIN
  24. #include "diff.h"
  25. #include "getopt.h"
  26. #include "fnmatch.h"
  27.  
  28. #ifndef DEFAULT_WIDTH
  29. #define DEFAULT_WIDTH 130
  30. #endif
  31.  
  32. #ifndef GUTTER_WIDTH_MINIMUM
  33. #define GUTTER_WIDTH_MINIMUM 3
  34. #endif
  35.  
  36. int compare_files ();
  37. int diff_dirs ();
  38. int diff_2_files ();
  39. void add_regexp();
  40. void specify_style ();
  41. void usage ();
  42.  
  43. /* Nonzero for -r: if comparing two directories,
  44.    compare their common subdirectories recursively.  */
  45.  
  46. int recursive;
  47.  
  48. /* For debugging: don't do discard_confusing_lines.  */
  49.  
  50. int no_discards;
  51.  
  52. /* Return a string containing the command options with which diff was invoked.
  53.    Spaces appear between what were separate ARGV-elements.
  54.    There is a space at the beginning but none at the end.
  55.    If there were no options, the result is an empty string.
  56.  
  57.    Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
  58.    the length of that vector.  */
  59.  
  60. static char *
  61. option_list (optionvec, count)
  62.      char **optionvec;  /* Was `vector', but that collides on Alliant.  */
  63.      int count;
  64. {
  65.   int i;
  66.   int length = 0;
  67.   char *result;
  68.  
  69.   for (i = 0; i < count; i++)
  70.     length += strlen (optionvec[i]) + 1;
  71.  
  72.   result = (char *) xmalloc (length + 1);
  73.   result[0] = 0;
  74.  
  75.   for (i = 0; i < count; i++)
  76.     {
  77.       strcat (result, " ");
  78.       strcat (result, optionvec[i]);
  79.     }
  80.  
  81.   return result;
  82. }
  83.  
  84. /* Convert STR to a positive integer, storing the result in *OUT. 
  85.    If STR is not a valid integer, return -1 (otherwise 0). */
  86. static int
  87. ck_atoi (str, out)
  88.      char *str;
  89.      int *out;
  90. {
  91.   char *p;
  92.   for (p = str; *p; p++)
  93.     if (*p < '0' || *p > '9')
  94.       return -1;
  95.  
  96.   *out = atoi (optarg);
  97.   return 0;
  98. }
  99.  
  100. /* Keep track of excluded file name patterns.  */
  101.  
  102. static const char **exclude;
  103. static int exclude_alloc, exclude_count;
  104.  
  105. int
  106. excluded_filename (f)
  107.      const char *f;
  108. {
  109.   int i;
  110.   for (i = 0;  i < exclude_count;  i++)
  111.     if (fnmatch (exclude[i], f, 0) == 0)
  112.       return 1;
  113.   return 0;
  114. }
  115.  
  116. static void
  117. add_exclude (pattern)
  118.      const char *pattern;
  119. {
  120.   if (exclude_alloc <= exclude_count)
  121.     exclude = (const char **)
  122.           (exclude_alloc == 0
  123.            ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude))
  124.            : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude)));
  125.  
  126.   exclude[exclude_count++] = pattern;
  127. }
  128.  
  129. static int
  130. add_exclude_file (name)
  131.      const char *name;
  132. {
  133.   struct file_data f;
  134.   char *p, *q, *lim;
  135.  
  136.   f.name = optarg;
  137.   f.desc = strcmp (optarg, "-") == 0 ? 0 : open (optarg, O_RDONLY, 0);
  138.   if (f.desc < 0 || fstat (f.desc, &f.stat) != 0)
  139.     return -1;
  140.  
  141.   (void) sip (&f);
  142.   slurp (&f);
  143.  
  144.   for (p = f.buffer, lim = p + f.buffered_chars;  p < lim;  p = q)
  145.     {
  146.       q = memchr (p, '\n', lim - p);
  147.       if (!q)
  148.     q = lim;
  149.       *q++ = 0;
  150.       add_exclude (p);
  151.     }
  152.  
  153.   return close (f.desc);
  154. }
  155.  
  156. /* The numbers 129- that appear in the fourth element of some entries
  157.    tell the big switch in `main' how to process those options.  */
  158.  
  159. static struct option longopts[] =
  160. {
  161.   {"ignore-blank-lines", 0, 0, 'B'},
  162.   {"context", 2, 0, 129},
  163.   {"ifdef", 1, 0, 'D'},
  164.   {"show-function-line", 1, 0, 'F'},
  165.   {"speed-large-files", 0, 0, 'H'},
  166.   {"ignore-matching-lines", 1, 0, 'I'},
  167.   {"label", 1, 0, 'L'},
  168.   {"file-label", 1, 0, 'L'},    /* An alias, no longer recommended */
  169.   {"new-file", 0, 0, 'N'},
  170.   {"entire-new-file", 0, 0, 'N'},    /* An alias, no longer recommended */
  171.   {"unidirectional-new-file", 0, 0, 'P'},
  172.   {"starting-file", 1, 0, 'S'},
  173.   {"initial-tab", 0, 0, 'T'},
  174.   {"width", 1, 0, 'W'},
  175.   {"text", 0, 0, 'a'},
  176.   {"ascii", 0, 0, 'a'},        /* An alias, no longer recommended */
  177.   {"ignore-space-change", 0, 0, 'b'},
  178.   {"minimal", 0, 0, 'd'},
  179.   {"ed", 0, 0, 'e'},
  180.   {"forward-ed", 0, 0, 'f'},
  181.   {"ignore-case", 0, 0, 'i'},
  182.   {"paginate", 0, 0, 'l'},
  183.   {"print", 0, 0, 'l'},        /* An alias, no longer recommended */
  184.   {"rcs", 0, 0, 'n'},
  185.   {"show-c-function", 0, 0, 'p'},
  186.   {"binary", 0, 0, 'q'},    /* An alias, no longer recommended */
  187.   {"brief", 0, 0, 'q'},
  188.   {"recursive", 0, 0, 'r'},
  189.   {"report-identical-files", 0, 0, 's'},
  190.   {"expand-tabs", 0, 0, 't'},
  191.   {"version", 0, 0, 'v'},
  192.   {"ignore-all-space", 0, 0, 'w'},
  193.   {"exclude", 1, 0, 'x'},
  194.   {"exclude-from", 1, 0, 'X'},
  195.   {"side-by-side", 0, 0, 'y'},
  196.   {"unified", 2, 0, 130},
  197.   {"left-column", 0, 0, 131},
  198.   {"suppress-common-lines", 0, 0, 132},
  199.   {"sdiff-merge-assist", 0, 0, 133},
  200.   {0, 0, 0, 0}
  201. };
  202.  
  203. int
  204. main (argc, argv)
  205.      int argc;
  206.      char *argv[];
  207. {
  208.   int val;
  209.   int c;
  210.   int prev = -1;
  211.   extern char *version_string;
  212.   int width = DEFAULT_WIDTH;
  213.  
  214.   program = argv[0];
  215.  
  216.   /* Do our initializations. */
  217.   output_style = OUTPUT_NORMAL;
  218.   always_text_flag = FALSE;
  219.   ignore_space_change_flag = FALSE;
  220.   ignore_all_space_flag = FALSE;
  221.   length_varies = FALSE;
  222.   ignore_case_flag = FALSE;
  223.   ignore_blank_lines_flag = FALSE;
  224.   ignore_regexp_list = NULL;
  225.   function_regexp_list = NULL;
  226.   print_file_same_flag = FALSE;
  227.   entire_new_file_flag = FALSE;
  228.   unidirectional_new_file_flag = FALSE;
  229.   no_details_flag = FALSE;
  230.   context = -1;
  231.   line_end_char = '\n';
  232.   tab_align_flag = FALSE;
  233.   tab_expand_flag = FALSE;
  234.   recursive = FALSE;
  235.   paginate_flag = FALSE;
  236.   ifdef_string = NULL;
  237.   heuristic = FALSE;
  238.   dir_start_file = NULL;
  239.   msg_chain = NULL;
  240.   msg_chain_end = NULL;
  241.   no_discards = 0;
  242.  
  243.   /* Decode the options.  */
  244.  
  245.   while ((c = getopt_long (argc, argv,
  246.                "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuvwW:x:X:y",
  247.                longopts, (int *)0)) != EOF)
  248.     {
  249.       switch (c)
  250.     {
  251.       /* All digits combine in decimal to specify the context-size.  */
  252.     case '1':
  253.     case '2':
  254.     case '3':
  255.     case '4':
  256.     case '5':
  257.     case '6':
  258.     case '7':
  259.     case '8':
  260.     case '9':
  261.     case '0':
  262.       if (context == -1)
  263.         context = 0;
  264.       /* If a context length has already been specified,
  265.          more digits allowed only if they follow right after the others.
  266.          Reject two separate runs of digits, or digits after -C.  */
  267.       else if (prev < '0' || prev > '9')
  268.         fatal ("context length specified twice");
  269.  
  270.       context = context * 10 + c - '0';
  271.       break;
  272.  
  273.     case 'a':
  274.       /* Treat all files as text files; never treat as binary.  */
  275.       always_text_flag = 1;
  276.       break;
  277.  
  278.     case 'b':
  279.       /* Ignore changes in amount of whitespace.  */
  280.       ignore_space_change_flag = 1;
  281.       length_varies = 1;
  282.       break;
  283.  
  284.     case 'B':
  285.       /* Ignore changes affecting only blank lines.  */
  286.       ignore_blank_lines_flag = 1;
  287.       break;
  288.  
  289.     case 'C':
  290.     case 129:        /* +context[=lines] */
  291.     case 130:        /* +unified[=lines] */
  292.       if (optarg)
  293.         {
  294.           if (context >= 0)
  295.         fatal ("context length specified twice");
  296.  
  297.           if (ck_atoi (optarg, &context))
  298.         fatal ("invalid context length argument");
  299.         }
  300.  
  301.       /* Falls through.  */
  302.     case 'c':
  303.       /* Make context-style output.  */
  304.       specify_style (c == 130 ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
  305.       break;
  306.  
  307.     case 'd':
  308.       /* Don't discard lines.  This makes things slower (sometimes much
  309.          slower) but will find a guaranteed minimal set of changes.  */
  310.       no_discards = 1;
  311.       break;
  312.  
  313.     case 'D':
  314.       /* Make merged #ifdef output.  */
  315.       specify_style (OUTPUT_IFDEF);
  316.       ifdef_string = optarg;
  317.       break;
  318.  
  319.     case 'e':
  320.       /* Make output that is a valid `ed' script.  */
  321.       specify_style (OUTPUT_ED);
  322.       break;
  323.  
  324.     case 'f':
  325.       /* Make output that looks vaguely like an `ed' script
  326.          but has changes in the order they appear in the file.  */
  327.       specify_style (OUTPUT_FORWARD_ED);
  328.       break;
  329.  
  330.     case 'F':
  331.       /* Show, for each set of changes, the previous line that
  332.          matches the specified regexp.  Currently affects only
  333.          context-style output.  */
  334.       add_regexp (&function_regexp_list, optarg);
  335.       break;
  336.  
  337.     case 'h':
  338.       /* Split the files into chunks of around 1500 lines
  339.          for faster processing.  Usually does not change the result.
  340.  
  341.          This currently has no effect.  */
  342.       break;
  343.  
  344.     case 'H':
  345.       /* Turn on heuristics that speed processing of large files
  346.          with a small density of changes.  */
  347.       heuristic = 1;
  348.       break;
  349.  
  350.     case 'i':
  351.       /* Ignore changes in case.  */
  352.       ignore_case_flag = 1;
  353.       break;
  354.  
  355.     case 'I':
  356.       /* Ignore changes affecting only lines that match the
  357.          specified regexp.  */
  358.       add_regexp (&ignore_regexp_list, optarg);
  359.       break;
  360.  
  361.     case 'l':
  362.       /* Pass the output through `pr' to paginate it.  */
  363.       paginate_flag = 1;
  364.       break;
  365.  
  366.     case 'L':
  367.       /* Specify file labels for `-c' output headers.  */
  368.       if (!file_label[0])
  369.         file_label[0] = optarg;
  370.       else if (!file_label[1])
  371.         file_label[1] = optarg;
  372.       else
  373.         fatal ("too many file label options");
  374.       break;
  375.       
  376.     case 'n':
  377.       /* Output RCS-style diffs, like `-f' except that each command
  378.          specifies the number of lines affected.  */
  379.       specify_style (OUTPUT_RCS);
  380.       break;
  381.  
  382.     case 'N':
  383.       /* When comparing directories, if a file appears only in one
  384.          directory, treat it as present but empty in the other.  */
  385.       entire_new_file_flag = 1;
  386.       break;
  387.  
  388.     case 'p':
  389.       /* Make context-style output and show name of last C function.  */
  390.       specify_style (OUTPUT_CONTEXT);
  391.       add_regexp (&function_regexp_list, "^[_a-zA-Z$]");
  392.       break;
  393.  
  394.     case 'P':
  395.       /* When comparing directories, if a file appears only in
  396.          the second directory of the two,
  397.          treat it as present but empty in the other.  */
  398.       unidirectional_new_file_flag = 1;
  399.       break;
  400.  
  401.     case 'q':
  402.       no_details_flag = 1;
  403.       break;
  404.  
  405.     case 'r':
  406.       /* When comparing directories, 
  407.          recursively compare any subdirectories found.  */
  408.       recursive = 1;
  409.       break;
  410.  
  411.     case 's':
  412.       /* Print a message if the files are the same.  */
  413.       print_file_same_flag = 1;
  414.       break;
  415.  
  416.     case 'S':
  417.       /* When comparing directories, start with the specified
  418.          file name.  This is used for resuming an aborted comparison.  */
  419.       dir_start_file = optarg;
  420.       break;
  421.  
  422.     case 't':
  423.       /* Expand tabs to spaces in the output so that it preserves
  424.          the alignment of the input files.  */
  425.       tab_expand_flag = 1;
  426.       break;
  427.  
  428.     case 'T':
  429.       /* Use a tab in the output, rather than a space, before the
  430.          text of an input line, so as to keep the proper alignment
  431.          in the input line without changing the characters in it.  */
  432.       tab_align_flag = 1;
  433.       break;
  434.  
  435.     case 'u':
  436.       /* Output the context diff in unidiff format.  */
  437.       specify_style (OUTPUT_UNIFIED);
  438.       break;
  439.  
  440.     case 'v':
  441.       fprintf (stderr, "GNU diff version %s\n", version_string);
  442.       break;
  443.  
  444.     case 'w':
  445.       /* Ignore horizontal whitespace when comparing lines.  */
  446.       ignore_all_space_flag = 1;
  447.       length_varies = 1;
  448.       break;
  449.  
  450.     case 'x':
  451.       add_exclude (optarg);
  452.       break;
  453.  
  454.     case 'X':
  455.       if (add_exclude_file (optarg) != 0)
  456.         pfatal_with_name (optarg);
  457.       break;
  458.  
  459.     case 'y':
  460.       /* Use side-by-side (sdiff-style) columnar output. */
  461.       specify_style (OUTPUT_SDIFF);
  462.       break;
  463.  
  464.     case 'W':
  465.       /* Set the line width for OUTPUT_SDIFF.  */
  466.       if (ck_atoi (optarg, &width) || width <= 0)
  467.         fatal ("column width must be a positive integer");
  468.       break;
  469.       
  470.     case 131:
  471.       sdiff_left_only = 1;
  472.       break;
  473.       
  474.     case 132:
  475.       sdiff_skip_common_lines = 1;
  476.       break;
  477.       
  478.     case 133:
  479.       /* sdiff-style columns output. */
  480.       specify_style (OUTPUT_SDIFF);
  481.       sdiff_help_sdiff = 1;
  482.       break;
  483.  
  484.     default:
  485.       usage ();
  486.     }
  487.       prev = c;
  488.     }
  489.  
  490.   if (optind != argc - 2)
  491.     usage ();
  492.  
  493.  
  494.   {
  495.     /*
  496.      *    We maximize first the half line width, and then the gutter width,
  497.      *    according to the following constraints:
  498.      *    1.  Two half lines plus a gutter must fit in a line.
  499.      *    2.  If the half line width is nonzero:
  500.      *        a.  The gutter width is at least GUTTER_WIDTH_MINIMUM.
  501.      *        b.  If tabs are not expanded to spaces,
  502.      *        a half line plus a gutter is an integral number of tabs,
  503.      *        so that tabs in the right column line up.
  504.      */
  505.     int t = tab_expand_flag ? 1 : TAB_WIDTH;
  506.     int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t)  *  t;
  507.     sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)),
  508.     sdiff_column2_offset = sdiff_half_width ? off : width;
  509.   }
  510.  
  511.   if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED)
  512.     context = 0;
  513.   else if (context == -1)
  514.     /* Default amount of context for -c.  */
  515.     context = 3;
  516.  
  517.   no_diff_means_no_output =
  518.     output_style != OUTPUT_IFDEF
  519.       && (output_style != OUTPUT_SDIFF
  520.       || (sdiff_skip_common_lines && ! sdiff_help_sdiff));
  521.  
  522.   switch_string = option_list (argv + 1, optind - 1);
  523.  
  524.   val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
  525.  
  526.   /* Print any messages that were saved up for last.  */
  527.   print_message_queue ();
  528.  
  529.   if (ferror (stdout) || fclose (stdout) != 0)
  530.     fatal ("write error");
  531.   exit (val);
  532.   return val;
  533. }
  534.  
  535. /* Add the compiled form of regexp PATTERN to REGLIST.  */
  536.  
  537. void
  538. add_regexp (reglist, pattern)
  539.      struct regexp_list **reglist;
  540.      char *pattern;
  541. {
  542.   struct regexp_list *r;
  543.   const char *m;
  544.  
  545.   r = (struct regexp_list *) xmalloc (sizeof (*r));
  546.   bzero (r, sizeof (*r));
  547.   r->buf.fastmap = (char *) xmalloc (256);
  548.   m = re_compile_pattern (pattern, strlen (pattern), &r->buf);
  549.   if (m != 0)
  550.     error ("%s: %s", pattern, m);
  551.  
  552.   /* Add to the start of the list, since it's easier than the end.  */
  553.   r->next = *reglist;
  554.   *reglist = r;
  555. }
  556.  
  557. void
  558. usage ()
  559. {
  560.   fprintf (stderr, "Usage: %s [options] from-file to-file\n", program);
  561.   fprintf (stderr, "Options:\n\
  562.        [-abBcdefhHilnNpPqrstTuvwy] [-C lines] [-D symbol] [-F regexp]\n\
  563.        [-I regexp] [-L from-label [-L to-label]] [-S starting-file]\n\
  564.        [-W columns] [-x pattern] [-X pattern-file] [--exclude=pattern]\n\
  565.        [--exclude-from=pattern-file] [--ignore-blank-lines] [--context[=lines]]\n\
  566.        [--ifdef=symbol] [--show-function-line=regexp] [--speed-large-files]\n\
  567.        [--label=from-label [--label=to-label]] [--new-file]\n");
  568.   fprintf (stderr, "\
  569.        [--ignore-matching-lines=regexp] [--unidirectional-new-file]\n\
  570.        [--starting-file=starting-file] [--initial-tab] [--width=columns]\n\
  571.        [--text] [--ignore-space-change] [--minimal] [--ed] [--forward-ed]\n\
  572.        [--ignore-case] [--paginate] [--rcs] [--show-c-function] [--brief]\n\
  573.        [--recursive] [--report-identical-files] [--expand-tabs] [--version]\n");
  574.   fprintf (stderr, "\
  575.        [--ignore-all-space] [--side-by-side] [--unified[=lines]]\n\
  576.        [--left-column] [--suppress-common-lines] [--sdiff-merge-assist]\n");
  577.   exit (2);
  578.  
  579. void
  580. specify_style (style)
  581.      enum output_style style;
  582. {
  583.   if (output_style != OUTPUT_NORMAL
  584.       && output_style != style)
  585.     error ("conflicting specifications of output style", 0, 0);
  586.   output_style = style;
  587. }
  588.  
  589. /* Compare two files (or dirs) with specified names
  590.    DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
  591.    (if DIR0 is 0, then the name is just NAME0, etc.)
  592.    This is self-contained; it opens the files and closes them.
  593.  
  594.    Value is 0 if files are the same, 1 if different,
  595.    2 if there is a problem opening them.  */
  596.  
  597. int
  598. compare_files (dir0, name0, dir1, name1, depth)
  599.      char *dir0, *dir1;
  600.      char *name0, *name1;
  601.      int depth;
  602. {
  603.   struct file_data inf[2];
  604.   register int i;
  605.   int val;
  606.   int same_files;
  607.   int errorcount = 0;
  608.  
  609.   /* If this is directory comparison, perhaps we have a file
  610.      that exists only in one of the directories.
  611.      If so, just print a message to that effect.  */
  612.  
  613.   if (! ((name0 != 0 && name1 != 0)
  614.      || (unidirectional_new_file_flag && name1 != 0)
  615.      || entire_new_file_flag))
  616.     {
  617.       char *name = name0 == 0 ? name1 : name0;
  618.       char *dir = name0 == 0 ? dir1 : dir0;
  619.       message ("Only in %s: %s\n", dir, name);
  620.       /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
  621.       return 1;
  622.     }
  623.  
  624.   /* Mark any nonexistent file with -1 in the desc field.  */
  625.   /* Mark unopened files (i.e. directories) with -2. */
  626.  
  627.   inf[0].desc = name0 == 0 ? -1 : -2;
  628.   inf[1].desc = name1 == 0 ? -1 : -2;
  629.  
  630.   /* Now record the full name of each file, including nonexistent ones.  */
  631.  
  632.   if (name0 == 0)
  633.     name0 = name1;
  634.   if (name1 == 0)
  635.     name1 = name0;
  636.  
  637.   inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0);
  638.   inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1);
  639.  
  640.   /* Stat the files.  Record whether they are directories.  */
  641.  
  642.   for (i = 0; i <= 1; i++)
  643.     {
  644.       bzero (&inf[i].stat, sizeof (struct stat));
  645.       inf[i].dir_p = 0;
  646.  
  647.       if (inf[i].desc != -1)
  648.     {
  649.       int stat_result;
  650.  
  651.       if (strcmp (inf[i].name, "-") == 0)
  652.         {
  653.           inf[i].desc = 0;
  654.           inf[i].name = "Standard Input";
  655.           stat_result = fstat (0, &inf[i].stat);
  656.         }
  657.       else
  658.         stat_result = stat (inf[i].name, &inf[i].stat);
  659.  
  660.       if (stat_result != 0)
  661.         {
  662.           perror_with_name (inf[i].name);
  663.           errorcount = 1;
  664.         }
  665.       else
  666.         inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
  667.     }
  668.     }
  669.  
  670.   if (name0 == 0)
  671.     inf[0].dir_p = inf[1].dir_p;
  672.   if (name1 == 0)
  673.     inf[1].dir_p = inf[0].dir_p;
  674.  
  675.   if (errorcount == 0 && depth == 0 && inf[0].dir_p != inf[1].dir_p)
  676.     {
  677.       /* If one is a directory, and it was specified in the command line,
  678.      use the file in that dir with the other file's basename.  */
  679.  
  680.       int fnm_arg = inf[0].dir_p;
  681.       int dir_arg = 1 - fnm_arg;
  682.       char *p = rindex (inf[fnm_arg].name, '/');
  683.       char *filename = inf[dir_arg].name
  684.     = concat (inf[dir_arg].name,  "/", (p ? p+1 : inf[fnm_arg].name));
  685.  
  686.       if (inf[fnm_arg].desc == 0)
  687.     fatal ("can't compare - to a directory");
  688.  
  689.       if (stat (filename, &inf[dir_arg].stat) != 0)
  690.     {
  691.       perror_with_name (filename);
  692.       errorcount = 1;
  693.     }
  694.       else
  695.     inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode);
  696.     }
  697.  
  698.   if (errorcount)
  699.     {
  700.  
  701.       /* If either file should exist but does not, return 2.  */
  702.  
  703.       val = 2;
  704.  
  705.     }
  706.   else if ((same_files =    inf[0].stat.st_ino == inf[1].stat.st_ino
  707.              && inf[0].stat.st_dev == inf[1].stat.st_dev
  708.              && inf[0].desc != -1
  709.              && inf[1].desc != -1)
  710.        && no_diff_means_no_output)
  711.     {
  712.       /* The two named files are actually the same physical file.
  713.      We know they are identical without actually reading them.  */
  714.  
  715.       val = 0;
  716.     }
  717.   else if (inf[0].dir_p & inf[1].dir_p)
  718.     {
  719.       if (output_style == OUTPUT_IFDEF)
  720.     fatal ("-D option not supported with directories");
  721.  
  722.       /* If both are directories, compare the files in them.  */
  723.  
  724.       if (depth > 0 && !recursive)
  725.     {
  726.       /* But don't compare dir contents one level down
  727.          unless -r was specified.  */
  728.       message ("Common subdirectories: %s and %s\n",
  729.            inf[0].name, inf[1].name);
  730.       val = 0;
  731.     }
  732.       else
  733.     {
  734.       val = diff_dirs (inf[0].name, inf[1].name, 
  735.                compare_files, depth, 0, 0);
  736.     }
  737.  
  738.     }
  739.   else if (inf[0].dir_p | inf[1].dir_p)
  740.     {
  741.       /* Perhaps we have a subdirectory that exists only in one directory.
  742.      If so, just print a message to that effect.  */
  743.  
  744.       if (inf[0].desc == -1 || inf[1].desc == -1)
  745.     {
  746.       if (recursive
  747.           && (entire_new_file_flag
  748.           || (unidirectional_new_file_flag && inf[0].desc == -1)))
  749.         val = diff_dirs (inf[0].name, inf[1].name, compare_files, depth,
  750.                  inf[0].desc == -1, inf[1].desc == -1);
  751.       else
  752.         {
  753.           char *dir = (inf[0].desc == -1) ? dir1 : dir0;
  754.           message ("Only in %s: %s\n", dir, name0);
  755.           val = 1;
  756.         }
  757.     }
  758.       else
  759.     {
  760.       /* We have a subdirectory in one directory
  761.          and a file in the other.  */
  762.  
  763.       message ("%s is a directory but %s is not\n",
  764.            inf[1 - inf[0].dir_p].name, inf[inf[0].dir_p].name);
  765.  
  766.       /* This is a difference.  */
  767.       val = 1;
  768.     }
  769.     }
  770.   else if (no_details_flag
  771.        && inf[0].stat.st_size != inf[1].stat.st_size
  772.        && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode))
  773.        && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode)))
  774.     {
  775.       message ("Files %s and %s differ\n", inf[0].name, inf[1].name);
  776.       val = 1;
  777.     }
  778.   else
  779.     {
  780.       /* Both exist and neither is a directory.  */
  781.  
  782.       /* Open the files and record their descriptors.  */
  783.  
  784.       if (inf[0].desc == -2)
  785.     if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0)
  786.       {
  787.         perror_with_name (inf[0].name);
  788.         errorcount = 1;
  789.       }
  790.       if (inf[1].desc == -2)
  791.     if (same_files)
  792.       inf[1].desc = inf[0].desc;
  793.     else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0)
  794.       {
  795.         perror_with_name (inf[1].name);
  796.         errorcount = 1;
  797.       }
  798.     
  799.       /* Compare the files, if no error was found.  */
  800.  
  801.       val = errorcount ? 2 : diff_2_files (inf, depth);
  802.  
  803.       /* Close the file descriptors.  */
  804.  
  805.       if (inf[0].desc >= 0 && close (inf[0].desc) != 0)
  806.     {
  807.       perror_with_name (inf[0].name);
  808.       val = 2;
  809.     }
  810.       if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc
  811.       && close (inf[1].desc) != 0)
  812.     {
  813.       perror_with_name (inf[1].name);
  814.       val = 2;
  815.     }
  816.     }
  817.  
  818.   /* Now the comparison has been done, if no error prevented it,
  819.      and VAL is the value this function will return.  */
  820.  
  821.   if (val == 0 && !inf[0].dir_p)
  822.     {
  823.       if (print_file_same_flag)
  824.     message ("Files %s and %s are identical\n",
  825.          inf[0].name, inf[1].name);
  826.     }
  827.   else
  828.     fflush (stdout);
  829.  
  830.   if (dir0 != 0)
  831.     free (inf[0].name);
  832.   if (dir1 != 0)
  833.     free (inf[1].name);
  834.  
  835.   return val;
  836. }
  837.