11e72d8d2Sderaadt /*
21e72d8d2Sderaadt * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt *
51e72d8d2Sderaadt * You may distribute under the terms of the GNU General Public License as
62286d8edStholo * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt *
81e72d8d2Sderaadt * Difference
91e72d8d2Sderaadt *
101e72d8d2Sderaadt * Run diff against versions in the repository. Options that are specified are
111e72d8d2Sderaadt * passed on directly to "rcsdiff".
121e72d8d2Sderaadt *
131e72d8d2Sderaadt * Without any file arguments, runs diff against all the currently modified
141e72d8d2Sderaadt * files.
151e72d8d2Sderaadt */
161e72d8d2Sderaadt
171e72d8d2Sderaadt #include "cvs.h"
181e72d8d2Sderaadt
1950bf276cStholo enum diff_file
2050bf276cStholo {
2150bf276cStholo DIFF_ERROR,
2250bf276cStholo DIFF_ADDED,
2350bf276cStholo DIFF_REMOVED,
2450bf276cStholo DIFF_DIFFERENT,
2550bf276cStholo DIFF_SAME
2650bf276cStholo };
2750bf276cStholo
2850bf276cStholo static Dtype diff_dirproc PROTO ((void *callerdat, char *dir,
2950bf276cStholo char *pos_repos, char *update_dir,
3050bf276cStholo List *entries));
3150bf276cStholo static int diff_filesdoneproc PROTO ((void *callerdat, int err,
3250bf276cStholo char *repos, char *update_dir,
3350bf276cStholo List *entries));
3450bf276cStholo static int diff_dirleaveproc PROTO ((void *callerdat, char *dir,
3550bf276cStholo int err, char *update_dir,
3650bf276cStholo List *entries));
3750bf276cStholo static enum diff_file diff_file_nodiff PROTO ((struct file_info *finfo,
3850bf276cStholo Vers_TS *vers,
3950bf276cStholo enum diff_file));
4050bf276cStholo static int diff_fileproc PROTO ((void *callerdat, struct file_info *finfo));
411e72d8d2Sderaadt static void diff_mark_errors PROTO((int err));
421e72d8d2Sderaadt
43c71bc7e2Stholo
44c71bc7e2Stholo /* Global variables. Would be cleaner if we just put this stuff in a
45c71bc7e2Stholo struct like log.c does. */
46c71bc7e2Stholo
47c71bc7e2Stholo /* Command line tags, from -r option. Points into argv. */
481e72d8d2Sderaadt static char *diff_rev1, *diff_rev2;
49c71bc7e2Stholo /* Command line dates, from -D option. Malloc'd. */
501e72d8d2Sderaadt static char *diff_date1, *diff_date2;
511e72d8d2Sderaadt static char *use_rev1, *use_rev2;
522286d8edStholo static int have_rev1_label, have_rev2_label;
531e72d8d2Sderaadt
541e72d8d2Sderaadt /* Revision of the user file, if it is unchanged from something in the
551e72d8d2Sderaadt repository and we want to use that fact. */
561e72d8d2Sderaadt static char *user_file_rev;
571e72d8d2Sderaadt
581e72d8d2Sderaadt static char *options;
59461cc63eStholo static char *opts;
60461cc63eStholo static size_t opts_allocated = 1;
611e72d8d2Sderaadt static int diff_errors;
621e72d8d2Sderaadt static int empty_files = 0;
631e72d8d2Sderaadt
6450bf276cStholo /* FIXME: should be documenting all the options here. They don't
6550bf276cStholo perfectly match rcsdiff options (for example, we always support
6650bf276cStholo --ifdef and --context, but rcsdiff only does if diff does). */
671e72d8d2Sderaadt static const char *const diff_usage[] =
681e72d8d2Sderaadt {
692770ece5Stholo "Usage: %s %s [-lNR] [rcsdiff-options]\n",
701e72d8d2Sderaadt " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
711e72d8d2Sderaadt "\t-l\tLocal directory only, not recursive\n",
722770ece5Stholo "\t-R\tProcess directories recursively.\n",
731e72d8d2Sderaadt "\t-D d1\tDiff revision for date against working file.\n",
741e72d8d2Sderaadt "\t-D d2\tDiff rev1/date1 against date2.\n",
751e72d8d2Sderaadt "\t-N\tinclude diffs for added and removed files.\n",
761e72d8d2Sderaadt "\t-r rev1\tDiff revision for rev1 against working file.\n",
771e72d8d2Sderaadt "\t-r rev2\tDiff rev1/date1 against rev2.\n",
78780d15dfStholo "\t--ifdef=arg\tOutput diffs in ifdef format.\n",
79780d15dfStholo "(consult the documentation for your diff program for rcsdiff-options.\n",
80780d15dfStholo "The most popular is -c for context diffs but there are many more).\n",
812286d8edStholo "(Specify the --help global option for a list of other help options)\n",
821e72d8d2Sderaadt NULL
831e72d8d2Sderaadt };
841e72d8d2Sderaadt
8550bf276cStholo /* I copied this array directly out of diff.c in diffutils 2.7, after
8650bf276cStholo removing the following entries, none of which seem relevant to use
8750bf276cStholo with CVS:
8850bf276cStholo --help
8950bf276cStholo --version
9050bf276cStholo --recursive
9150bf276cStholo --unidirectional-new-file
9250bf276cStholo --starting-file
9350bf276cStholo --exclude
9450bf276cStholo --exclude-from
9550bf276cStholo --sdiff-merge-assist
9650bf276cStholo
9750bf276cStholo I changed the options which take optional arguments (--context and
9850bf276cStholo --unified) to return a number rather than a letter, so that the
9950bf276cStholo optional argument could be handled more easily. I changed the
10050bf276cStholo --paginate and --brief options to return a number, since -l and -q
10150bf276cStholo mean something else to cvs diff.
10250bf276cStholo
10350bf276cStholo The numbers 129- that appear in the fourth element of some entries
10450bf276cStholo tell the big switch in `diff' how to process those options. -- Ian
10550bf276cStholo
10650bf276cStholo The following options, which diff lists as "An alias, no longer
10750bf276cStholo recommended" have been removed: --file-label --entire-new-file
10850bf276cStholo --ascii --print. */
10950bf276cStholo
11050bf276cStholo static struct option const longopts[] =
11150bf276cStholo {
11250bf276cStholo {"ignore-blank-lines", 0, 0, 'B'},
11350bf276cStholo {"context", 2, 0, 143},
1142286d8edStholo {"ifdef", 1, 0, 131},
11550bf276cStholo {"show-function-line", 1, 0, 'F'},
11650bf276cStholo {"speed-large-files", 0, 0, 'H'},
11750bf276cStholo {"ignore-matching-lines", 1, 0, 'I'},
11850bf276cStholo {"label", 1, 0, 'L'},
11950bf276cStholo {"new-file", 0, 0, 'N'},
12043c1707eStholo {"initial-tab", 0, 0, 'T'},
12150bf276cStholo {"width", 1, 0, 'W'},
12250bf276cStholo {"text", 0, 0, 'a'},
12350bf276cStholo {"ignore-space-change", 0, 0, 'b'},
12450bf276cStholo {"minimal", 0, 0, 'd'},
12550bf276cStholo {"ed", 0, 0, 'e'},
12650bf276cStholo {"forward-ed", 0, 0, 'f'},
12750bf276cStholo {"ignore-case", 0, 0, 'i'},
12850bf276cStholo {"paginate", 0, 0, 144},
12950bf276cStholo {"rcs", 0, 0, 'n'},
13050bf276cStholo {"show-c-function", 0, 0, 'p'},
13150bf276cStholo
13250bf276cStholo /* This is a potentially very useful option, except the output is so
13350bf276cStholo silly. It would be much better for it to look like "cvs rdiff -s"
13450bf276cStholo which displays all the same info, minus quite a few lines of
13550bf276cStholo extraneous garbage. */
13650bf276cStholo {"brief", 0, 0, 145},
13750bf276cStholo
13850bf276cStholo {"report-identical-files", 0, 0, 's'},
13950bf276cStholo {"expand-tabs", 0, 0, 't'},
14050bf276cStholo {"ignore-all-space", 0, 0, 'w'},
14143c1707eStholo {"side-by-side", 0, 0, 'y'},
14250bf276cStholo {"unified", 2, 0, 146},
14350bf276cStholo {"left-column", 0, 0, 129},
14450bf276cStholo {"suppress-common-lines", 0, 0, 130},
14550bf276cStholo {"old-line-format", 1, 0, 132},
14650bf276cStholo {"new-line-format", 1, 0, 133},
14750bf276cStholo {"unchanged-line-format", 1, 0, 134},
14850bf276cStholo {"line-format", 1, 0, 135},
14950bf276cStholo {"old-group-format", 1, 0, 136},
15050bf276cStholo {"new-group-format", 1, 0, 137},
15150bf276cStholo {"unchanged-group-format", 1, 0, 138},
15250bf276cStholo {"changed-group-format", 1, 0, 139},
15350bf276cStholo {"horizon-lines", 1, 0, 140},
15450bf276cStholo {"binary", 0, 0, 142},
15550bf276cStholo {0, 0, 0, 0}
15650bf276cStholo };
15750bf276cStholo
1582286d8edStholo /* CVS 1.9 and similar versions seemed to have pretty weird handling
1592286d8edStholo of -y and -T. In the cases where it called rcsdiff,
1602286d8edStholo they would have the meanings mentioned below. In the cases where it
1612286d8edStholo called diff, they would have the meanings mentioned in "longopts".
1622286d8edStholo Noone seems to have missed them, so I think the right thing to do is
1632286d8edStholo just to remove the options altogether (which I have done).
1642286d8edStholo
1652286d8edStholo In the case of -z and -q, "cvs diff" did not accept them even back
1662286d8edStholo when we called rcsdiff (at least, it hasn't accepted them
1672286d8edStholo recently).
1682286d8edStholo
1692286d8edStholo In comparing rcsdiff to the new CVS implementation, I noticed that
1702286d8edStholo the following rcsdiff flags are not handled by CVS diff:
1712286d8edStholo
1722286d8edStholo -y: perform diff even when the requested revisions are the
1732286d8edStholo same revision number
1742286d8edStholo -q: run quietly
1752286d8edStholo -T: preserve modification time on the RCS file
1762286d8edStholo -z: specify timezone for use in file labels
1772286d8edStholo
1782286d8edStholo I think these are not really relevant. -y is undocumented even in
1792286d8edStholo RCS 5.7, and seems like a minor change at best. According to RCS
1802286d8edStholo documentation, -T only applies when a RCS file has been modified
1812286d8edStholo because of lock changes; doesn't CVS sidestep RCS's entire lock
1822286d8edStholo structure? -z seems to be unsupported by CVS diff, and has a
1832286d8edStholo different meaning as a global option anyway. (Adding it could be
1842286d8edStholo a feature, but if it is left out for now, it should not break
1852286d8edStholo anything.) For the purposes of producing output, CVS diff appears
1862286d8edStholo mostly to ignore -q. Maybe this should be fixed, but I think it's
1872286d8edStholo a larger issue than the changes included here. */
1882286d8edStholo
1891e72d8d2Sderaadt int
diff(argc,argv)1901e72d8d2Sderaadt diff (argc, argv)
1911e72d8d2Sderaadt int argc;
1921e72d8d2Sderaadt char **argv;
1931e72d8d2Sderaadt {
1941e72d8d2Sderaadt char tmp[50];
1951e72d8d2Sderaadt int c, err = 0;
1961e72d8d2Sderaadt int local = 0;
1971e72d8d2Sderaadt int which;
19850bf276cStholo int option_index;
1991e72d8d2Sderaadt
2001e72d8d2Sderaadt if (argc == -1)
2011e72d8d2Sderaadt usage (diff_usage);
2021e72d8d2Sderaadt
2032286d8edStholo have_rev1_label = have_rev2_label = 0;
2042286d8edStholo
2051e72d8d2Sderaadt /*
2061e72d8d2Sderaadt * Note that we catch all the valid arguments here, so that we can
2071e72d8d2Sderaadt * intercept the -r arguments for doing revision diffs; and -l/-R for a
2081e72d8d2Sderaadt * non-recursive/recursive diff.
2091e72d8d2Sderaadt */
210461cc63eStholo
211c71bc7e2Stholo /* Clean out our global variables (multiroot can call us multiple
212c71bc7e2Stholo times and the server can too, if the client sends several
213c71bc7e2Stholo diff commands). */
214461cc63eStholo if (opts == NULL)
215461cc63eStholo {
216461cc63eStholo opts_allocated = 1;
217461cc63eStholo opts = xmalloc (opts_allocated);
218461cc63eStholo }
2191e72d8d2Sderaadt opts[0] = '\0';
220c71bc7e2Stholo diff_rev1 = NULL;
221c71bc7e2Stholo diff_rev2 = NULL;
222c71bc7e2Stholo diff_date1 = NULL;
223c71bc7e2Stholo diff_date2 = NULL;
224461cc63eStholo
2252770ece5Stholo optind = 0;
22650bf276cStholo while ((c = getopt_long (argc, argv,
22743c1707eStholo "+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:",
22850bf276cStholo longopts, &option_index)) != -1)
2291e72d8d2Sderaadt {
2301e72d8d2Sderaadt switch (c)
2311e72d8d2Sderaadt {
2321e72d8d2Sderaadt case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
23350bf276cStholo case 'h': case 'i': case 'n': case 'p': case 's': case 't':
23443c1707eStholo case 'u': case 'w': case 'y':
23543c1707eStholo case '0': case '1': case '2': case '3': case '4': case '5':
23643c1707eStholo case '6': case '7': case '8': case '9':
23743c1707eStholo case 'B': case 'H': case 'T':
2381e72d8d2Sderaadt (void) sprintf (tmp, " -%c", (char) c);
23943c1707eStholo allocate_and_strcat (&opts, &opts_allocated, tmp);
2401e72d8d2Sderaadt break;
2412286d8edStholo case 'L':
2422286d8edStholo if (have_rev1_label++)
2432286d8edStholo if (have_rev2_label++)
2442286d8edStholo {
2452286d8edStholo error (0, 0, "extra -L arguments ignored");
2462286d8edStholo break;
2472286d8edStholo }
2482286d8edStholo
24943c1707eStholo allocate_and_strcat (&opts, &opts_allocated, " -L");
25043c1707eStholo allocate_and_strcat (&opts, &opts_allocated, optarg);
2512286d8edStholo break;
2522286d8edStholo case 'C': case 'F': case 'I': case 'U': case 'V': case 'W':
25350bf276cStholo (void) sprintf (tmp, " -%c", (char) c);
25443c1707eStholo allocate_and_strcat (&opts, &opts_allocated, tmp);
25543c1707eStholo allocate_and_strcat (&opts, &opts_allocated, optarg);
25650bf276cStholo break;
2572286d8edStholo case 131:
25850bf276cStholo /* --ifdef. */
25943c1707eStholo allocate_and_strcat (&opts, &opts_allocated, " --ifdef=");
26043c1707eStholo allocate_and_strcat (&opts, &opts_allocated, optarg);
26150bf276cStholo break;
2622286d8edStholo case 129: case 130: case 132: case 133: case 134:
26350bf276cStholo case 135: case 136: case 137: case 138: case 139: case 140:
26450bf276cStholo case 141: case 142: case 143: case 144: case 145: case 146:
26543c1707eStholo allocate_and_strcat (&opts, &opts_allocated, " --");
26643c1707eStholo allocate_and_strcat (&opts, &opts_allocated,
267461cc63eStholo longopts[option_index].name);
26850bf276cStholo if (longopts[option_index].has_arg == 1
26950bf276cStholo || (longopts[option_index].has_arg == 2
27050bf276cStholo && optarg != NULL))
27150bf276cStholo {
27243c1707eStholo allocate_and_strcat (&opts, &opts_allocated, "=");
27343c1707eStholo allocate_and_strcat (&opts, &opts_allocated, optarg);
27450bf276cStholo }
2751e72d8d2Sderaadt break;
2761e72d8d2Sderaadt case 'R':
2771e72d8d2Sderaadt local = 0;
2781e72d8d2Sderaadt break;
2791e72d8d2Sderaadt case 'l':
2801e72d8d2Sderaadt local = 1;
2811e72d8d2Sderaadt break;
2821e72d8d2Sderaadt case 'k':
2831e72d8d2Sderaadt if (options)
2841e72d8d2Sderaadt free (options);
2851e72d8d2Sderaadt options = RCS_check_kflag (optarg);
2861e72d8d2Sderaadt break;
2871e72d8d2Sderaadt case 'r':
2881e72d8d2Sderaadt if (diff_rev2 != NULL || diff_date2 != NULL)
2891e72d8d2Sderaadt error (1, 0,
2901e72d8d2Sderaadt "no more than two revisions/dates can be specified");
2911e72d8d2Sderaadt if (diff_rev1 != NULL || diff_date1 != NULL)
2921e72d8d2Sderaadt diff_rev2 = optarg;
2931e72d8d2Sderaadt else
2941e72d8d2Sderaadt diff_rev1 = optarg;
2951e72d8d2Sderaadt break;
2961e72d8d2Sderaadt case 'D':
2971e72d8d2Sderaadt if (diff_rev2 != NULL || diff_date2 != NULL)
2981e72d8d2Sderaadt error (1, 0,
2991e72d8d2Sderaadt "no more than two revisions/dates can be specified");
3001e72d8d2Sderaadt if (diff_rev1 != NULL || diff_date1 != NULL)
3011e72d8d2Sderaadt diff_date2 = Make_Date (optarg);
3021e72d8d2Sderaadt else
3031e72d8d2Sderaadt diff_date1 = Make_Date (optarg);
3041e72d8d2Sderaadt break;
3051e72d8d2Sderaadt case 'N':
3061e72d8d2Sderaadt empty_files = 1;
3071e72d8d2Sderaadt break;
3081e72d8d2Sderaadt case '?':
3091e72d8d2Sderaadt default:
3101e72d8d2Sderaadt usage (diff_usage);
3111e72d8d2Sderaadt break;
3121e72d8d2Sderaadt }
3131e72d8d2Sderaadt }
3141e72d8d2Sderaadt argc -= optind;
3151e72d8d2Sderaadt argv += optind;
3161e72d8d2Sderaadt
3171e72d8d2Sderaadt /* make sure options is non-null */
3181e72d8d2Sderaadt if (!options)
3191e72d8d2Sderaadt options = xstrdup ("");
3201e72d8d2Sderaadt
3211e72d8d2Sderaadt #ifdef CLIENT_SUPPORT
32243c1707eStholo if (current_parsed_root->isremote) {
3231e72d8d2Sderaadt /* We're the client side. Fire up the remote server. */
3241e72d8d2Sderaadt start_server ();
3251e72d8d2Sderaadt
3261e72d8d2Sderaadt ign_setup ();
3271e72d8d2Sderaadt
3281e72d8d2Sderaadt if (local)
3291e72d8d2Sderaadt send_arg("-l");
3301e72d8d2Sderaadt if (empty_files)
3311e72d8d2Sderaadt send_arg("-N");
3321e72d8d2Sderaadt send_option_string (opts);
333780d15dfStholo if (options[0] != '\0')
334780d15dfStholo send_arg (options);
3351e72d8d2Sderaadt if (diff_rev1)
3361e72d8d2Sderaadt option_with_arg ("-r", diff_rev1);
3371e72d8d2Sderaadt if (diff_date1)
3381e72d8d2Sderaadt client_senddate (diff_date1);
3391e72d8d2Sderaadt if (diff_rev2)
3401e72d8d2Sderaadt option_with_arg ("-r", diff_rev2);
3411e72d8d2Sderaadt if (diff_date2)
3421e72d8d2Sderaadt client_senddate (diff_date2);
3431e72d8d2Sderaadt
3441e72d8d2Sderaadt /* Send the current files unless diffing two revs from the archive */
3451e72d8d2Sderaadt if (diff_rev2 == NULL && diff_date2 == NULL)
346b6c02222Stholo send_files (argc, argv, local, 0, 0);
347b6c02222Stholo else
348b6c02222Stholo send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
3491e72d8d2Sderaadt
350c71bc7e2Stholo send_file_names (argc, argv, SEND_EXPAND_WILD);
351c71bc7e2Stholo
35213571821Stholo send_to_server ("diff\012", 0);
3531e72d8d2Sderaadt err = get_responses_and_close ();
3541e72d8d2Sderaadt free (options);
355c71bc7e2Stholo options = NULL;
3561e72d8d2Sderaadt return (err);
3571e72d8d2Sderaadt }
3581e72d8d2Sderaadt #endif
3591e72d8d2Sderaadt
36013571821Stholo if (diff_rev1 != NULL)
36113571821Stholo tag_check_valid (diff_rev1, argc, argv, local, 0, "");
36213571821Stholo if (diff_rev2 != NULL)
36313571821Stholo tag_check_valid (diff_rev2, argc, argv, local, 0, "");
36413571821Stholo
3651e72d8d2Sderaadt which = W_LOCAL;
36650bf276cStholo if (diff_rev1 != NULL || diff_date1 != NULL)
3671e72d8d2Sderaadt which |= W_REPOS | W_ATTIC;
3681e72d8d2Sderaadt
3691e72d8d2Sderaadt wrap_setup ();
3701e72d8d2Sderaadt
3711e72d8d2Sderaadt /* start the recursion processor */
3721e72d8d2Sderaadt err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
37350bf276cStholo diff_dirleaveproc, NULL, argc, argv, local,
37450bf276cStholo which, 0, 1, (char *) NULL, 1);
3751e72d8d2Sderaadt
3761e72d8d2Sderaadt /* clean up */
3771e72d8d2Sderaadt free (options);
378c71bc7e2Stholo options = NULL;
379c71bc7e2Stholo
380c71bc7e2Stholo if (diff_date1 != NULL)
381c71bc7e2Stholo free (diff_date1);
382c71bc7e2Stholo if (diff_date2 != NULL)
383c71bc7e2Stholo free (diff_date2);
384c71bc7e2Stholo
3851e72d8d2Sderaadt return (err);
3861e72d8d2Sderaadt }
3871e72d8d2Sderaadt
3881e72d8d2Sderaadt /*
3891e72d8d2Sderaadt * Do a file diff
3901e72d8d2Sderaadt */
3911e72d8d2Sderaadt /* ARGSUSED */
3921e72d8d2Sderaadt static int
diff_fileproc(callerdat,finfo)39350bf276cStholo diff_fileproc (callerdat, finfo)
39450bf276cStholo void *callerdat;
395c26070a5Stholo struct file_info *finfo;
3961e72d8d2Sderaadt {
3971e72d8d2Sderaadt int status, err = 2; /* 2 == trouble, like rcsdiff */
3981e72d8d2Sderaadt Vers_TS *vers;
39950bf276cStholo enum diff_file empty_file = DIFF_DIFFERENT;
40050bf276cStholo char *tmp;
4011e72d8d2Sderaadt char *tocvsPath;
402461cc63eStholo char *fname;
40343c1707eStholo char *label1;
40443c1707eStholo char *label2;
4051e72d8d2Sderaadt
4062286d8edStholo /* Initialize these solely to avoid warnings from gcc -Wall about
4072286d8edStholo variables that might be used uninitialized. */
4082286d8edStholo tmp = NULL;
4092286d8edStholo fname = NULL;
4102286d8edStholo
4111e72d8d2Sderaadt user_file_rev = 0;
41250bf276cStholo vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0);
4131e72d8d2Sderaadt
4141e72d8d2Sderaadt if (diff_rev2 != NULL || diff_date2 != NULL)
4151e72d8d2Sderaadt {
4161e72d8d2Sderaadt /* Skip all the following checks regarding the user file; we're
4171e72d8d2Sderaadt not using it. */
4181e72d8d2Sderaadt }
4191e72d8d2Sderaadt else if (vers->vn_user == NULL)
4201e72d8d2Sderaadt {
42150bf276cStholo /* The file does not exist in the working directory. */
42250bf276cStholo if ((diff_rev1 != NULL || diff_date1 != NULL)
42350bf276cStholo && vers->srcfile != NULL)
42450bf276cStholo {
42550bf276cStholo /* The file does exist in the repository. */
42650bf276cStholo if (empty_files)
42750bf276cStholo empty_file = DIFF_REMOVED;
42850bf276cStholo else
42950bf276cStholo {
43050bf276cStholo int exists;
43150bf276cStholo
43250bf276cStholo exists = 0;
43350bf276cStholo /* special handling for TAG_HEAD */
43450bf276cStholo if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
435b6f6614eStholo {
436b6f6614eStholo char *head =
437b6f6614eStholo (vers->vn_rcs == NULL
438b6f6614eStholo ? NULL
439b6f6614eStholo : RCS_branch_head (vers->srcfile, vers->vn_rcs));
440b6f6614eStholo exists = head != NULL;
441b6f6614eStholo if (head != NULL)
442b6f6614eStholo free (head);
443b6f6614eStholo }
44450bf276cStholo else
44550bf276cStholo {
44650bf276cStholo Vers_TS *xvers;
44750bf276cStholo
44850bf276cStholo xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1,
44950bf276cStholo 1, 0);
45050bf276cStholo exists = xvers->vn_rcs != NULL;
45150bf276cStholo freevers_ts (&xvers);
45250bf276cStholo }
45350bf276cStholo if (exists)
45450bf276cStholo error (0, 0,
45550bf276cStholo "%s no longer exists, no comparison available",
45650bf276cStholo finfo->fullname);
45750bf276cStholo freevers_ts (&vers);
45850bf276cStholo diff_mark_errors (err);
45950bf276cStholo return (err);
46050bf276cStholo }
46150bf276cStholo }
46250bf276cStholo else
46350bf276cStholo {
464c2c61682Stholo error (0, 0, "I know nothing about %s", finfo->fullname);
4651e72d8d2Sderaadt freevers_ts (&vers);
4661e72d8d2Sderaadt diff_mark_errors (err);
4671e72d8d2Sderaadt return (err);
4681e72d8d2Sderaadt }
46950bf276cStholo }
4701e72d8d2Sderaadt else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
4711e72d8d2Sderaadt {
4721e72d8d2Sderaadt if (empty_files)
4731e72d8d2Sderaadt empty_file = DIFF_ADDED;
4741e72d8d2Sderaadt else
4751e72d8d2Sderaadt {
476c2c61682Stholo error (0, 0, "%s is a new entry, no comparison available",
477c2c61682Stholo finfo->fullname);
4781e72d8d2Sderaadt freevers_ts (&vers);
4791e72d8d2Sderaadt diff_mark_errors (err);
4801e72d8d2Sderaadt return (err);
4811e72d8d2Sderaadt }
4821e72d8d2Sderaadt }
4831e72d8d2Sderaadt else if (vers->vn_user[0] == '-')
4841e72d8d2Sderaadt {
4851e72d8d2Sderaadt if (empty_files)
4861e72d8d2Sderaadt empty_file = DIFF_REMOVED;
4871e72d8d2Sderaadt else
4881e72d8d2Sderaadt {
489c2c61682Stholo error (0, 0, "%s was removed, no comparison available",
490c2c61682Stholo finfo->fullname);
4911e72d8d2Sderaadt freevers_ts (&vers);
4921e72d8d2Sderaadt diff_mark_errors (err);
4931e72d8d2Sderaadt return (err);
4941e72d8d2Sderaadt }
4951e72d8d2Sderaadt }
4961e72d8d2Sderaadt else
4971e72d8d2Sderaadt {
4981e72d8d2Sderaadt if (vers->vn_rcs == NULL && vers->srcfile == NULL)
4991e72d8d2Sderaadt {
500c2c61682Stholo error (0, 0, "cannot find revision control file for %s",
501c2c61682Stholo finfo->fullname);
5021e72d8d2Sderaadt freevers_ts (&vers);
5031e72d8d2Sderaadt diff_mark_errors (err);
5041e72d8d2Sderaadt return (err);
5051e72d8d2Sderaadt }
5061e72d8d2Sderaadt else
5071e72d8d2Sderaadt {
5081e72d8d2Sderaadt if (vers->ts_user == NULL)
5091e72d8d2Sderaadt {
510c2c61682Stholo error (0, 0, "cannot find %s", finfo->fullname);
5111e72d8d2Sderaadt freevers_ts (&vers);
5121e72d8d2Sderaadt diff_mark_errors (err);
5131e72d8d2Sderaadt return (err);
5141e72d8d2Sderaadt }
5151e72d8d2Sderaadt else if (!strcmp (vers->ts_user, vers->ts_rcs))
5161e72d8d2Sderaadt {
5171e72d8d2Sderaadt /* The user file matches some revision in the repository
5181e72d8d2Sderaadt Diff against the repository (for remote CVS, we might not
5191e72d8d2Sderaadt have a copy of the user file around). */
5201e72d8d2Sderaadt user_file_rev = vers->vn_user;
5211e72d8d2Sderaadt }
5221e72d8d2Sderaadt }
5231e72d8d2Sderaadt }
5241e72d8d2Sderaadt
52550bf276cStholo empty_file = diff_file_nodiff (finfo, vers, empty_file);
52650bf276cStholo if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR)
52750bf276cStholo {
52850bf276cStholo freevers_ts (&vers);
52950bf276cStholo if (empty_file == DIFF_SAME)
5302770ece5Stholo {
5312770ece5Stholo /* In the server case, would be nice to send a "Checked-in"
5322770ece5Stholo response, so that the client can rewrite its timestamp.
5332770ece5Stholo server_checked_in by itself isn't the right thing (it
5342770ece5Stholo needs a server_register), but I'm not sure what is.
5352770ece5Stholo It isn't clear to me how "cvs status" handles this (that
5362770ece5Stholo is, for a client which sends Modified not Is-modified to
5372770ece5Stholo "cvs status"), but it does. */
53850bf276cStholo return (0);
5392770ece5Stholo }
54050bf276cStholo else
54150bf276cStholo {
54250bf276cStholo diff_mark_errors (err);
54350bf276cStholo return (err);
54450bf276cStholo }
54550bf276cStholo }
54650bf276cStholo
54750bf276cStholo if (empty_file == DIFF_DIFFERENT)
54850bf276cStholo {
54950bf276cStholo int dead1, dead2;
55050bf276cStholo
55150bf276cStholo if (use_rev1 == NULL)
55250bf276cStholo dead1 = 0;
55350bf276cStholo else
55450bf276cStholo dead1 = RCS_isdead (vers->srcfile, use_rev1);
55550bf276cStholo if (use_rev2 == NULL)
55650bf276cStholo dead2 = 0;
55750bf276cStholo else
55850bf276cStholo dead2 = RCS_isdead (vers->srcfile, use_rev2);
55950bf276cStholo
56050bf276cStholo if (dead1 && dead2)
5611e72d8d2Sderaadt {
5621e72d8d2Sderaadt freevers_ts (&vers);
5631e72d8d2Sderaadt return (0);
5641e72d8d2Sderaadt }
56550bf276cStholo else if (dead1)
56650bf276cStholo {
56750bf276cStholo if (empty_files)
56850bf276cStholo empty_file = DIFF_ADDED;
56950bf276cStholo else
57050bf276cStholo {
57150bf276cStholo error (0, 0, "%s is a new entry, no comparison available",
57250bf276cStholo finfo->fullname);
57350bf276cStholo freevers_ts (&vers);
57450bf276cStholo diff_mark_errors (err);
57550bf276cStholo return (err);
57650bf276cStholo }
57750bf276cStholo }
57850bf276cStholo else if (dead2)
57950bf276cStholo {
58050bf276cStholo if (empty_files)
58150bf276cStholo empty_file = DIFF_REMOVED;
58250bf276cStholo else
58350bf276cStholo {
58450bf276cStholo error (0, 0, "%s was removed, no comparison available",
58550bf276cStholo finfo->fullname);
58650bf276cStholo freevers_ts (&vers);
58750bf276cStholo diff_mark_errors (err);
58850bf276cStholo return (err);
58950bf276cStholo }
59050bf276cStholo }
59150bf276cStholo }
5921e72d8d2Sderaadt
5931e72d8d2Sderaadt /* Output an "Index:" line for patch to use */
5942286d8edStholo cvs_output ("Index: ", 0);
5952286d8edStholo cvs_output (finfo->fullname, 0);
5962286d8edStholo cvs_output ("\n", 1);
5971e72d8d2Sderaadt
598c26070a5Stholo tocvsPath = wrap_tocvs_process_file(finfo->file);
5991e72d8d2Sderaadt if (tocvsPath)
6001e72d8d2Sderaadt {
6011e72d8d2Sderaadt /* Backup the current version of the file to CVS/,,filename */
602461cc63eStholo fname = xmalloc (strlen (finfo->file)
603461cc63eStholo + sizeof CVSADM
604461cc63eStholo + sizeof CVSPREFIX
605461cc63eStholo + 10);
606c26070a5Stholo sprintf(fname,"%s/%s%s",CVSADM, CVSPREFIX, finfo->file);
6071e72d8d2Sderaadt if (unlink_file_dir (fname) < 0)
60813571821Stholo if (! existence_error (errno))
609461cc63eStholo error (1, errno, "cannot remove %s", fname);
610c26070a5Stholo rename_file (finfo->file, fname);
6111e72d8d2Sderaadt /* Copy the wrapped file to the current directory then go to work */
612c26070a5Stholo copy_file (tocvsPath, finfo->file);
6131e72d8d2Sderaadt }
6141e72d8d2Sderaadt
61543c1707eStholo /* Set up file labels appropriate for compatibility with the Larry Wall
61643c1707eStholo * implementation of patch if the user didn't specify. This is irrelevant
61743c1707eStholo * according to the POSIX.2 specification.
61843c1707eStholo */
61943c1707eStholo label1 = NULL;
62043c1707eStholo label2 = NULL;
62143c1707eStholo if (!have_rev1_label)
62243c1707eStholo {
62343c1707eStholo if (empty_file == DIFF_ADDED)
62443c1707eStholo label1 =
62543c1707eStholo make_file_label (DEVNULL, NULL, NULL);
62643c1707eStholo else
62743c1707eStholo label1 =
62843c1707eStholo make_file_label (finfo->fullname, use_rev1, vers ? vers->srcfile : NULL);
62943c1707eStholo }
63043c1707eStholo
63143c1707eStholo if (!have_rev2_label)
63243c1707eStholo {
63343c1707eStholo if (empty_file == DIFF_REMOVED)
63443c1707eStholo label2 =
63543c1707eStholo make_file_label (DEVNULL, NULL, NULL);
63643c1707eStholo else
63743c1707eStholo label2 =
63843c1707eStholo make_file_label (finfo->fullname, use_rev2, vers ? vers->srcfile : NULL);
63943c1707eStholo }
64043c1707eStholo
6411e72d8d2Sderaadt if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED)
6421e72d8d2Sderaadt {
64343c1707eStholo /* This is fullname, not file, possibly despite the POSIX.2
64443c1707eStholo * specification, because that's the way all the Larry Wall
64543c1707eStholo * implementations of patch (are there other implementations?) want
64643c1707eStholo * things and the POSIX.2 spec appears to leave room for this.
64743c1707eStholo */
6482286d8edStholo cvs_output ("\
6492286d8edStholo ===================================================================\n\
6502286d8edStholo RCS file: ", 0);
65143c1707eStholo cvs_output (finfo->fullname, 0);
6522286d8edStholo cvs_output ("\n", 1);
6532286d8edStholo
6542286d8edStholo cvs_output ("diff -N ", 0);
65543c1707eStholo cvs_output (finfo->fullname, 0);
6562286d8edStholo cvs_output ("\n", 1);
6571e72d8d2Sderaadt
6581e72d8d2Sderaadt if (empty_file == DIFF_ADDED)
6591e72d8d2Sderaadt {
66050bf276cStholo if (use_rev2 == NULL)
66143c1707eStholo status = diff_exec (DEVNULL, finfo->file, label1, label2, opts, RUN_TTY);
66250bf276cStholo else
66350bf276cStholo {
66450bf276cStholo int retcode;
66550bf276cStholo
66650bf276cStholo tmp = cvs_temp_name ();
66750bf276cStholo retcode = RCS_checkout (vers->srcfile, (char *) NULL,
66850bf276cStholo use_rev2, (char *) NULL,
66950bf276cStholo (*options
67050bf276cStholo ? options
67150bf276cStholo : vers->options),
672b6c02222Stholo tmp, (RCSCHECKOUTPROC) NULL,
673b6c02222Stholo (void *) NULL);
6745e617892Stholo if (retcode != 0)
67550bf276cStholo {
6765e617892Stholo diff_mark_errors (err);
6775e617892Stholo return err;
67850bf276cStholo }
67950bf276cStholo
68043c1707eStholo status = diff_exec (DEVNULL, tmp, label1, label2, opts, RUN_TTY);
68150bf276cStholo }
6821e72d8d2Sderaadt }
6831e72d8d2Sderaadt else
6841e72d8d2Sderaadt {
685c26070a5Stholo int retcode;
686c26070a5Stholo
68750bf276cStholo tmp = cvs_temp_name ();
68850bf276cStholo retcode = RCS_checkout (vers->srcfile, (char *) NULL,
68950bf276cStholo use_rev1, (char *) NULL,
69050bf276cStholo *options ? options : vers->options,
691b6c02222Stholo tmp, (RCSCHECKOUTPROC) NULL,
692b6c02222Stholo (void *) NULL);
6935e617892Stholo if (retcode != 0)
6941e72d8d2Sderaadt {
6955e617892Stholo diff_mark_errors (err);
6965e617892Stholo return err;
6971e72d8d2Sderaadt }
6981e72d8d2Sderaadt
69943c1707eStholo status = diff_exec (tmp, DEVNULL, label1, label2, opts, RUN_TTY);
7001e72d8d2Sderaadt }
7011e72d8d2Sderaadt }
7021e72d8d2Sderaadt else
7031e72d8d2Sderaadt {
7042286d8edStholo status = RCS_exec_rcsdiff (vers->srcfile, opts,
7052286d8edStholo *options ? options : vers->options,
7062286d8edStholo use_rev1, use_rev2,
7072286d8edStholo label1, label2,
7082286d8edStholo finfo->file);
7092286d8edStholo
7102286d8edStholo if (label1) free (label1);
7112286d8edStholo if (label2) free (label2);
7121e72d8d2Sderaadt }
7131e72d8d2Sderaadt
7142286d8edStholo switch (status)
7151e72d8d2Sderaadt {
7161e72d8d2Sderaadt case -1: /* fork failed */
7172286d8edStholo error (1, errno, "fork failed while diffing %s",
7181e72d8d2Sderaadt vers->srcfile->path);
7191e72d8d2Sderaadt case 0: /* everything ok */
7201e72d8d2Sderaadt err = 0;
7211e72d8d2Sderaadt break;
7221e72d8d2Sderaadt default: /* other error */
7231e72d8d2Sderaadt err = status;
7241e72d8d2Sderaadt break;
7251e72d8d2Sderaadt }
7261e72d8d2Sderaadt
7271e72d8d2Sderaadt if (tocvsPath)
7281e72d8d2Sderaadt {
729c26070a5Stholo if (unlink_file_dir (finfo->file) < 0)
73013571821Stholo if (! existence_error (errno))
731c26070a5Stholo error (1, errno, "cannot remove %s", finfo->file);
7321e72d8d2Sderaadt
733c26070a5Stholo rename_file (fname, finfo->file);
7341e72d8d2Sderaadt if (unlink_file (tocvsPath) < 0)
735461cc63eStholo error (1, errno, "cannot remove %s", tocvsPath);
736461cc63eStholo free (fname);
7371e72d8d2Sderaadt }
7381e72d8d2Sderaadt
73950bf276cStholo if (empty_file == DIFF_REMOVED
74050bf276cStholo || (empty_file == DIFF_ADDED && use_rev2 != NULL))
74150bf276cStholo {
7425e617892Stholo if (CVS_UNLINK (tmp) < 0)
7435e617892Stholo error (0, errno, "cannot remove %s", tmp);
74450bf276cStholo free (tmp);
74550bf276cStholo }
7461e72d8d2Sderaadt
7471e72d8d2Sderaadt freevers_ts (&vers);
7481e72d8d2Sderaadt diff_mark_errors (err);
7491e72d8d2Sderaadt return (err);
7501e72d8d2Sderaadt }
7511e72d8d2Sderaadt
7521e72d8d2Sderaadt /*
7531e72d8d2Sderaadt * Remember the exit status for each file.
7541e72d8d2Sderaadt */
7551e72d8d2Sderaadt static void
diff_mark_errors(err)7561e72d8d2Sderaadt diff_mark_errors (err)
7571e72d8d2Sderaadt int err;
7581e72d8d2Sderaadt {
7591e72d8d2Sderaadt if (err > diff_errors)
7601e72d8d2Sderaadt diff_errors = err;
7611e72d8d2Sderaadt }
7621e72d8d2Sderaadt
7631e72d8d2Sderaadt /*
7641e72d8d2Sderaadt * Print a warm fuzzy message when we enter a dir
76513571821Stholo *
76613571821Stholo * Don't try to diff directories that don't exist! -- DW
7671e72d8d2Sderaadt */
7681e72d8d2Sderaadt /* ARGSUSED */
7691e72d8d2Sderaadt static Dtype
diff_dirproc(callerdat,dir,pos_repos,update_dir,entries)77050bf276cStholo diff_dirproc (callerdat, dir, pos_repos, update_dir, entries)
77150bf276cStholo void *callerdat;
7721e72d8d2Sderaadt char *dir;
7731e72d8d2Sderaadt char *pos_repos;
7741e72d8d2Sderaadt char *update_dir;
77550bf276cStholo List *entries;
7761e72d8d2Sderaadt {
7771e72d8d2Sderaadt /* XXX - check for dirs we don't want to process??? */
77813571821Stholo
77913571821Stholo /* YES ... for instance dirs that don't exist!!! -- DW */
78013571821Stholo if (!isdir (dir))
78113571821Stholo return (R_SKIP_ALL);
78213571821Stholo
7831e72d8d2Sderaadt if (!quiet)
7841e72d8d2Sderaadt error (0, 0, "Diffing %s", update_dir);
7851e72d8d2Sderaadt return (R_PROCESS);
7861e72d8d2Sderaadt }
7871e72d8d2Sderaadt
7881e72d8d2Sderaadt /*
7891e72d8d2Sderaadt * Concoct the proper exit status - done with files
7901e72d8d2Sderaadt */
7911e72d8d2Sderaadt /* ARGSUSED */
7921e72d8d2Sderaadt static int
diff_filesdoneproc(callerdat,err,repos,update_dir,entries)79350bf276cStholo diff_filesdoneproc (callerdat, err, repos, update_dir, entries)
79450bf276cStholo void *callerdat;
7951e72d8d2Sderaadt int err;
7961e72d8d2Sderaadt char *repos;
7971e72d8d2Sderaadt char *update_dir;
79850bf276cStholo List *entries;
7991e72d8d2Sderaadt {
8001e72d8d2Sderaadt return (diff_errors);
8011e72d8d2Sderaadt }
8021e72d8d2Sderaadt
8031e72d8d2Sderaadt /*
8041e72d8d2Sderaadt * Concoct the proper exit status - leaving directories
8051e72d8d2Sderaadt */
8061e72d8d2Sderaadt /* ARGSUSED */
8071e72d8d2Sderaadt static int
diff_dirleaveproc(callerdat,dir,err,update_dir,entries)80850bf276cStholo diff_dirleaveproc (callerdat, dir, err, update_dir, entries)
80950bf276cStholo void *callerdat;
8101e72d8d2Sderaadt char *dir;
8111e72d8d2Sderaadt int err;
8121e72d8d2Sderaadt char *update_dir;
81350bf276cStholo List *entries;
8141e72d8d2Sderaadt {
8151e72d8d2Sderaadt return (diff_errors);
8161e72d8d2Sderaadt }
8171e72d8d2Sderaadt
8181e72d8d2Sderaadt /*
81950bf276cStholo * verify that a file is different
8201e72d8d2Sderaadt */
82150bf276cStholo static enum diff_file
diff_file_nodiff(finfo,vers,empty_file)82250bf276cStholo diff_file_nodiff (finfo, vers, empty_file)
82350bf276cStholo struct file_info *finfo;
8241e72d8d2Sderaadt Vers_TS *vers;
82550bf276cStholo enum diff_file empty_file;
8261e72d8d2Sderaadt {
8271e72d8d2Sderaadt Vers_TS *xvers;
828c26070a5Stholo int retcode;
8291e72d8d2Sderaadt
8301e72d8d2Sderaadt /* free up any old use_rev* variables and reset 'em */
8311e72d8d2Sderaadt if (use_rev1)
8321e72d8d2Sderaadt free (use_rev1);
8331e72d8d2Sderaadt if (use_rev2)
8341e72d8d2Sderaadt free (use_rev2);
8351e72d8d2Sderaadt use_rev1 = use_rev2 = (char *) NULL;
8361e72d8d2Sderaadt
8371e72d8d2Sderaadt if (diff_rev1 || diff_date1)
8381e72d8d2Sderaadt {
8391e72d8d2Sderaadt /* special handling for TAG_HEAD */
8401e72d8d2Sderaadt if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
841b6f6614eStholo use_rev1 = ((vers->vn_rcs == NULL || vers->srcfile == NULL)
842b6f6614eStholo ? NULL
843b6f6614eStholo : RCS_branch_head (vers->srcfile, vers->vn_rcs));
8441e72d8d2Sderaadt else
8451e72d8d2Sderaadt {
84650bf276cStholo xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0);
84750bf276cStholo if (xvers->vn_rcs != NULL)
8481e72d8d2Sderaadt use_rev1 = xstrdup (xvers->vn_rcs);
8491e72d8d2Sderaadt freevers_ts (&xvers);
8501e72d8d2Sderaadt }
8511e72d8d2Sderaadt }
8521e72d8d2Sderaadt if (diff_rev2 || diff_date2)
8531e72d8d2Sderaadt {
8541e72d8d2Sderaadt /* special handling for TAG_HEAD */
8551e72d8d2Sderaadt if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
856b6f6614eStholo use_rev2 = ((vers->vn_rcs == NULL || vers->srcfile == NULL)
857b6f6614eStholo ? NULL
858b6f6614eStholo : RCS_branch_head (vers->srcfile, vers->vn_rcs));
8591e72d8d2Sderaadt else
8601e72d8d2Sderaadt {
86150bf276cStholo xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0);
86250bf276cStholo if (xvers->vn_rcs != NULL)
8631e72d8d2Sderaadt use_rev2 = xstrdup (xvers->vn_rcs);
8641e72d8d2Sderaadt freevers_ts (&xvers);
8651e72d8d2Sderaadt }
8661e72d8d2Sderaadt
86750bf276cStholo if (use_rev1 == NULL)
86850bf276cStholo {
86950bf276cStholo /* The first revision does not exist. If EMPTY_FILES is
87050bf276cStholo true, treat this as an added file. Otherwise, warn
87150bf276cStholo about the missing tag. */
872*d7942398Ssturm if (use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) )
873780d15dfStholo /* At least in the case where DIFF_REV1 and DIFF_REV2
874780d15dfStholo are both numeric, we should be returning some kind
875780d15dfStholo of error (see basicb-8a0 in testsuite). The symbolic
876780d15dfStholo case may be more complicated. */
87750bf276cStholo return DIFF_SAME;
87850bf276cStholo else if (empty_files)
87950bf276cStholo return DIFF_ADDED;
88050bf276cStholo else if (diff_rev1)
88150bf276cStholo error (0, 0, "tag %s is not in file %s", diff_rev1,
88250bf276cStholo finfo->fullname);
88350bf276cStholo else
88450bf276cStholo error (0, 0, "no revision for date %s in file %s",
88550bf276cStholo diff_date1, finfo->fullname);
88650bf276cStholo return DIFF_ERROR;
88750bf276cStholo }
88850bf276cStholo
88950bf276cStholo if (use_rev2 == NULL)
89050bf276cStholo {
89150bf276cStholo /* The second revision does not exist. If EMPTY_FILES is
89250bf276cStholo true, treat this as a removed file. Otherwise warn
89350bf276cStholo about the missing tag. */
89450bf276cStholo if (empty_files)
89550bf276cStholo return DIFF_REMOVED;
89650bf276cStholo else if (diff_rev2)
89750bf276cStholo error (0, 0, "tag %s is not in file %s", diff_rev2,
89850bf276cStholo finfo->fullname);
89950bf276cStholo else
90050bf276cStholo error (0, 0, "no revision for date %s in file %s",
90150bf276cStholo diff_date2, finfo->fullname);
90250bf276cStholo return DIFF_ERROR;
90350bf276cStholo }
90450bf276cStholo
9051e72d8d2Sderaadt /* now, see if we really need to do the diff */
90650bf276cStholo if (strcmp (use_rev1, use_rev2) == 0)
90750bf276cStholo return DIFF_SAME;
90850bf276cStholo else
90950bf276cStholo return DIFF_DIFFERENT;
91050bf276cStholo }
91150bf276cStholo
91250bf276cStholo if ((diff_rev1 || diff_date1) && use_rev1 == NULL)
91350bf276cStholo {
91450bf276cStholo /* The first revision does not exist, and no second revision
91550bf276cStholo was given. */
91650bf276cStholo if (empty_files)
91750bf276cStholo {
91850bf276cStholo if (empty_file == DIFF_REMOVED)
91950bf276cStholo return DIFF_SAME;
92050bf276cStholo else
92150bf276cStholo {
92250bf276cStholo if (user_file_rev && use_rev2 == NULL)
92350bf276cStholo use_rev2 = xstrdup (user_file_rev);
92450bf276cStholo return DIFF_ADDED;
9251e72d8d2Sderaadt }
9261e72d8d2Sderaadt }
92750bf276cStholo else
92850bf276cStholo {
92950bf276cStholo if (diff_rev1)
93050bf276cStholo error (0, 0, "tag %s is not in file %s", diff_rev1,
93150bf276cStholo finfo->fullname);
93250bf276cStholo else
93350bf276cStholo error (0, 0, "no revision for date %s in file %s",
93450bf276cStholo diff_date1, finfo->fullname);
93550bf276cStholo return DIFF_ERROR;
93650bf276cStholo }
93750bf276cStholo }
93850bf276cStholo
9391e72d8d2Sderaadt if (user_file_rev)
9401e72d8d2Sderaadt {
9411e72d8d2Sderaadt /* drop user_file_rev into first unused use_rev */
9421e72d8d2Sderaadt if (!use_rev1)
9431e72d8d2Sderaadt use_rev1 = xstrdup (user_file_rev);
9441e72d8d2Sderaadt else if (!use_rev2)
9451e72d8d2Sderaadt use_rev2 = xstrdup (user_file_rev);
9461e72d8d2Sderaadt /* and if not, it wasn't needed anyhow */
9471e72d8d2Sderaadt user_file_rev = 0;
9481e72d8d2Sderaadt }
9491e72d8d2Sderaadt
9501e72d8d2Sderaadt /* now, see if we really need to do the diff */
9511e72d8d2Sderaadt if (use_rev1 && use_rev2)
9521e72d8d2Sderaadt {
95350bf276cStholo if (strcmp (use_rev1, use_rev2) == 0)
95450bf276cStholo return DIFF_SAME;
95550bf276cStholo else
95650bf276cStholo return DIFF_DIFFERENT;
9571e72d8d2Sderaadt }
958461cc63eStholo
95950bf276cStholo if (use_rev1 == NULL
96050bf276cStholo || (vers->vn_user != NULL && strcmp (use_rev1, vers->vn_user) == 0))
9611e72d8d2Sderaadt {
962461cc63eStholo if (empty_file == DIFF_DIFFERENT
963461cc63eStholo && vers->ts_user != NULL
964461cc63eStholo && strcmp (vers->ts_rcs, vers->ts_user) == 0
965461cc63eStholo && (!(*options) || strcmp (options, vers->options) == 0))
9661e72d8d2Sderaadt {
96750bf276cStholo return DIFF_SAME;
9681e72d8d2Sderaadt }
969461cc63eStholo if (use_rev1 == NULL
970461cc63eStholo && (vers->vn_user[0] != '0' || vers->vn_user[1] != '\0'))
971461cc63eStholo {
972461cc63eStholo if (vers->vn_user[0] == '-')
973461cc63eStholo use_rev1 = xstrdup (vers->vn_user + 1);
974461cc63eStholo else
9751e72d8d2Sderaadt use_rev1 = xstrdup (vers->vn_user);
9761e72d8d2Sderaadt }
977461cc63eStholo }
9781e72d8d2Sderaadt
97950bf276cStholo /* If we already know that the file is being added or removed,
98050bf276cStholo then we don't want to do an actual file comparison here. */
98150bf276cStholo if (empty_file != DIFF_DIFFERENT)
98250bf276cStholo return empty_file;
98350bf276cStholo
9841e72d8d2Sderaadt /*
9851e72d8d2Sderaadt * with 0 or 1 -r option specified, run a quick diff to see if we
9861e72d8d2Sderaadt * should bother with it at all.
9871e72d8d2Sderaadt */
988b6c02222Stholo
989b6c02222Stholo retcode = RCS_cmp_file (vers->srcfile, use_rev1,
99050bf276cStholo *options ? options : vers->options,
991b6c02222Stholo finfo->file);
992b6c02222Stholo
993b6c02222Stholo return retcode == 0 ? DIFF_SAME : DIFF_DIFFERENT;
9941e72d8d2Sderaadt }
995