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