1 /* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8 * Portions Copyright (C) 1989-1992, Brian Berliner 9 * 10 * You may distribute under the terms of the GNU General Public License as 11 * specified in the README file that comes with the CVS source distribution. 12 * 13 * Patch 14 * 15 * Create a Larry Wall format "patch" file between a previous release and the 16 * current head of a module, or between two releases. Can specify the 17 * release as either a date or a revision number. 18 */ 19 #include <sys/cdefs.h> 20 __RCSID("$NetBSD: patch.c,v 1.7 2016/05/17 14:00:09 christos Exp $"); 21 22 #include "cvs.h" 23 #include "getline.h" 24 25 static RETSIGTYPE patch_cleanup (int); 26 static Dtype patch_dirproc (void *callerdat, const char *dir, 27 const char *repos, const char *update_dir, 28 List *entries); 29 static int patch_fileproc (void *callerdat, struct file_info *finfo); 30 static int patch_proc (int argc, char **argv, char *xwhere, 31 char *mwhere, char *mfile, int shorten, 32 int local_specified, char *mname, char *msg); 33 34 static int force_tag_match = 1; 35 static int patch_short = 0; 36 static int toptwo_diffs = 0; 37 static char *options = NULL; 38 static char *rev1 = NULL; 39 static int rev1_validated = 0; 40 static char *rev2 = NULL; 41 static int rev2_validated = 0; 42 static char *date1 = NULL; 43 static char *date2 = NULL; 44 static char *tmpfile1 = NULL; 45 static char *tmpfile2 = NULL; 46 static char *tmpfile3 = NULL; 47 static int unidiff = 0; 48 static int show_c_func = 0; 49 50 static const char *const patch_usage[] = 51 { 52 "Usage: %s %s [-flpR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n", 53 " -r rev|-D date [-r rev2 | -D date2] modules...\n", 54 "\t-f\tForce a head revision match if tag/date not found.\n", 55 "\t-l\tLocal directory only, not recursive\n", 56 "\t-R\tProcess directories recursively.\n", 57 "\t-c\tContext diffs (default)\n", 58 "\t-u\tUnidiff format.\n", 59 "\t-p\tShow which C function each change is in.\n", /* --show-c-function */ 60 "\t-s\tShort patch - one liner per file.\n", 61 "\t-t\tTop two diffs - last change made to the file.\n", 62 "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n", 63 "\t-k kopt\tSpecify keyword expansion mode.\n", 64 "\t-D date\tDate.\n", 65 "\t-r rev\tRevision - symbolic or numeric.\n", 66 "(Specify the --help global option for a list of other help options)\n", 67 NULL 68 }; 69 70 71 72 int 73 patch (int argc, char **argv) 74 { 75 register int i; 76 int local = 0; 77 int c; 78 int err = 0; 79 DBM *db; 80 81 if (argc == -1) 82 usage (patch_usage); 83 84 getoptreset (); 85 while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:p")) != -1) 86 { 87 switch (c) 88 { 89 case 'Q': 90 case 'q': 91 /* The CVS 1.5 client sends these options (in addition to 92 Global_option requests), so we must ignore them. */ 93 if (!server_active) 94 error (1, 0, 95 "-q or -Q must be specified before \"%s\"", 96 cvs_cmd_name); 97 break; 98 case 'f': 99 force_tag_match = 0; 100 break; 101 case 'l': 102 local = 1; 103 break; 104 case 'R': 105 local = 0; 106 break; 107 case 't': 108 toptwo_diffs = 1; 109 break; 110 case 's': 111 patch_short = 1; 112 break; 113 case 'D': 114 if (rev2 != NULL || date2 != NULL) 115 error (1, 0, 116 "no more than two revisions/dates can be specified"); 117 if (rev1 != NULL || date1 != NULL) 118 date2 = Make_Date (optarg); 119 else 120 date1 = Make_Date (optarg); 121 break; 122 case 'r': 123 if (rev2 != NULL || date2 != NULL) 124 error (1, 0, 125 "no more than two revisions/dates can be specified"); 126 if (rev1 != NULL || date1 != NULL) 127 rev2 = optarg; 128 else 129 rev1 = optarg; 130 break; 131 case 'k': 132 if (options) 133 free (options); 134 options = RCS_check_kflag (optarg); 135 break; 136 case 'V': 137 /* This option is pretty seriously broken: 138 1. It is not clear what it does (does it change keyword 139 expansion behavior? If so, how? Or does it have 140 something to do with what version of RCS we are using? 141 Or the format we write RCS files in?). 142 2. Because both it and -k use the options variable, 143 specifying both -V and -k doesn't work. 144 3. At least as of CVS 1.9, it doesn't work (failed 145 assertion in RCS_checkout where it asserts that options 146 starts with -k). Few people seem to be complaining. 147 In the future (perhaps the near future), I have in mind 148 removing it entirely, and updating NEWS and cvs.texinfo, 149 but in case it is a good idea to give people more time 150 to complain if they would miss it, I'll just add this 151 quick and dirty error message for now. */ 152 error (1, 0, 153 "the -V option is obsolete and should not be used"); 154 break; 155 case 'u': 156 unidiff = 1; /* Unidiff */ 157 break; 158 case 'c': /* Context diff */ 159 unidiff = 0; 160 break; 161 case 'p': 162 show_c_func = 1; 163 break; 164 case '?': 165 default: 166 usage (patch_usage); 167 break; 168 } 169 } 170 argc -= optind; 171 argv += optind; 172 173 /* Sanity checks */ 174 if (argc < 1) 175 usage (patch_usage); 176 177 if (toptwo_diffs && patch_short) 178 error (1, 0, "-t and -s options are mutually exclusive"); 179 if (toptwo_diffs && (date1 != NULL || date2 != NULL || 180 rev1 != NULL || rev2 != NULL)) 181 error (1, 0, "must not specify revisions/dates with -t option!"); 182 183 if (!toptwo_diffs && (date1 == NULL && date2 == NULL && 184 rev1 == NULL && rev2 == NULL)) 185 error (1, 0, "must specify at least one revision/date!"); 186 if (date1 != NULL && date2 != NULL) 187 if (RCS_datecmp (date1, date2) >= 0) 188 error (1, 0, "second date must come after first date!"); 189 190 /* if options is NULL, make it a NULL string */ 191 if (options == NULL) 192 options = xstrdup (""); 193 194 #ifdef CLIENT_SUPPORT 195 if (current_parsed_root->isremote) 196 { 197 /* We're the client side. Fire up the remote server. */ 198 start_server (); 199 200 ign_setup (); 201 202 if (local) 203 send_arg("-l"); 204 if (!force_tag_match) 205 send_arg("-f"); 206 if (toptwo_diffs) 207 send_arg("-t"); 208 if (patch_short) 209 send_arg("-s"); 210 if (unidiff) 211 send_arg("-u"); 212 if (show_c_func) 213 send_arg("-p"); 214 215 if (rev1) 216 option_with_arg ("-r", rev1); 217 if (date1) 218 client_senddate (date1); 219 if (rev2) 220 option_with_arg ("-r", rev2); 221 if (date2) 222 client_senddate (date2); 223 if (options[0] != '\0') 224 send_arg (options); 225 226 { 227 int i; 228 for (i = 0; i < argc; ++i) 229 send_arg (argv[i]); 230 } 231 232 send_to_server ("rdiff\012", 0); 233 return get_responses_and_close (); 234 } 235 #endif 236 237 /* clean up if we get a signal */ 238 #ifdef SIGABRT 239 (void)SIG_register (SIGABRT, patch_cleanup); 240 #endif 241 #ifdef SIGHUP 242 (void)SIG_register (SIGHUP, patch_cleanup); 243 #endif 244 #ifdef SIGINT 245 (void)SIG_register (SIGINT, patch_cleanup); 246 #endif 247 #ifdef SIGQUIT 248 (void)SIG_register (SIGQUIT, patch_cleanup); 249 #endif 250 #ifdef SIGPIPE 251 (void)SIG_register (SIGPIPE, patch_cleanup); 252 #endif 253 #ifdef SIGTERM 254 (void)SIG_register (SIGTERM, patch_cleanup); 255 #endif 256 257 db = open_module (); 258 for (i = 0; i < argc; i++) 259 err += do_module (db, argv[i], PATCH, "Patching", patch_proc, 260 NULL, 0, local, 0, 0, NULL); 261 close_module (db); 262 free (options); 263 patch_cleanup (0); 264 return err; 265 } 266 267 268 269 /* 270 * callback proc for doing the real work of patching 271 */ 272 /* ARGSUSED */ 273 static int 274 patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile, 275 int shorten, int local_specified, char *mname, char *msg) 276 { 277 char *myargv[2]; 278 int err = 0; 279 int which; 280 char *repository; 281 char *where; 282 283 TRACE ( TRACE_FUNCTION, "patch_proc ( %s, %s, %s, %d, %d, %s, %s )", 284 xwhere ? xwhere : "(null)", 285 mwhere ? mwhere : "(null)", 286 mfile ? mfile : "(null)", 287 shorten, local_specified, 288 mname ? mname : "(null)", 289 msg ? msg : "(null)" ); 290 291 repository = xmalloc (strlen (current_parsed_root->directory) 292 + strlen (argv[0]) 293 + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); 294 (void)sprintf (repository, "%s/%s", 295 current_parsed_root->directory, argv[0]); 296 where = xmalloc (strlen (argv[0]) 297 + (mfile == NULL ? 0 : strlen (mfile) + 1) 298 + 1); 299 (void)strcpy (where, argv[0]); 300 301 /* if mfile isn't null, we need to set up to do only part of the module */ 302 if (mfile != NULL) 303 { 304 char *cp; 305 char *path; 306 307 /* if the portion of the module is a path, put the dir part on repos */ 308 if ((cp = strrchr (mfile, '/')) != NULL) 309 { 310 *cp = '\0'; 311 (void)strcat (repository, "/"); 312 (void)strcat (repository, mfile); 313 (void)strcat (where, "/"); 314 (void)strcat (where, mfile); 315 mfile = cp + 1; 316 } 317 318 /* take care of the rest */ 319 path = xmalloc (strlen (repository) + strlen (mfile) + 2); 320 (void)sprintf (path, "%s/%s", repository, mfile); 321 if (isdir (path)) 322 { 323 /* directory means repository gets the dir tacked on */ 324 (void)strcpy (repository, path); 325 (void)strcat (where, "/"); 326 (void)strcat (where, mfile); 327 } 328 else 329 { 330 myargv[0] = argv[0]; 331 myargv[1] = mfile; 332 argc = 2; 333 argv = myargv; 334 } 335 free (path); 336 } 337 338 /* cd to the starting repository */ 339 if (CVS_CHDIR (repository) < 0) 340 { 341 error (0, errno, "cannot chdir to %s", repository); 342 free (repository); 343 free (where); 344 return 1; 345 } 346 347 if (force_tag_match) 348 which = W_REPOS | W_ATTIC; 349 else 350 which = W_REPOS; 351 352 if (rev1 != NULL && !rev1_validated) 353 { 354 tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0, 355 repository, false); 356 rev1_validated = 1; 357 } 358 if (rev2 != NULL && !rev2_validated) 359 { 360 tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0, 361 repository, false); 362 rev2_validated = 1; 363 } 364 365 /* start the recursion processor */ 366 err = start_recursion (patch_fileproc, NULL, patch_dirproc, NULL, NULL, 367 argc - 1, argv + 1, local_specified, 368 which, 0, CVS_LOCK_READ, where, 1, repository ); 369 free (repository); 370 free (where); 371 372 return err; 373 } 374 375 376 377 /* 378 * Called to examine a particular RCS file, as appropriate with the options 379 * that were set above. 380 */ 381 /* ARGSUSED */ 382 static int 383 patch_fileproc (void *callerdat, struct file_info *finfo) 384 { 385 struct utimbuf t; 386 char *vers_tag, *vers_head; 387 char *rcs = NULL; 388 char *rcs_orig = NULL; 389 RCSNode *rcsfile; 390 FILE *fp1, *fp2, *fp3; 391 int ret = 0; 392 int isattic = 0; 393 int retcode = 0; 394 char *file1; 395 char *file2; 396 char *strippath; 397 char *line1, *line2; 398 size_t line1_chars_allocated; 399 size_t line2_chars_allocated; 400 char *cp1, *cp2; 401 FILE *fp; 402 int line_length; 403 int dargc = 0; 404 size_t darg_allocated = 0; 405 char **dargv = NULL; 406 407 line1 = NULL; 408 line1_chars_allocated = 0; 409 line2 = NULL; 410 line2_chars_allocated = 0; 411 vers_tag = vers_head = NULL; 412 413 /* find the parsed rcs file */ 414 if ((rcsfile = finfo->rcs) == NULL) 415 { 416 ret = 1; 417 goto out2; 418 } 419 if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC)) 420 isattic = 1; 421 422 rcs_orig = rcs = Xasprintf ("%s%s", finfo->file, RCSEXT); 423 424 /* if vers_head is NULL, may have been removed from the release */ 425 if (isattic && rev2 == NULL && date2 == NULL) 426 vers_head = NULL; 427 else 428 { 429 vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match, 430 NULL); 431 if (vers_head != NULL && RCS_isdead (rcsfile, vers_head)) 432 { 433 free (vers_head); 434 vers_head = NULL; 435 } 436 } 437 438 if (toptwo_diffs) 439 { 440 if (vers_head == NULL) 441 { 442 ret = 1; 443 goto out2; 444 } 445 446 if (!date1) 447 date1 = xmalloc (MAXDATELEN); 448 *date1 = '\0'; 449 if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1) 450 { 451 if (!really_quiet) 452 error (0, 0, "cannot find date in rcs file %s revision %s", 453 rcs, vers_head); 454 ret = 1; 455 goto out2; 456 } 457 } 458 vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, NULL); 459 if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag)) 460 { 461 free (vers_tag); 462 vers_tag = NULL; 463 } 464 465 if ((vers_tag == NULL && vers_head == NULL) || 466 (vers_tag != NULL && vers_head != NULL && 467 strcmp (vers_head, vers_tag) == 0)) 468 { 469 /* Nothing known about specified revs or 470 * not changed between releases. 471 */ 472 ret = 0; 473 goto out2; 474 } 475 476 if (patch_short && (vers_tag == NULL || vers_head == NULL)) 477 { 478 /* For adds & removes with a short patch requested, we can print our 479 * error message now and get out. 480 */ 481 cvs_output ("File ", 0); 482 cvs_output (finfo->fullname, 0); 483 if (vers_tag == NULL) 484 { 485 cvs_output (" is new; ", 0); 486 cvs_output (rev2 ? rev2 : date2 ? date2 : "current", 0); 487 cvs_output (" revision ", 0); 488 cvs_output (vers_head, 0); 489 cvs_output ("\n", 1); 490 } 491 else 492 { 493 cvs_output (" is removed; ", 0); 494 cvs_output (rev1 ? rev1 : date1, 0); 495 cvs_output (" revision ", 0); 496 cvs_output (vers_tag, 0); 497 cvs_output ("\n", 1); 498 } 499 ret = 0; 500 goto out2; 501 } 502 503 /* cvsacl patch */ 504 #ifdef SERVER_SUPPORT 505 if (use_cvs_acl /* && server_active */) 506 { 507 if (rev1) 508 { 509 if (!access_allowed (finfo->file, finfo->repository, rev1, 5, 510 NULL, NULL, 1)) 511 { 512 if (stop_at_first_permission_denied) 513 error (1, 0, "permission denied for %s", 514 Short_Repository (finfo->repository)); 515 else 516 error (0, 0, "permission denied for %s/%s", 517 Short_Repository (finfo->repository), finfo->file); 518 519 return (0); 520 } 521 } 522 if (rev2) 523 { 524 if (!access_allowed (finfo->file, finfo->repository, rev2, 5, 525 NULL, NULL, 1)) 526 { 527 if (stop_at_first_permission_denied) 528 error (1, 0, "permission denied for %s", 529 Short_Repository (finfo->repository)); 530 else 531 error (0, 0, "permission denied for %s/%s", 532 Short_Repository (finfo->repository), finfo->file); 533 534 return (0); 535 } 536 } 537 } 538 #endif 539 540 /* Create 3 empty files. I'm not really sure there is any advantage 541 * to doing so now rather than just waiting until later. 542 * 543 * There is - cvs_temp_file opens the file so that it can guarantee that 544 * we have exclusive write access to the file. Unfortunately we spoil that 545 * by closing it and reopening it again. Of course any better solution 546 * requires that the RCS functions accept open file pointers rather than 547 * simple file names. 548 */ 549 if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL) 550 { 551 error (0, errno, "cannot create temporary file %s", tmpfile1); 552 ret = 1; 553 goto out; 554 } 555 else 556 if (fclose (fp1) < 0) 557 error (0, errno, "warning: cannot close %s", tmpfile1); 558 if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL) 559 { 560 error (0, errno, "cannot create temporary file %s", tmpfile2); 561 ret = 1; 562 goto out; 563 } 564 else 565 if (fclose (fp2) < 0) 566 error (0, errno, "warning: cannot close %s", tmpfile2); 567 if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL) 568 { 569 error (0, errno, "cannot create temporary file %s", tmpfile3); 570 ret = 1; 571 goto out; 572 } 573 else 574 if (fclose (fp3) < 0) 575 error (0, errno, "warning: cannot close %s", tmpfile3); 576 577 if (vers_tag != NULL) 578 { 579 retcode = RCS_checkout (rcsfile, NULL, vers_tag, rev1, options, 580 tmpfile1, NULL, NULL); 581 if (retcode != 0) 582 { 583 error (0, 0, 584 "cannot check out revision %s of %s", vers_tag, rcs); 585 ret = 1; 586 goto out; 587 } 588 memset ((char *) &t, 0, sizeof (t)); 589 if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag, 590 NULL, 0)) != -1) 591 /* I believe this timestamp only affects the dates in our diffs, 592 and therefore should be on the server, not the client. */ 593 (void)utime (tmpfile1, &t); 594 } 595 else if (toptwo_diffs) 596 { 597 ret = 1; 598 goto out; 599 } 600 if (vers_head != NULL) 601 { 602 retcode = RCS_checkout (rcsfile, NULL, vers_head, rev2, options, 603 tmpfile2, NULL, NULL); 604 if (retcode != 0) 605 { 606 error (0, 0, 607 "cannot check out revision %s of %s", vers_head, rcs); 608 ret = 1; 609 goto out; 610 } 611 if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head, 612 NULL, 0)) != -1) 613 /* I believe this timestamp only affects the dates in our diffs, 614 and therefore should be on the server, not the client. */ 615 (void)utime (tmpfile2, &t); 616 } 617 618 if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u"); 619 else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c"); 620 if (show_c_func) 621 run_add_arg_p (&dargc, &darg_allocated, &dargv, "-p"); 622 switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv, 623 tmpfile3)) 624 { 625 case -1: /* fork/wait failure */ 626 error (1, errno, "fork for diff failed on %s", rcs); 627 break; 628 case 0: /* nothing to do */ 629 break; 630 case 1: 631 /* 632 * The two revisions are really different, so read the first two 633 * lines of the diff output file, and munge them to include more 634 * reasonable file names that "patch" will understand, unless the 635 * user wanted a short patch. In that case, just output the short 636 * message. 637 */ 638 if (patch_short) 639 { 640 cvs_output ("File ", 0); 641 cvs_output (finfo->fullname, 0); 642 cvs_output (" changed from revision ", 0); 643 cvs_output (vers_tag, 0); 644 cvs_output (" to ", 0); 645 cvs_output (vers_head, 0); 646 cvs_output ("\n", 1); 647 ret = 0; 648 goto out; 649 } 650 651 /* Output an "Index:" line for patch to use */ 652 cvs_output ("Index: ", 0); 653 cvs_output (finfo->fullname, 0); 654 cvs_output ("\n", 1); 655 656 /* Now the munging. */ 657 fp = xfopen (tmpfile3, "r"); 658 if (getline (&line1, &line1_chars_allocated, fp) < 0 || 659 getline (&line2, &line2_chars_allocated, fp) < 0) 660 { 661 if (line1 && strncmp("Binary files ", line1, 13) == 0) { 662 cvs_output ("Binary files are different\n", 0); 663 } else { 664 if (feof (fp)) 665 error (0, 0, "\ 666 failed to read diff file header %s for %s: end of file", tmpfile3, rcs); 667 else 668 error (0, errno, 669 "failed to read diff file header %s for %s", 670 tmpfile3, rcs); 671 ret = 1; 672 } 673 if (fclose (fp) < 0) 674 error (0, errno, "error closing %s", tmpfile3); 675 goto out; 676 } 677 if (!unidiff) 678 { 679 if (strncmp (line1, "*** ", 4) != 0 || 680 strncmp (line2, "--- ", 4) != 0 || 681 (cp1 = strchr (line1, '\t')) == NULL || 682 (cp2 = strchr (line2, '\t')) == NULL) 683 { 684 error (0, 0, "invalid diff header for %s", rcs); 685 ret = 1; 686 if (fclose (fp) < 0) 687 error (0, errno, "error closing %s", tmpfile3); 688 goto out; 689 } 690 } 691 else 692 { 693 if (strncmp (line1, "--- ", 4) != 0 || 694 strncmp (line2, "+++ ", 4) != 0 || 695 (cp1 = strchr (line1, '\t')) == NULL || 696 (cp2 = strchr (line2, '\t')) == NULL) 697 { 698 error (0, 0, "invalid unidiff header for %s", rcs); 699 ret = 1; 700 if (fclose (fp) < 0) 701 error (0, errno, "error closing %s", tmpfile3); 702 goto out; 703 } 704 } 705 assert (current_parsed_root != NULL); 706 assert (current_parsed_root->directory != NULL); 707 708 strippath = Xasprintf ("%s/", current_parsed_root->directory); 709 710 if (strncmp (rcs, strippath, strlen (strippath)) == 0) 711 rcs += strlen (strippath); 712 free (strippath); 713 if (vers_tag != NULL) 714 file1 = Xasprintf ("%s:%s", finfo->fullname, vers_tag); 715 else 716 file1 = xstrdup (DEVNULL); 717 718 file2 = Xasprintf ("%s:%s", finfo->fullname, 719 vers_head ? vers_head : "removed"); 720 721 /* Note that the string "diff" is specified by POSIX (for -c) 722 and is part of the diff output format, not the name of a 723 program. */ 724 if (unidiff) 725 { 726 cvs_output ("diff -u ", 0); 727 cvs_output (file1, 0); 728 cvs_output (" ", 1); 729 cvs_output (file2, 0); 730 cvs_output ("\n", 1); 731 732 cvs_output ("--- ", 0); 733 cvs_output (file1, 0); 734 cvs_output (cp1, 0); 735 cvs_output ("+++ ", 0); 736 } 737 else 738 { 739 cvs_output ("diff -c ", 0); 740 cvs_output (file1, 0); 741 cvs_output (" ", 1); 742 cvs_output (file2, 0); 743 cvs_output ("\n", 1); 744 745 cvs_output ("*** ", 0); 746 cvs_output (file1, 0); 747 cvs_output (cp1, 0); 748 cvs_output ("--- ", 0); 749 } 750 751 cvs_output (finfo->fullname, 0); 752 cvs_output (cp2, 0); 753 754 /* spew the rest of the diff out */ 755 while ((line_length 756 = getline (&line1, &line1_chars_allocated, fp)) 757 >= 0) 758 cvs_output (line1, 0); 759 if (line_length < 0 && !feof (fp)) 760 error (0, errno, "cannot read %s", tmpfile3); 761 762 if (fclose (fp) < 0) 763 error (0, errno, "cannot close %s", tmpfile3); 764 free (file1); 765 free (file2); 766 break; 767 default: 768 error (0, 0, "diff failed for %s", finfo->fullname); 769 } 770 out: 771 if (line1) 772 free (line1); 773 if (line2) 774 free (line2); 775 if (CVS_UNLINK (tmpfile1) < 0) 776 error (0, errno, "cannot unlink %s", tmpfile1); 777 if (CVS_UNLINK (tmpfile2) < 0) 778 error (0, errno, "cannot unlink %s", tmpfile2); 779 if (CVS_UNLINK (tmpfile3) < 0) 780 error (0, errno, "cannot unlink %s", tmpfile3); 781 free (tmpfile1); 782 free (tmpfile2); 783 free (tmpfile3); 784 tmpfile1 = tmpfile2 = tmpfile3 = NULL; 785 if (darg_allocated) 786 { 787 run_arg_free_p (dargc, dargv); 788 free (dargv); 789 } 790 791 out2: 792 if (vers_tag != NULL) 793 free (vers_tag); 794 if (vers_head != NULL) 795 free (vers_head); 796 if (rcs_orig) 797 free (rcs_orig); 798 return ret; 799 } 800 801 802 803 /* 804 * Print a warm fuzzy message 805 */ 806 /* ARGSUSED */ 807 static Dtype 808 patch_dirproc (void *callerdat, const char *dir, const char *repos, 809 const char *update_dir, List *entries) 810 { 811 if (!quiet) 812 error (0, 0, "Diffing %s", update_dir); 813 return R_PROCESS; 814 } 815 816 817 818 /* 819 * Clean up temporary files 820 */ 821 static RETSIGTYPE 822 patch_cleanup (int sig) 823 { 824 /* Note that the checks for existence_error are because we are 825 called from a signal handler, without SIG_begincrsect, so 826 we don't know whether the files got created. */ 827 828 static int reenter = 0; 829 830 if (reenter++) 831 _exit(1); 832 833 if (tmpfile1 != NULL) 834 { 835 if (unlink_file (tmpfile1) < 0 836 && !existence_error (errno)) 837 error (0, errno, "cannot remove %s", tmpfile1); 838 free (tmpfile1); 839 } 840 if (tmpfile2 != NULL) 841 { 842 if (unlink_file (tmpfile2) < 0 843 && !existence_error (errno)) 844 error (0, errno, "cannot remove %s", tmpfile2); 845 free (tmpfile2); 846 } 847 if (tmpfile3 != NULL) 848 { 849 if (unlink_file (tmpfile3) < 0 850 && !existence_error (errno)) 851 error (0, errno, "cannot remove %s", tmpfile3); 852 free (tmpfile3); 853 } 854 tmpfile1 = tmpfile2 = tmpfile3 = NULL; 855 856 if (sig != 0) 857 { 858 const char *name; 859 char temp[10]; 860 861 switch (sig) 862 { 863 #ifdef SIGABRT 864 case SIGABRT: 865 name = "abort"; 866 break; 867 #endif 868 #ifdef SIGHUP 869 case SIGHUP: 870 name = "hangup"; 871 break; 872 #endif 873 #ifdef SIGINT 874 case SIGINT: 875 name = "interrupt"; 876 break; 877 #endif 878 #ifdef SIGQUIT 879 case SIGQUIT: 880 name = "quit"; 881 break; 882 #endif 883 #ifdef SIGPIPE 884 case SIGPIPE: 885 name = "broken pipe"; 886 break; 887 #endif 888 #ifdef SIGTERM 889 case SIGTERM: 890 name = "termination"; 891 break; 892 #endif 893 default: 894 /* This case should never be reached, because we list 895 above all the signals for which we actually establish a 896 signal handler. */ 897 sprintf (temp, "%d", sig); 898 name = temp; 899 break; 900 } 901 error (0, 0, "received %s signal", name); 902 } 903 } 904