| File: | src/gnu/usr.bin/cvs/src/diff.c | 
| Warning: | line 432, column 3 Value stored to 'exists' is never read | 
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | 
| 2 | * Copyright (c) 1992, Brian Berliner and Jeff Polk | 
| 3 | * Copyright (c) 1989-1992, Brian Berliner | 
| 4 | * | 
| 5 | * You may distribute under the terms of the GNU General Public License as | 
| 6 | * specified in the README file that comes with the CVS source distribution. | 
| 7 | * | 
| 8 | * Difference | 
| 9 | * | 
| 10 | * Run diff against versions in the repository. Options that are specified are | 
| 11 | * passed on directly to "rcsdiff". | 
| 12 | * | 
| 13 | * Without any file arguments, runs diff against all the currently modified | 
| 14 | * files. | 
| 15 | */ | 
| 16 | |
| 17 | #include "cvs.h" | 
| 18 | |
| 19 | enum diff_file | 
| 20 | { | 
| 21 | DIFF_ERROR, | 
| 22 | DIFF_ADDED, | 
| 23 | DIFF_REMOVED, | 
| 24 | DIFF_DIFFERENT, | 
| 25 | DIFF_SAME | 
| 26 | }; | 
| 27 | |
| 28 | static Dtype diff_dirproc PROTO ((void *callerdat, char *dir,(void *callerdat, char *dir, char *pos_repos, char *update_dir , List *entries) | 
| 29 | char *pos_repos, char *update_dir,(void *callerdat, char *dir, char *pos_repos, char *update_dir , List *entries) | 
| 30 | List *entries))(void *callerdat, char *dir, char *pos_repos, char *update_dir , List *entries); | 
| 31 | static int diff_filesdoneproc PROTO ((void *callerdat, int err,(void *callerdat, int err, char *repos, char *update_dir, List *entries) | 
| 32 | char *repos, char *update_dir,(void *callerdat, int err, char *repos, char *update_dir, List *entries) | 
| 33 | List *entries))(void *callerdat, int err, char *repos, char *update_dir, List *entries); | 
| 34 | static int diff_dirleaveproc PROTO ((void *callerdat, char *dir,(void *callerdat, char *dir, int err, char *update_dir, List * entries) | 
| 35 | int err, char *update_dir,(void *callerdat, char *dir, int err, char *update_dir, List * entries) | 
| 36 | List *entries))(void *callerdat, char *dir, int err, char *update_dir, List * entries); | 
| 37 | static enum diff_file diff_file_nodiff PROTO ((struct file_info *finfo,(struct file_info *finfo, Vers_TS *vers, enum diff_file) | 
| 38 | Vers_TS *vers,(struct file_info *finfo, Vers_TS *vers, enum diff_file) | 
| 39 | enum diff_file))(struct file_info *finfo, Vers_TS *vers, enum diff_file); | 
| 40 | static int diff_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo); | 
| 41 | static void diff_mark_errors PROTO((int err))(int err); | 
| 42 | |
| 43 | |
| 44 | /* Global variables. Would be cleaner if we just put this stuff in a | 
| 45 | struct like log.c does. */ | 
| 46 | |
| 47 | /* Command line tags, from -r option. Points into argv. */ | 
| 48 | static char *diff_rev1, *diff_rev2; | 
| 49 | /* Command line dates, from -D option. Malloc'd. */ | 
| 50 | static char *diff_date1, *diff_date2; | 
| 51 | static char *use_rev1, *use_rev2; | 
| 52 | static int have_rev1_label, have_rev2_label; | 
| 53 | |
| 54 | /* Revision of the user file, if it is unchanged from something in the | 
| 55 | repository and we want to use that fact. */ | 
| 56 | static char *user_file_rev; | 
| 57 | |
| 58 | static char *options; | 
| 59 | static char *opts; | 
| 60 | static size_t opts_allocated = 1; | 
| 61 | static int diff_errors; | 
| 62 | static int empty_files = 0; | 
| 63 | |
| 64 | /* FIXME: should be documenting all the options here. They don't | 
| 65 | perfectly match rcsdiff options (for example, we always support | 
| 66 | --ifdef and --context, but rcsdiff only does if diff does). */ | 
| 67 | static const char *const diff_usage[] = | 
| 68 | { | 
| 69 | "Usage: %s %s [-lNR] [rcsdiff-options]\n", | 
| 70 | " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n", | 
| 71 | "\t-l\tLocal directory only, not recursive\n", | 
| 72 | "\t-R\tProcess directories recursively.\n", | 
| 73 | "\t-D d1\tDiff revision for date against working file.\n", | 
| 74 | "\t-D d2\tDiff rev1/date1 against date2.\n", | 
| 75 | "\t-N\tinclude diffs for added and removed files.\n", | 
| 76 | "\t-r rev1\tDiff revision for rev1 against working file.\n", | 
| 77 | "\t-r rev2\tDiff rev1/date1 against rev2.\n", | 
| 78 | "\t--ifdef=arg\tOutput diffs in ifdef format.\n", | 
| 79 | "(consult the documentation for your diff program for rcsdiff-options.\n", | 
| 80 | "The most popular is -c for context diffs but there are many more).\n", | 
| 81 | "(Specify the --help global option for a list of other help options)\n", | 
| 82 | NULL((void*)0) | 
| 83 | }; | 
| 84 | |
| 85 | /* I copied this array directly out of diff.c in diffutils 2.7, after | 
| 86 | removing the following entries, none of which seem relevant to use | 
| 87 | with CVS: | 
| 88 | --help | 
| 89 | --version | 
| 90 | --recursive | 
| 91 | --unidirectional-new-file | 
| 92 | --starting-file | 
| 93 | --exclude | 
| 94 | --exclude-from | 
| 95 | --sdiff-merge-assist | 
| 96 | |
| 97 | I changed the options which take optional arguments (--context and | 
| 98 | --unified) to return a number rather than a letter, so that the | 
| 99 | optional argument could be handled more easily. I changed the | 
| 100 | --paginate and --brief options to return a number, since -l and -q | 
| 101 | mean something else to cvs diff. | 
| 102 | |
| 103 | The numbers 129- that appear in the fourth element of some entries | 
| 104 | tell the big switch in `diff' how to process those options. -- Ian | 
| 105 | |
| 106 | The following options, which diff lists as "An alias, no longer | 
| 107 | recommended" have been removed: --file-label --entire-new-file | 
| 108 | --ascii --print. */ | 
| 109 | |
| 110 | static struct option const longopts[] = | 
| 111 | { | 
| 112 | {"ignore-blank-lines", 0, 0, 'B'}, | 
| 113 | {"context", 2, 0, 143}, | 
| 114 | {"ifdef", 1, 0, 131}, | 
| 115 | {"show-function-line", 1, 0, 'F'}, | 
| 116 | {"speed-large-files", 0, 0, 'H'}, | 
| 117 | {"ignore-matching-lines", 1, 0, 'I'}, | 
| 118 | {"label", 1, 0, 'L'}, | 
| 119 | {"new-file", 0, 0, 'N'}, | 
| 120 | {"initial-tab", 0, 0, 'T'}, | 
| 121 | {"width", 1, 0, 'W'}, | 
| 122 | {"text", 0, 0, 'a'}, | 
| 123 | {"ignore-space-change", 0, 0, 'b'}, | 
| 124 | {"minimal", 0, 0, 'd'}, | 
| 125 | {"ed", 0, 0, 'e'}, | 
| 126 | {"forward-ed", 0, 0, 'f'}, | 
| 127 | {"ignore-case", 0, 0, 'i'}, | 
| 128 | {"paginate", 0, 0, 144}, | 
| 129 | {"rcs", 0, 0, 'n'}, | 
| 130 | {"show-c-function", 0, 0, 'p'}, | 
| 131 | |
| 132 | /* This is a potentially very useful option, except the output is so | 
| 133 | silly. It would be much better for it to look like "cvs rdiff -s" | 
| 134 | which displays all the same info, minus quite a few lines of | 
| 135 | extraneous garbage. */ | 
| 136 | {"brief", 0, 0, 145}, | 
| 137 | |
| 138 | {"report-identical-files", 0, 0, 's'}, | 
| 139 | {"expand-tabs", 0, 0, 't'}, | 
| 140 | {"ignore-all-space", 0, 0, 'w'}, | 
| 141 | {"side-by-side", 0, 0, 'y'}, | 
| 142 | {"unified", 2, 0, 146}, | 
| 143 | {"left-column", 0, 0, 129}, | 
| 144 | {"suppress-common-lines", 0, 0, 130}, | 
| 145 | {"old-line-format", 1, 0, 132}, | 
| 146 | {"new-line-format", 1, 0, 133}, | 
| 147 | {"unchanged-line-format", 1, 0, 134}, | 
| 148 | {"line-format", 1, 0, 135}, | 
| 149 | {"old-group-format", 1, 0, 136}, | 
| 150 | {"new-group-format", 1, 0, 137}, | 
| 151 | {"unchanged-group-format", 1, 0, 138}, | 
| 152 | {"changed-group-format", 1, 0, 139}, | 
| 153 | {"horizon-lines", 1, 0, 140}, | 
| 154 | {"binary", 0, 0, 142}, | 
| 155 | {0, 0, 0, 0} | 
| 156 | }; | 
| 157 | |
| 158 | /* CVS 1.9 and similar versions seemed to have pretty weird handling | 
| 159 | of -y and -T. In the cases where it called rcsdiff, | 
| 160 | they would have the meanings mentioned below. In the cases where it | 
| 161 | called diff, they would have the meanings mentioned in "longopts". | 
| 162 | Noone seems to have missed them, so I think the right thing to do is | 
| 163 | just to remove the options altogether (which I have done). | 
| 164 | |
| 165 | In the case of -z and -q, "cvs diff" did not accept them even back | 
| 166 | when we called rcsdiff (at least, it hasn't accepted them | 
| 167 | recently). | 
| 168 | |
| 169 | In comparing rcsdiff to the new CVS implementation, I noticed that | 
| 170 | the following rcsdiff flags are not handled by CVS diff: | 
| 171 | |
| 172 | -y: perform diff even when the requested revisions are the | 
| 173 | same revision number | 
| 174 | -q: run quietly | 
| 175 | -T: preserve modification time on the RCS file | 
| 176 | -z: specify timezone for use in file labels | 
| 177 | |
| 178 | I think these are not really relevant. -y is undocumented even in | 
| 179 | RCS 5.7, and seems like a minor change at best. According to RCS | 
| 180 | documentation, -T only applies when a RCS file has been modified | 
| 181 | because of lock changes; doesn't CVS sidestep RCS's entire lock | 
| 182 | structure? -z seems to be unsupported by CVS diff, and has a | 
| 183 | different meaning as a global option anyway. (Adding it could be | 
| 184 | a feature, but if it is left out for now, it should not break | 
| 185 | anything.) For the purposes of producing output, CVS diff appears | 
| 186 | mostly to ignore -q. Maybe this should be fixed, but I think it's | 
| 187 | a larger issue than the changes included here. */ | 
| 188 | |
| 189 | int | 
| 190 | diff (argc, argv) | 
| 191 | int argc; | 
| 192 | char **argv; | 
| 193 | { | 
| 194 | char tmp[50]; | 
| 195 | int c, err = 0; | 
| 196 | int local = 0; | 
| 197 | int which; | 
| 198 | int option_index; | 
| 199 | |
| 200 | if (argc == -1) | 
| 201 | usage (diff_usage); | 
| 202 | |
| 203 | have_rev1_label = have_rev2_label = 0; | 
| 204 | |
| 205 | /* | 
| 206 | * Note that we catch all the valid arguments here, so that we can | 
| 207 | * intercept the -r arguments for doing revision diffs; and -l/-R for a | 
| 208 | * non-recursive/recursive diff. | 
| 209 | */ | 
| 210 | |
| 211 | /* Clean out our global variables (multiroot can call us multiple | 
| 212 | times and the server can too, if the client sends several | 
| 213 | diff commands). */ | 
| 214 | if (opts == NULL((void*)0)) | 
| 215 | { | 
| 216 | opts_allocated = 1; | 
| 217 | opts = xmalloc (opts_allocated); | 
| 218 | } | 
| 219 | opts[0] = '\0'; | 
| 220 | diff_rev1 = NULL((void*)0); | 
| 221 | diff_rev2 = NULL((void*)0); | 
| 222 | diff_date1 = NULL((void*)0); | 
| 223 | diff_date2 = NULL((void*)0); | 
| 224 | |
| 225 | optind = 0; | 
| 226 | while ((c = getopt_long (argc, argv, | 
| 227 | "+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:", | 
| 228 | longopts, &option_index)) != -1) | 
| 229 | { | 
| 230 | switch (c) | 
| 231 | { | 
| 232 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': | 
| 233 | case 'h': case 'i': case 'n': case 'p': case 's': case 't': | 
| 234 | case 'u': case 'w': case 'y': | 
| 235 | case '0': case '1': case '2': case '3': case '4': case '5': | 
| 236 | case '6': case '7': case '8': case '9': | 
| 237 | case 'B': case 'H': case 'T': | 
| 238 | (void) sprintf (tmp, " -%c", (char) c); | 
| 239 | allocate_and_strcat (&opts, &opts_allocated, tmp); | 
| 240 | break; | 
| 241 | case 'L': | 
| 242 | if (have_rev1_label++) | 
| 243 | if (have_rev2_label++) | 
| 244 | { | 
| 245 | error (0, 0, "extra -L arguments ignored"); | 
| 246 | break; | 
| 247 | } | 
| 248 | |
| 249 | allocate_and_strcat (&opts, &opts_allocated, " -L"); | 
| 250 | allocate_and_strcat (&opts, &opts_allocated, optarg); | 
| 251 | break; | 
| 252 | case 'C': case 'F': case 'I': case 'U': case 'V': case 'W': | 
| 253 | (void) sprintf (tmp, " -%c", (char) c); | 
| 254 | allocate_and_strcat (&opts, &opts_allocated, tmp); | 
| 255 | allocate_and_strcat (&opts, &opts_allocated, optarg); | 
| 256 | break; | 
| 257 | case 131: | 
| 258 | /* --ifdef. */ | 
| 259 | allocate_and_strcat (&opts, &opts_allocated, " --ifdef="); | 
| 260 | allocate_and_strcat (&opts, &opts_allocated, optarg); | 
| 261 | break; | 
| 262 | case 129: case 130: case 132: case 133: case 134: | 
| 263 | case 135: case 136: case 137: case 138: case 139: case 140: | 
| 264 | case 141: case 142: case 143: case 144: case 145: case 146: | 
| 265 | allocate_and_strcat (&opts, &opts_allocated, " --"); | 
| 266 | allocate_and_strcat (&opts, &opts_allocated, | 
| 267 | longopts[option_index].name); | 
| 268 | if (longopts[option_index].has_arg == 1 | 
| 269 | || (longopts[option_index].has_arg == 2 | 
| 270 | && optarg != NULL((void*)0))) | 
| 271 | { | 
| 272 | allocate_and_strcat (&opts, &opts_allocated, "="); | 
| 273 | allocate_and_strcat (&opts, &opts_allocated, optarg); | 
| 274 | } | 
| 275 | break; | 
| 276 | case 'R': | 
| 277 | local = 0; | 
| 278 | break; | 
| 279 | case 'l': | 
| 280 | local = 1; | 
| 281 | break; | 
| 282 | case 'k': | 
| 283 | if (options) | 
| 284 | free (options); | 
| 285 | options = RCS_check_kflag (optarg); | 
| 286 | break; | 
| 287 | case 'r': | 
| 288 | if (diff_rev2 != NULL((void*)0) || diff_date2 != NULL((void*)0)) | 
| 289 | error (1, 0, | 
| 290 | "no more than two revisions/dates can be specified"); | 
| 291 | if (diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) | 
| 292 | diff_rev2 = optarg; | 
| 293 | else | 
| 294 | diff_rev1 = optarg; | 
| 295 | break; | 
| 296 | case 'D': | 
| 297 | if (diff_rev2 != NULL((void*)0) || diff_date2 != NULL((void*)0)) | 
| 298 | error (1, 0, | 
| 299 | "no more than two revisions/dates can be specified"); | 
| 300 | if (diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) | 
| 301 | diff_date2 = Make_Date (optarg); | 
| 302 | else | 
| 303 | diff_date1 = Make_Date (optarg); | 
| 304 | break; | 
| 305 | case 'N': | 
| 306 | empty_files = 1; | 
| 307 | break; | 
| 308 | case '?': | 
| 309 | default: | 
| 310 | usage (diff_usage); | 
| 311 | break; | 
| 312 | } | 
| 313 | } | 
| 314 | argc -= optind; | 
| 315 | argv += optind; | 
| 316 | |
| 317 | /* make sure options is non-null */ | 
| 318 | if (!options) | 
| 319 | options = xstrdup (""); | 
| 320 | |
| 321 | #ifdef CLIENT_SUPPORT1 | 
| 322 | if (current_parsed_root->isremote) { | 
| 323 | /* We're the client side. Fire up the remote server. */ | 
| 324 | start_server (); | 
| 325 | |
| 326 | ign_setup (); | 
| 327 | |
| 328 | if (local) | 
| 329 | send_arg("-l"); | 
| 330 | if (empty_files) | 
| 331 | send_arg("-N"); | 
| 332 | send_option_string (opts); | 
| 333 | if (options[0] != '\0') | 
| 334 | send_arg (options); | 
| 335 | if (diff_rev1) | 
| 336 | option_with_arg ("-r", diff_rev1); | 
| 337 | if (diff_date1) | 
| 338 | client_senddate (diff_date1); | 
| 339 | if (diff_rev2) | 
| 340 | option_with_arg ("-r", diff_rev2); | 
| 341 | if (diff_date2) | 
| 342 | client_senddate (diff_date2); | 
| 343 | |
| 344 | /* Send the current files unless diffing two revs from the archive */ | 
| 345 | if (diff_rev2 == NULL((void*)0) && diff_date2 == NULL((void*)0)) | 
| 346 | send_files (argc, argv, local, 0, 0); | 
| 347 | else | 
| 348 | send_files (argc, argv, local, 0, SEND_NO_CONTENTS4); | 
| 349 | |
| 350 | send_file_names (argc, argv, SEND_EXPAND_WILD1); | 
| 351 | |
| 352 | send_to_server ("diff\012", 0); | 
| 353 | err = get_responses_and_close (); | 
| 354 | free (options); | 
| 355 | options = NULL((void*)0); | 
| 356 | return (err); | 
| 357 | } | 
| 358 | #endif | 
| 359 | |
| 360 | if (diff_rev1 != NULL((void*)0)) | 
| 361 | tag_check_valid (diff_rev1, argc, argv, local, 0, ""); | 
| 362 | if (diff_rev2 != NULL((void*)0)) | 
| 363 | tag_check_valid (diff_rev2, argc, argv, local, 0, ""); | 
| 364 | |
| 365 | which = W_LOCAL0x01; | 
| 366 | if (diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) | 
| 367 | which |= W_REPOS0x02 | W_ATTIC0x04; | 
| 368 | |
| 369 | wrap_setup (); | 
| 370 | |
| 371 | /* start the recursion processor */ | 
| 372 | err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc, | 
| 373 | diff_dirleaveproc, NULL((void*)0), argc, argv, local, | 
| 374 | which, 0, 1, (char *) NULL((void*)0), 1); | 
| 375 | |
| 376 | /* clean up */ | 
| 377 | free (options); | 
| 378 | options = NULL((void*)0); | 
| 379 | |
| 380 | if (diff_date1 != NULL((void*)0)) | 
| 381 | free (diff_date1); | 
| 382 | if (diff_date2 != NULL((void*)0)) | 
| 383 | free (diff_date2); | 
| 384 | |
| 385 | return (err); | 
| 386 | } | 
| 387 | |
| 388 | /* | 
| 389 | * Do a file diff | 
| 390 | */ | 
| 391 | /* ARGSUSED */ | 
| 392 | static int | 
| 393 | diff_fileproc (callerdat, finfo) | 
| 394 | void *callerdat; | 
| 395 | struct file_info *finfo; | 
| 396 | { | 
| 397 | int status, err = 2; /* 2 == trouble, like rcsdiff */ | 
| 398 | Vers_TS *vers; | 
| 399 | enum diff_file empty_file = DIFF_DIFFERENT; | 
| 400 | char *tmp; | 
| 401 | char *tocvsPath; | 
| 402 | char *fname; | 
| 403 | char *label1; | 
| 404 | char *label2; | 
| 405 | |
| 406 | /* Initialize these solely to avoid warnings from gcc -Wall about | 
| 407 | variables that might be used uninitialized. */ | 
| 408 | tmp = NULL((void*)0); | 
| 409 | fname = NULL((void*)0); | 
| 410 | |
| 411 | user_file_rev = 0; | 
| 412 | vers = Version_TS (finfo, NULL((void*)0), NULL((void*)0), NULL((void*)0), 1, 0); | 
| 413 | |
| 414 | if (diff_rev2 != NULL((void*)0) || diff_date2 != NULL((void*)0)) | 
| 415 | { | 
| 416 | /* Skip all the following checks regarding the user file; we're | 
| 417 | not using it. */ | 
| 418 | } | 
| 419 | else if (vers->vn_user == NULL((void*)0)) | 
| 420 | { | 
| 421 | /* The file does not exist in the working directory. */ | 
| 422 | if ((diff_rev1 != NULL((void*)0) || diff_date1 != NULL((void*)0)) | 
| 423 | && vers->srcfile != NULL((void*)0)) | 
| 424 | { | 
| 425 | /* The file does exist in the repository. */ | 
| 426 | if (empty_files) | 
| 427 | empty_file = DIFF_REMOVED; | 
| 428 | else | 
| 429 | { | 
| 430 | int exists; | 
| 431 | |
| 432 | exists = 0; | 
| Value stored to 'exists' is never read | |
| 433 | /* special handling for TAG_HEAD */ | 
| 434 | if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD"HEAD") == 0) | 
| 435 | { | 
| 436 | char *head = | 
| 437 | (vers->vn_rcs == NULL((void*)0) | 
| 438 | ? NULL((void*)0) | 
| 439 | : RCS_branch_head (vers->srcfile, vers->vn_rcs)); | 
| 440 | exists = head != NULL((void*)0); | 
| 441 | if (head != NULL((void*)0)) | 
| 442 | free (head); | 
| 443 | } | 
| 444 | else | 
| 445 | { | 
| 446 | Vers_TS *xvers; | 
| 447 | |
| 448 | xvers = Version_TS (finfo, NULL((void*)0), diff_rev1, diff_date1, | 
| 449 | 1, 0); | 
| 450 | exists = xvers->vn_rcs != NULL((void*)0); | 
| 451 | freevers_ts (&xvers); | 
| 452 | } | 
| 453 | if (exists) | 
| 454 | error (0, 0, | 
| 455 | "%s no longer exists, no comparison available", | 
| 456 | finfo->fullname); | 
| 457 | freevers_ts (&vers); | 
| 458 | diff_mark_errors (err); | 
| 459 | return (err); | 
| 460 | } | 
| 461 | } | 
| 462 | else | 
| 463 | { | 
| 464 | error (0, 0, "I know nothing about %s", finfo->fullname); | 
| 465 | freevers_ts (&vers); | 
| 466 | diff_mark_errors (err); | 
| 467 | return (err); | 
| 468 | } | 
| 469 | } | 
| 470 | else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') | 
| 471 | { | 
| 472 | if (empty_files) | 
| 473 | empty_file = DIFF_ADDED; | 
| 474 | else | 
| 475 | { | 
| 476 | error (0, 0, "%s is a new entry, no comparison available", | 
| 477 | finfo->fullname); | 
| 478 | freevers_ts (&vers); | 
| 479 | diff_mark_errors (err); | 
| 480 | return (err); | 
| 481 | } | 
| 482 | } | 
| 483 | else if (vers->vn_user[0] == '-') | 
| 484 | { | 
| 485 | if (empty_files) | 
| 486 | empty_file = DIFF_REMOVED; | 
| 487 | else | 
| 488 | { | 
| 489 | error (0, 0, "%s was removed, no comparison available", | 
| 490 | finfo->fullname); | 
| 491 | freevers_ts (&vers); | 
| 492 | diff_mark_errors (err); | 
| 493 | return (err); | 
| 494 | } | 
| 495 | } | 
| 496 | else | 
| 497 | { | 
| 498 | if (vers->vn_rcs == NULL((void*)0) && vers->srcfile == NULL((void*)0)) | 
| 499 | { | 
| 500 | error (0, 0, "cannot find revision control file for %s", | 
| 501 | finfo->fullname); | 
| 502 | freevers_ts (&vers); | 
| 503 | diff_mark_errors (err); | 
| 504 | return (err); | 
| 505 | } | 
| 506 | else | 
| 507 | { | 
| 508 | if (vers->ts_user == NULL((void*)0)) | 
| 509 | { | 
| 510 | error (0, 0, "cannot find %s", finfo->fullname); | 
| 511 | freevers_ts (&vers); | 
| 512 | diff_mark_errors (err); | 
| 513 | return (err); | 
| 514 | } | 
| 515 | else if (!strcmp (vers->ts_user, vers->ts_rcs)) | 
| 516 | { | 
| 517 | /* The user file matches some revision in the repository | 
| 518 | Diff against the repository (for remote CVS, we might not | 
| 519 | have a copy of the user file around). */ | 
| 520 | user_file_rev = vers->vn_user; | 
| 521 | } | 
| 522 | } | 
| 523 | } | 
| 524 | |
| 525 | empty_file = diff_file_nodiff (finfo, vers, empty_file); | 
| 526 | if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR) | 
| 527 | { | 
| 528 | freevers_ts (&vers); | 
| 529 | if (empty_file == DIFF_SAME) | 
| 530 | { | 
| 531 | /* In the server case, would be nice to send a "Checked-in" | 
| 532 | response, so that the client can rewrite its timestamp. | 
| 533 | server_checked_in by itself isn't the right thing (it | 
| 534 | needs a server_register), but I'm not sure what is. | 
| 535 | It isn't clear to me how "cvs status" handles this (that | 
| 536 | is, for a client which sends Modified not Is-modified to | 
| 537 | "cvs status"), but it does. */ | 
| 538 | return (0); | 
| 539 | } | 
| 540 | else | 
| 541 | { | 
| 542 | diff_mark_errors (err); | 
| 543 | return (err); | 
| 544 | } | 
| 545 | } | 
| 546 | |
| 547 | if (empty_file == DIFF_DIFFERENT) | 
| 548 | { | 
| 549 | int dead1, dead2; | 
| 550 | |
| 551 | if (use_rev1 == NULL((void*)0)) | 
| 552 | dead1 = 0; | 
| 553 | else | 
| 554 | dead1 = RCS_isdead (vers->srcfile, use_rev1); | 
| 555 | if (use_rev2 == NULL((void*)0)) | 
| 556 | dead2 = 0; | 
| 557 | else | 
| 558 | dead2 = RCS_isdead (vers->srcfile, use_rev2); | 
| 559 | |
| 560 | if (dead1 && dead2) | 
| 561 | { | 
| 562 | freevers_ts (&vers); | 
| 563 | return (0); | 
| 564 | } | 
| 565 | else if (dead1) | 
| 566 | { | 
| 567 | if (empty_files) | 
| 568 | empty_file = DIFF_ADDED; | 
| 569 | else | 
| 570 | { | 
| 571 | error (0, 0, "%s is a new entry, no comparison available", | 
| 572 | finfo->fullname); | 
| 573 | freevers_ts (&vers); | 
| 574 | diff_mark_errors (err); | 
| 575 | return (err); | 
| 576 | } | 
| 577 | } | 
| 578 | else if (dead2) | 
| 579 | { | 
| 580 | if (empty_files) | 
| 581 | empty_file = DIFF_REMOVED; | 
| 582 | else | 
| 583 | { | 
| 584 | error (0, 0, "%s was removed, no comparison available", | 
| 585 | finfo->fullname); | 
| 586 | freevers_ts (&vers); | 
| 587 | diff_mark_errors (err); | 
| 588 | return (err); | 
| 589 | } | 
| 590 | } | 
| 591 | } | 
| 592 | |
| 593 | /* Output an "Index:" line for patch to use */ | 
| 594 | cvs_output ("Index: ", 0); | 
| 595 | cvs_output (finfo->fullname, 0); | 
| 596 | cvs_output ("\n", 1); | 
| 597 | |
| 598 | tocvsPath = wrap_tocvs_process_file(finfo->file); | 
| 599 | if (tocvsPath) | 
| 600 | { | 
| 601 | /* Backup the current version of the file to CVS/,,filename */ | 
| 602 | fname = xmalloc (strlen (finfo->file) | 
| 603 | + sizeof CVSADM"CVS" | 
| 604 | + sizeof CVSPREFIX",," | 
| 605 | + 10); | 
| 606 | sprintf(fname,"%s/%s%s",CVSADM"CVS", CVSPREFIX",,", finfo->file); | 
| 607 | if (unlink_file_dir (fname) < 0) | 
| 608 | if (! existence_error (errno)(((*__errno())) == 2)) | 
| 609 | error (1, errno(*__errno()), "cannot remove %s", fname); | 
| 610 | rename_file (finfo->file, fname); | 
| 611 | /* Copy the wrapped file to the current directory then go to work */ | 
| 612 | copy_file (tocvsPath, finfo->file); | 
| 613 | } | 
| 614 | |
| 615 | /* Set up file labels appropriate for compatibility with the Larry Wall | 
| 616 | * implementation of patch if the user didn't specify. This is irrelevant | 
| 617 | * according to the POSIX.2 specification. | 
| 618 | */ | 
| 619 | label1 = NULL((void*)0); | 
| 620 | label2 = NULL((void*)0); | 
| 621 | if (!have_rev1_label) | 
| 622 | { | 
| 623 | if (empty_file == DIFF_ADDED) | 
| 624 | label1 = | 
| 625 | make_file_label (DEVNULL"/dev/null", NULL((void*)0), NULL((void*)0)); | 
| 626 | else | 
| 627 | label1 = | 
| 628 | make_file_label (finfo->fullname, use_rev1, vers ? vers->srcfile : NULL((void*)0)); | 
| 629 | } | 
| 630 | |
| 631 | if (!have_rev2_label) | 
| 632 | { | 
| 633 | if (empty_file == DIFF_REMOVED) | 
| 634 | label2 = | 
| 635 | make_file_label (DEVNULL"/dev/null", NULL((void*)0), NULL((void*)0)); | 
| 636 | else | 
| 637 | label2 = | 
| 638 | make_file_label (finfo->fullname, use_rev2, vers ? vers->srcfile : NULL((void*)0)); | 
| 639 | } | 
| 640 | |
| 641 | if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED) | 
| 642 | { | 
| 643 | /* This is fullname, not file, possibly despite the POSIX.2 | 
| 644 | * specification, because that's the way all the Larry Wall | 
| 645 | * implementations of patch (are there other implementations?) want | 
| 646 | * things and the POSIX.2 spec appears to leave room for this. | 
| 647 | */ | 
| 648 | cvs_output ("\ | 
| 649 | ===================================================================\n\ | 
| 650 | RCS file: ", 0); | 
| 651 | cvs_output (finfo->fullname, 0); | 
| 652 | cvs_output ("\n", 1); | 
| 653 | |
| 654 | cvs_output ("diff -N ", 0); | 
| 655 | cvs_output (finfo->fullname, 0); | 
| 656 | cvs_output ("\n", 1); | 
| 657 | |
| 658 | if (empty_file == DIFF_ADDED) | 
| 659 | { | 
| 660 | if (use_rev2 == NULL((void*)0)) | 
| 661 | status = diff_exec (DEVNULL"/dev/null", finfo->file, label1, label2, opts, RUN_TTY(char *)0); | 
| 662 | else | 
| 663 | { | 
| 664 | int retcode; | 
| 665 | |
| 666 | tmp = cvs_temp_name (); | 
| 667 | retcode = RCS_checkout (vers->srcfile, (char *) NULL((void*)0), | 
| 668 | use_rev2, (char *) NULL((void*)0), | 
| 669 | (*options | 
| 670 | ? options | 
| 671 | : vers->options), | 
| 672 | tmp, (RCSCHECKOUTPROC) NULL((void*)0), | 
| 673 | (void *) NULL((void*)0)); | 
| 674 | if (retcode != 0) | 
| 675 | { | 
| 676 | diff_mark_errors (err); | 
| 677 | return err; | 
| 678 | } | 
| 679 | |
| 680 | status = diff_exec (DEVNULL"/dev/null", tmp, label1, label2, opts, RUN_TTY(char *)0); | 
| 681 | } | 
| 682 | } | 
| 683 | else | 
| 684 | { | 
| 685 | int retcode; | 
| 686 | |
| 687 | tmp = cvs_temp_name (); | 
| 688 | retcode = RCS_checkout (vers->srcfile, (char *) NULL((void*)0), | 
| 689 | use_rev1, (char *) NULL((void*)0), | 
| 690 | *options ? options : vers->options, | 
| 691 | tmp, (RCSCHECKOUTPROC) NULL((void*)0), | 
| 692 | (void *) NULL((void*)0)); | 
| 693 | if (retcode != 0) | 
| 694 | { | 
| 695 | diff_mark_errors (err); | 
| 696 | return err; | 
| 697 | } | 
| 698 | |
| 699 | status = diff_exec (tmp, DEVNULL"/dev/null", label1, label2, opts, RUN_TTY(char *)0); | 
| 700 | } | 
| 701 | } | 
| 702 | else | 
| 703 | { | 
| 704 | status = RCS_exec_rcsdiff (vers->srcfile, opts, | 
| 705 | *options ? options : vers->options, | 
| 706 | use_rev1, use_rev2, | 
| 707 | label1, label2, | 
| 708 | finfo->file); | 
| 709 | |
| 710 | if (label1) free (label1); | 
| 711 | if (label2) free (label2); | 
| 712 | } | 
| 713 | |
| 714 | switch (status) | 
| 715 | { | 
| 716 | case -1: /* fork failed */ | 
| 717 | error (1, errno(*__errno()), "fork failed while diffing %s", | 
| 718 | vers->srcfile->path); | 
| 719 | case 0: /* everything ok */ | 
| 720 | err = 0; | 
| 721 | break; | 
| 722 | default: /* other error */ | 
| 723 | err = status; | 
| 724 | break; | 
| 725 | } | 
| 726 | |
| 727 | if (tocvsPath) | 
| 728 | { | 
| 729 | if (unlink_file_dir (finfo->file) < 0) | 
| 730 | if (! existence_error (errno)(((*__errno())) == 2)) | 
| 731 | error (1, errno(*__errno()), "cannot remove %s", finfo->file); | 
| 732 | |
| 733 | rename_file (fname, finfo->file); | 
| 734 | if (unlink_file (tocvsPath) < 0) | 
| 735 | error (1, errno(*__errno()), "cannot remove %s", tocvsPath); | 
| 736 | free (fname); | 
| 737 | } | 
| 738 | |
| 739 | if (empty_file == DIFF_REMOVED | 
| 740 | || (empty_file == DIFF_ADDED && use_rev2 != NULL((void*)0))) | 
| 741 | { | 
| 742 | if (CVS_UNLINKunlink (tmp) < 0) | 
| 743 | error (0, errno(*__errno()), "cannot remove %s", tmp); | 
| 744 | free (tmp); | 
| 745 | } | 
| 746 | |
| 747 | freevers_ts (&vers); | 
| 748 | diff_mark_errors (err); | 
| 749 | return (err); | 
| 750 | } | 
| 751 | |
| 752 | /* | 
| 753 | * Remember the exit status for each file. | 
| 754 | */ | 
| 755 | static void | 
| 756 | diff_mark_errors (err) | 
| 757 | int err; | 
| 758 | { | 
| 759 | if (err > diff_errors) | 
| 760 | diff_errors = err; | 
| 761 | } | 
| 762 | |
| 763 | /* | 
| 764 | * Print a warm fuzzy message when we enter a dir | 
| 765 | * | 
| 766 | * Don't try to diff directories that don't exist! -- DW | 
| 767 | */ | 
| 768 | /* ARGSUSED */ | 
| 769 | static Dtype | 
| 770 | diff_dirproc (callerdat, dir, pos_repos, update_dir, entries) | 
| 771 | void *callerdat; | 
| 772 | char *dir; | 
| 773 | char *pos_repos; | 
| 774 | char *update_dir; | 
| 775 | List *entries; | 
| 776 | { | 
| 777 | /* XXX - check for dirs we don't want to process??? */ | 
| 778 | |
| 779 | /* YES ... for instance dirs that don't exist!!! -- DW */ | 
| 780 | if (!isdir (dir)) | 
| 781 | return (R_SKIP_ALL); | 
| 782 | |
| 783 | if (!quiet) | 
| 784 | error (0, 0, "Diffing %s", update_dir); | 
| 785 | return (R_PROCESS); | 
| 786 | } | 
| 787 | |
| 788 | /* | 
| 789 | * Concoct the proper exit status - done with files | 
| 790 | */ | 
| 791 | /* ARGSUSED */ | 
| 792 | static int | 
| 793 | diff_filesdoneproc (callerdat, err, repos, update_dir, entries) | 
| 794 | void *callerdat; | 
| 795 | int err; | 
| 796 | char *repos; | 
| 797 | char *update_dir; | 
| 798 | List *entries; | 
| 799 | { | 
| 800 | return (diff_errors); | 
| 801 | } | 
| 802 | |
| 803 | /* | 
| 804 | * Concoct the proper exit status - leaving directories | 
| 805 | */ | 
| 806 | /* ARGSUSED */ | 
| 807 | static int | 
| 808 | diff_dirleaveproc (callerdat, dir, err, update_dir, entries) | 
| 809 | void *callerdat; | 
| 810 | char *dir; | 
| 811 | int err; | 
| 812 | char *update_dir; | 
| 813 | List *entries; | 
| 814 | { | 
| 815 | return (diff_errors); | 
| 816 | } | 
| 817 | |
| 818 | /* | 
| 819 | * verify that a file is different | 
| 820 | */ | 
| 821 | static enum diff_file | 
| 822 | diff_file_nodiff (finfo, vers, empty_file) | 
| 823 | struct file_info *finfo; | 
| 824 | Vers_TS *vers; | 
| 825 | enum diff_file empty_file; | 
| 826 | { | 
| 827 | Vers_TS *xvers; | 
| 828 | int retcode; | 
| 829 | |
| 830 | /* free up any old use_rev* variables and reset 'em */ | 
| 831 | if (use_rev1) | 
| 832 | free (use_rev1); | 
| 833 | if (use_rev2) | 
| 834 | free (use_rev2); | 
| 835 | use_rev1 = use_rev2 = (char *) NULL((void*)0); | 
| 836 | |
| 837 | if (diff_rev1 || diff_date1) | 
| 838 | { | 
| 839 | /* special handling for TAG_HEAD */ | 
| 840 | if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD"HEAD") == 0) | 
| 841 | use_rev1 = ((vers->vn_rcs == NULL((void*)0) || vers->srcfile == NULL((void*)0)) | 
| 842 | ? NULL((void*)0) | 
| 843 | : RCS_branch_head (vers->srcfile, vers->vn_rcs)); | 
| 844 | else | 
| 845 | { | 
| 846 | xvers = Version_TS (finfo, NULL((void*)0), diff_rev1, diff_date1, 1, 0); | 
| 847 | if (xvers->vn_rcs != NULL((void*)0)) | 
| 848 | use_rev1 = xstrdup (xvers->vn_rcs); | 
| 849 | freevers_ts (&xvers); | 
| 850 | } | 
| 851 | } | 
| 852 | if (diff_rev2 || diff_date2) | 
| 853 | { | 
| 854 | /* special handling for TAG_HEAD */ | 
| 855 | if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD"HEAD") == 0) | 
| 856 | use_rev2 = ((vers->vn_rcs == NULL((void*)0) || vers->srcfile == NULL((void*)0)) | 
| 857 | ? NULL((void*)0) | 
| 858 | : RCS_branch_head (vers->srcfile, vers->vn_rcs)); | 
| 859 | else | 
| 860 | { | 
| 861 | xvers = Version_TS (finfo, NULL((void*)0), diff_rev2, diff_date2, 1, 0); | 
| 862 | if (xvers->vn_rcs != NULL((void*)0)) | 
| 863 | use_rev2 = xstrdup (xvers->vn_rcs); | 
| 864 | freevers_ts (&xvers); | 
| 865 | } | 
| 866 | |
| 867 | if (use_rev1 == NULL((void*)0)) | 
| 868 | { | 
| 869 | /* The first revision does not exist. If EMPTY_FILES is | 
| 870 | true, treat this as an added file. Otherwise, warn | 
| 871 | about the missing tag. */ | 
| 872 | if (use_rev2 == NULL((void*)0) || RCS_isdead( vers->srcfile, use_rev2 ) ) | 
| 873 | /* At least in the case where DIFF_REV1 and DIFF_REV2 | 
| 874 | are both numeric, we should be returning some kind | 
| 875 | of error (see basicb-8a0 in testsuite). The symbolic | 
| 876 | case may be more complicated. */ | 
| 877 | return DIFF_SAME; | 
| 878 | else if (empty_files) | 
| 879 | return DIFF_ADDED; | 
| 880 | else if (diff_rev1) | 
| 881 | error (0, 0, "tag %s is not in file %s", diff_rev1, | 
| 882 | finfo->fullname); | 
| 883 | else | 
| 884 | error (0, 0, "no revision for date %s in file %s", | 
| 885 | diff_date1, finfo->fullname); | 
| 886 | return DIFF_ERROR; | 
| 887 | } | 
| 888 | |
| 889 | if (use_rev2 == NULL((void*)0)) | 
| 890 | { | 
| 891 | /* The second revision does not exist. If EMPTY_FILES is | 
| 892 | true, treat this as a removed file. Otherwise warn | 
| 893 | about the missing tag. */ | 
| 894 | if (empty_files) | 
| 895 | return DIFF_REMOVED; | 
| 896 | else if (diff_rev2) | 
| 897 | error (0, 0, "tag %s is not in file %s", diff_rev2, | 
| 898 | finfo->fullname); | 
| 899 | else | 
| 900 | error (0, 0, "no revision for date %s in file %s", | 
| 901 | diff_date2, finfo->fullname); | 
| 902 | return DIFF_ERROR; | 
| 903 | } | 
| 904 | |
| 905 | /* now, see if we really need to do the diff */ | 
| 906 | if (strcmp (use_rev1, use_rev2) == 0) | 
| 907 | return DIFF_SAME; | 
| 908 | else | 
| 909 | return DIFF_DIFFERENT; | 
| 910 | } | 
| 911 | |
| 912 | if ((diff_rev1 || diff_date1) && use_rev1 == NULL((void*)0)) | 
| 913 | { | 
| 914 | /* The first revision does not exist, and no second revision | 
| 915 | was given. */ | 
| 916 | if (empty_files) | 
| 917 | { | 
| 918 | if (empty_file == DIFF_REMOVED) | 
| 919 | return DIFF_SAME; | 
| 920 | else | 
| 921 | { | 
| 922 | if (user_file_rev && use_rev2 == NULL((void*)0)) | 
| 923 | use_rev2 = xstrdup (user_file_rev); | 
| 924 | return DIFF_ADDED; | 
| 925 | } | 
| 926 | } | 
| 927 | else | 
| 928 | { | 
| 929 | if (diff_rev1) | 
| 930 | error (0, 0, "tag %s is not in file %s", diff_rev1, | 
| 931 | finfo->fullname); | 
| 932 | else | 
| 933 | error (0, 0, "no revision for date %s in file %s", | 
| 934 | diff_date1, finfo->fullname); | 
| 935 | return DIFF_ERROR; | 
| 936 | } | 
| 937 | } | 
| 938 | |
| 939 | if (user_file_rev) | 
| 940 | { | 
| 941 | /* drop user_file_rev into first unused use_rev */ | 
| 942 | if (!use_rev1) | 
| 943 | use_rev1 = xstrdup (user_file_rev); | 
| 944 | else if (!use_rev2) | 
| 945 | use_rev2 = xstrdup (user_file_rev); | 
| 946 | /* and if not, it wasn't needed anyhow */ | 
| 947 | user_file_rev = 0; | 
| 948 | } | 
| 949 | |
| 950 | /* now, see if we really need to do the diff */ | 
| 951 | if (use_rev1 && use_rev2) | 
| 952 | { | 
| 953 | if (strcmp (use_rev1, use_rev2) == 0) | 
| 954 | return DIFF_SAME; | 
| 955 | else | 
| 956 | return DIFF_DIFFERENT; | 
| 957 | } | 
| 958 | |
| 959 | if (use_rev1 == NULL((void*)0) | 
| 960 | || (vers->vn_user != NULL((void*)0) && strcmp (use_rev1, vers->vn_user) == 0)) | 
| 961 | { | 
| 962 | if (empty_file == DIFF_DIFFERENT | 
| 963 | && vers->ts_user != NULL((void*)0) | 
| 964 | && strcmp (vers->ts_rcs, vers->ts_user) == 0 | 
| 965 | && (!(*options) || strcmp (options, vers->options) == 0)) | 
| 966 | { | 
| 967 | return DIFF_SAME; | 
| 968 | } | 
| 969 | if (use_rev1 == NULL((void*)0) | 
| 970 | && (vers->vn_user[0] != '0' || vers->vn_user[1] != '\0')) | 
| 971 | { | 
| 972 | if (vers->vn_user[0] == '-') | 
| 973 | use_rev1 = xstrdup (vers->vn_user + 1); | 
| 974 | else | 
| 975 | use_rev1 = xstrdup (vers->vn_user); | 
| 976 | } | 
| 977 | } | 
| 978 | |
| 979 | /* If we already know that the file is being added or removed, | 
| 980 | then we don't want to do an actual file comparison here. */ | 
| 981 | if (empty_file != DIFF_DIFFERENT) | 
| 982 | return empty_file; | 
| 983 | |
| 984 | /* | 
| 985 | * with 0 or 1 -r option specified, run a quick diff to see if we | 
| 986 | * should bother with it at all. | 
| 987 | */ | 
| 988 | |
| 989 | retcode = RCS_cmp_file (vers->srcfile, use_rev1, | 
| 990 | *options ? options : vers->options, | 
| 991 | finfo->file); | 
| 992 | |
| 993 | return retcode == 0 ? DIFF_SAME : DIFF_DIFFERENT; | 
| 994 | } |