| 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 | } |