1 /* $OpenBSD: commit.c,v 1.160 2019/06/28 13:35:00 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/stat.h> 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "cvs.h" 28 #include "diff.h" 29 #include "remote.h" 30 31 void cvs_commit_local(struct cvs_file *); 32 void cvs_commit_check_files(struct cvs_file *); 33 void cvs_commit_loginfo(char *); 34 void cvs_commit_lock_dirs(struct cvs_file *); 35 36 static BUF *commit_diff(struct cvs_file *, RCSNUM *, int); 37 static void commit_desc_set(struct cvs_file *); 38 39 struct file_info_list files_info; 40 struct trigger_list *line_list; 41 42 struct cvs_flisthead files_affected; 43 struct cvs_flisthead files_added; 44 struct cvs_flisthead files_removed; 45 struct cvs_flisthead files_modified; 46 47 char *logmsg = NULL; 48 char *loginfo = NULL; 49 50 static int conflicts_found; 51 52 struct cvs_cmd cvs_cmd_commit = { 53 CVS_OP_COMMIT, CVS_USE_WDIR, "commit", 54 { "ci", "com" }, 55 "Check files into the repository", 56 "[-flR] [-F logfile | -m msg] [-r rev] ...", 57 "F:flm:Rr:", 58 NULL, 59 cvs_commit 60 }; 61 62 int 63 cvs_commit(int argc, char **argv) 64 { 65 int flags; 66 int ch, Fflag, mflag; 67 struct module_checkout *mc; 68 struct cvs_recursion cr; 69 struct cvs_filelist *l; 70 struct file_info *fi; 71 char *arg = ".", repo[PATH_MAX]; 72 73 flags = CR_RECURSE_DIRS; 74 Fflag = mflag = 0; 75 76 while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) { 77 switch (ch) { 78 case 'F': 79 /* free previously assigned value */ 80 free(logmsg); 81 logmsg = cvs_logmsg_read(optarg); 82 Fflag = 1; 83 break; 84 case 'f': 85 break; 86 case 'l': 87 flags &= ~CR_RECURSE_DIRS; 88 break; 89 case 'm': 90 /* free previously assigned value */ 91 free(logmsg); 92 logmsg = xstrdup(optarg); 93 mflag = 1; 94 break; 95 case 'R': 96 flags |= CR_RECURSE_DIRS; 97 break; 98 case 'r': 99 break; 100 default: 101 fatal("%s", cvs_cmd_commit.cmd_synopsis); 102 } 103 } 104 105 argc -= optind; 106 argv += optind; 107 108 /* -F and -m are mutually exclusive */ 109 if (Fflag && mflag) 110 fatal("cannot specify both a log file and a message"); 111 112 RB_INIT(&files_affected); 113 RB_INIT(&files_added); 114 RB_INIT(&files_removed); 115 RB_INIT(&files_modified); 116 117 TAILQ_INIT(&files_info); 118 conflicts_found = 0; 119 120 cr.enterdir = NULL; 121 cr.leavedir = NULL; 122 cr.fileproc = cvs_commit_check_files; 123 cr.flags = flags; 124 125 if (argc > 0) 126 cvs_file_run(argc, argv, &cr); 127 else 128 cvs_file_run(1, &arg, &cr); 129 130 if (conflicts_found != 0) 131 fatal("%d conflicts found, please correct these first", 132 conflicts_found); 133 134 if (RB_EMPTY(&files_affected)) 135 return (0); 136 137 if (cvsroot_is_remote()) { 138 if (logmsg == NULL) { 139 logmsg = cvs_logmsg_create(NULL, &files_added, 140 &files_removed, &files_modified); 141 if (logmsg == NULL) 142 fatal("This shouldnt happen, honestly!"); 143 } 144 cvs_client_connect_to_server(); 145 cr.fileproc = cvs_client_sendfile; 146 147 if (argc > 0) 148 cvs_file_run(argc, argv, &cr); 149 else 150 cvs_file_run(1, &arg, &cr); 151 152 if (!(flags & CR_RECURSE_DIRS)) 153 cvs_client_send_request("Argument -l"); 154 155 cvs_client_send_logmsg(logmsg); 156 cvs_client_send_files(argv, argc); 157 cvs_client_senddir("."); 158 cvs_client_send_request("ci"); 159 cvs_client_get_responses(); 160 } else { 161 cvs_get_repository_name(".", repo, PATH_MAX); 162 163 line_list = cvs_trigger_getlines(CVS_PATH_COMMITINFO, repo); 164 if (line_list != NULL) { 165 RB_FOREACH(l, cvs_flisthead, &files_affected) { 166 fi = xcalloc(1, sizeof(*fi)); 167 fi->file_path = xstrdup(l->file_path); 168 TAILQ_INSERT_TAIL(&files_info, fi, 169 flist); 170 } 171 172 if (cvs_trigger_handle(CVS_TRIGGER_COMMITINFO, 173 repo, NULL, line_list, &files_info)) { 174 cvs_log(LP_ERR, 175 "Pre-commit check failed"); 176 cvs_trigger_freelist(line_list); 177 goto end; 178 } 179 180 cvs_trigger_freelist(line_list); 181 cvs_trigger_freeinfo(&files_info); 182 } 183 184 if (cvs_server_active) { 185 if (logmsg == NULL) 186 fatal("no log message specified"); 187 } else if (logmsg == NULL) { 188 logmsg = cvs_logmsg_create(NULL, &files_added, 189 &files_removed, &files_modified); 190 if (logmsg == NULL) 191 fatal("This shouldnt happen, honestly!"); 192 } 193 194 if (cvs_logmsg_verify(logmsg)) 195 goto end; 196 197 cr.fileproc = cvs_commit_lock_dirs; 198 cvs_file_walklist(&files_affected, &cr); 199 200 line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo); 201 202 cr.fileproc = cvs_commit_local; 203 cvs_file_walklist(&files_affected, &cr); 204 205 if (line_list != NULL) { 206 cvs_commit_loginfo(repo); 207 208 cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo, 209 loginfo, line_list, &files_info); 210 211 free(loginfo); 212 cvs_trigger_freelist(line_list); 213 cvs_trigger_freeinfo(&files_info); 214 } 215 216 mc = cvs_module_lookup(repo); 217 if (mc->mc_prog != NULL && 218 (mc->mc_flags & MODULE_RUN_ON_COMMIT)) 219 cvs_exec(mc->mc_prog, NULL, 0); 220 } 221 222 end: 223 cvs_trigger_freeinfo(&files_info); 224 free(logmsg); 225 return (0); 226 } 227 228 void 229 cvs_commit_loginfo(char *repo) 230 { 231 BUF *buf; 232 char pwd[PATH_MAX]; 233 struct cvs_filelist *cf; 234 235 if (getcwd(pwd, sizeof(pwd)) == NULL) 236 fatal("Can't get working directory"); 237 238 buf = buf_alloc(1024); 239 240 cvs_trigger_loginfo_header(buf, repo); 241 242 if (!RB_EMPTY(&files_added)) { 243 buf_puts(buf, "Added Files:"); 244 245 RB_FOREACH(cf, cvs_flisthead, &files_added) { 246 buf_putc(buf, '\n'); 247 buf_putc(buf, '\t'); 248 buf_puts(buf, cf->file_path); 249 } 250 251 buf_putc(buf, '\n'); 252 } 253 254 if (!RB_EMPTY(&files_modified)) { 255 buf_puts(buf, "Modified Files:"); 256 257 RB_FOREACH(cf, cvs_flisthead, &files_modified) { 258 buf_putc(buf, '\n'); 259 buf_putc(buf, '\t'); 260 buf_puts(buf, cf->file_path); 261 } 262 263 buf_putc(buf, '\n'); 264 } 265 266 if (!RB_EMPTY(&files_removed)) { 267 buf_puts(buf, "Removed Files:"); 268 269 RB_FOREACH(cf, cvs_flisthead, &files_removed) { 270 buf_putc(buf, '\n'); 271 buf_putc(buf, '\t'); 272 buf_puts(buf, cf->file_path); 273 } 274 275 buf_putc(buf, '\n'); 276 } 277 278 buf_puts(buf, "Log Message:\n"); 279 280 buf_puts(buf, logmsg); 281 282 buf_putc(buf, '\n'); 283 buf_putc(buf, '\0'); 284 285 loginfo = buf_release(buf); 286 } 287 288 void 289 cvs_commit_lock_dirs(struct cvs_file *cf) 290 { 291 char repo[PATH_MAX]; 292 293 cvs_get_repository_path(cf->file_wd, repo, sizeof(repo)); 294 cvs_log(LP_TRACE, "cvs_commit_lock_dirs: %s", repo); 295 296 /* locks stay in place until we are fully done and exit */ 297 cvs_repository_lock(repo, 1); 298 } 299 300 void 301 cvs_commit_check_files(struct cvs_file *cf) 302 { 303 char *tag; 304 RCSNUM *branch, *brev; 305 306 branch = brev = NULL; 307 308 cvs_log(LP_TRACE, "cvs_commit_check_files(%s)", cf->file_path); 309 310 if (cvsroot_is_remote()) 311 cvs_remote_classify_file(cf); 312 else 313 cvs_file_classify(cf, cvs_directory_tag); 314 315 if (cf->file_type == CVS_DIR) { 316 if (verbosity > 1) 317 cvs_log(LP_NOTICE, "Examining %s", cf->file_path); 318 return; 319 } 320 321 if (cf->file_status == FILE_UPTODATE) 322 return; 323 324 if (cf->file_status == FILE_MERGE || 325 cf->file_status == FILE_PATCH || 326 cf->file_status == FILE_CHECKOUT || 327 cf->file_status == FILE_LOST || 328 cf->file_status == FILE_UNLINK) { 329 cvs_log(LP_ERR, "conflict: %s is not up-to-date", 330 cf->file_path); 331 conflicts_found++; 332 return; 333 } 334 335 if (cf->file_status == FILE_CONFLICT && 336 cf->file_ent->ce_conflict != NULL) { 337 cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from " 338 "merging, please fix these first", cf->file_path); 339 conflicts_found++; 340 return; 341 } 342 343 if (cf->file_status == FILE_MODIFIED && 344 cf->file_ent->ce_conflict != NULL && 345 update_has_conflict_markers(cf)) { 346 cvs_log(LP_ERR, "warning: file %s seems to still contain " 347 "conflict indicators", cf->file_path); 348 } 349 350 if (cf->file_ent != NULL && cf->file_ent->ce_date != -1) { 351 cvs_log(LP_ERR, "conflict: cannot commit to sticky date for %s", 352 cf->file_path); 353 conflicts_found++; 354 return; 355 } 356 357 if (cvsroot_is_local()) { 358 tag = cvs_directory_tag; 359 if (cf->file_ent != NULL) 360 tag = cf->file_ent->ce_tag; 361 362 if (tag != NULL && cf->file_rcs != NULL) { 363 brev = rcs_sym_getrev(cf->file_rcs, tag); 364 if (brev != NULL) { 365 if (!RCSNUM_ISBRANCH(brev)) { 366 cvs_log(LP_ERR, "sticky tag %s is not " 367 "a branch for file %s", tag, 368 cf->file_path); 369 conflicts_found++; 370 } 371 } 372 } 373 } 374 375 free(branch); 376 free(brev); 377 378 if (cf->file_status != FILE_ADDED && 379 cf->file_status != FILE_REMOVED && 380 cf->file_status != FILE_MODIFIED) 381 return; 382 383 cvs_file_get(cf->file_path, 0, &files_affected, CVS_FILE); 384 385 switch (cf->file_status) { 386 case FILE_ADDED: 387 cvs_file_get(cf->file_path, 0, &files_added, CVS_FILE); 388 break; 389 case FILE_REMOVED: 390 cvs_file_get(cf->file_path, 0, &files_removed, CVS_FILE); 391 break; 392 case FILE_MODIFIED: 393 cvs_file_get(cf->file_path, 0, &files_modified, CVS_FILE); 394 break; 395 } 396 } 397 398 void 399 cvs_commit_local(struct cvs_file *cf) 400 { 401 char *tag; 402 BUF *b, *d; 403 int onbranch, isnew, histtype, branchadded; 404 RCSNUM *nrev, *crev, *rrev, *brev; 405 int openflags, rcsflags; 406 char rbuf[CVS_REV_BUFSZ], nbuf[CVS_REV_BUFSZ]; 407 CVSENTRIES *entlist; 408 char attic[PATH_MAX], repo[PATH_MAX], rcsfile[PATH_MAX]; 409 struct file_info *fi; 410 411 cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path); 412 cvs_file_classify(cf, cvs_directory_tag); 413 414 if (cvs_noexec == 1) 415 return; 416 417 if (cf->file_type != CVS_FILE) 418 fatal("cvs_commit_local: '%s' is not a file", cf->file_path); 419 420 tag = cvs_directory_tag; 421 if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) 422 tag = cf->file_ent->ce_tag; 423 424 branchadded = 0; 425 switch (cf->file_status) { 426 case FILE_ADDED: 427 if (cf->file_rcs == NULL && tag != NULL) { 428 branchadded = 1; 429 cvs_add_tobranch(cf, tag); 430 } 431 break; 432 case FILE_MODIFIED: 433 case FILE_REMOVED: 434 if (cf->file_rcs == NULL) { 435 cvs_log(LP_ERR, "RCS file for %s got lost", 436 cf->file_path); 437 return; 438 } 439 break; 440 default: 441 cvs_log(LP_ERR, "skipping bogus file `%s'", cf->file_path); 442 return; 443 } 444 445 onbranch = 0; 446 nrev = RCS_HEAD_REV; 447 crev = NULL; 448 rrev = NULL; 449 d = NULL; 450 451 if (cf->file_rcs != NULL && cf->file_rcs->rf_branch != NULL) { 452 free(cf->file_rcs->rf_branch); 453 cf->file_rcs->rf_branch = NULL; 454 } 455 456 if (cf->file_rcs != NULL) { 457 rrev = rcs_head_get(cf->file_rcs); 458 crev = rcs_head_get(cf->file_rcs); 459 if (crev == NULL || rrev == NULL) 460 fatal("no head revision in RCS file for %s", 461 cf->file_path); 462 463 if (tag != NULL) { 464 free(crev); 465 free(rrev); 466 brev = rcs_sym_getrev(cf->file_rcs, tag); 467 crev = rcs_translate_tag(tag, cf->file_rcs); 468 if (brev == NULL || crev == NULL) { 469 fatal("failed to resolve existing tag: %s", 470 tag); 471 } 472 473 rrev = rcsnum_alloc(); 474 rcsnum_cpy(brev, rrev, brev->rn_len - 1); 475 476 if (RCSNUM_ISBRANCHREV(crev) && 477 rcsnum_cmp(crev, rrev, 0)) { 478 nrev = rcsnum_alloc(); 479 rcsnum_cpy(crev, nrev, 0); 480 rcsnum_inc(nrev); 481 } else if (!RCSNUM_ISBRANCH(crev)) { 482 nrev = rcsnum_brtorev(brev); 483 if (nrev == NULL) 484 fatal("failed to create branch rev"); 485 } else { 486 fatal("this isnt suppose to happen, honestly"); 487 } 488 489 free(brev); 490 free(rrev); 491 rrev = rcsnum_branch_root(nrev); 492 493 /* branch stuff was checked in cvs_commit_check_files */ 494 onbranch = 1; 495 } 496 497 rcsnum_tostr(crev, rbuf, sizeof(rbuf)); 498 } else { 499 strlcpy(rbuf, "Non-existent", sizeof(rbuf)); 500 } 501 502 free(rrev); 503 isnew = 0; 504 if (cf->file_status == FILE_ADDED) { 505 isnew = 1; 506 rcsflags = RCS_CREATE; 507 openflags = O_CREAT | O_RDONLY; 508 if (cf->file_rcs != NULL) { 509 if (!onbranch) { 510 if (cf->in_attic == 0) 511 cvs_log(LP_ERR, "warning: expected %s " 512 "to be in the Attic", 513 cf->file_path); 514 515 if (cf->file_rcs->rf_dead == 0) 516 cvs_log(LP_ERR, "warning: expected %s " 517 "to be dead", cf->file_path); 518 519 cvs_get_repository_path(cf->file_wd, repo, 520 PATH_MAX); 521 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s%s", 522 repo, cf->file_name, RCS_FILE_EXT); 523 524 if (rename(cf->file_rpath, rcsfile) == -1) 525 fatal("cvs_commit_local: failed to " 526 "move %s outside the Attic: %s", 527 cf->file_path, strerror(errno)); 528 529 free(cf->file_rpath); 530 cf->file_rpath = xstrdup(rcsfile); 531 isnew = 0; 532 } 533 534 rcsflags = RCS_READ | RCS_PARSE_FULLY; 535 openflags = O_RDONLY; 536 rcs_close(cf->file_rcs); 537 } 538 539 cf->repo_fd = open(cf->file_rpath, openflags); 540 if (cf->repo_fd == -1) 541 fatal("cvs_commit_local: %s", strerror(errno)); 542 543 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, 544 rcsflags, 0444); 545 if (cf->file_rcs == NULL) 546 fatal("cvs_commit_local: failed to create RCS file " 547 "for %s", cf->file_path); 548 549 commit_desc_set(cf); 550 551 if (branchadded) 552 strlcpy(rbuf, "Non-existent", sizeof(rbuf)); 553 } 554 555 if (verbosity > 1) { 556 cvs_printf("Checking in %s:\n", cf->file_path); 557 cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path); 558 cvs_printf("old revision: %s; ", rbuf); 559 } 560 561 if (isnew == 0 && cf->file_rcs->rf_head == NULL) 562 fatal("no head revision in RCS file for %s", cf->file_path); 563 564 if (isnew == 0 && onbranch == 0) 565 d = commit_diff(cf, cf->file_rcs->rf_head, 0); 566 567 if (cf->file_status == FILE_REMOVED) { 568 b = rcs_rev_getbuf(cf->file_rcs, crev, 0); 569 } else if (onbranch == 1) { 570 b = commit_diff(cf, crev, 1); 571 } else { 572 b = buf_load_fd(cf->fd); 573 } 574 575 if (isnew == 0 && onbranch == 0) { 576 if (rcs_deltatext_set(cf->file_rcs, crev, d) == -1) 577 fatal("cvs_commit_local: failed to set delta"); 578 } 579 580 if (rcs_rev_add(cf->file_rcs, nrev, logmsg, -1, NULL) == -1) 581 fatal("cvs_commit_local: failed to add new revision"); 582 583 if (nrev == RCS_HEAD_REV) 584 nrev = cf->file_rcs->rf_head; 585 586 if (rcs_deltatext_set(cf->file_rcs, nrev, b) == -1) 587 fatal("cvs_commit_local: failed to set new HEAD delta"); 588 589 if (cf->file_status == FILE_REMOVED) { 590 if (rcs_state_set(cf->file_rcs, nrev, RCS_STATE_DEAD) == -1) 591 fatal("cvs_commit_local: failed to set state"); 592 } 593 594 if (cf->file_status == FILE_ADDED && cf->file_ent->ce_opts != NULL) { 595 int cf_kflag; 596 597 cf_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2); 598 rcs_kwexp_set(cf->file_rcs, cf_kflag); 599 } 600 601 rcs_write(cf->file_rcs); 602 603 if (cf->file_status == FILE_REMOVED) { 604 strlcpy(nbuf, "Removed", sizeof(nbuf)); 605 } else if (cf->file_status == FILE_ADDED) { 606 if (cf->file_rcs->rf_dead == 1) 607 strlcpy(nbuf, "Initial Revision", sizeof(nbuf)); 608 else 609 rcsnum_tostr(nrev, nbuf, sizeof(nbuf)); 610 } else if (cf->file_status == FILE_MODIFIED) { 611 rcsnum_tostr(nrev, nbuf, sizeof(nbuf)); 612 } 613 614 if (verbosity > 1) 615 cvs_printf("new revision: %s\n", nbuf); 616 617 (void)unlink(cf->file_path); 618 (void)close(cf->fd); 619 cf->fd = -1; 620 621 if (cf->file_status != FILE_REMOVED) { 622 cvs_checkout_file(cf, nrev, NULL, CO_COMMIT); 623 } else { 624 entlist = cvs_ent_open(cf->file_wd); 625 cvs_ent_remove(entlist, cf->file_name); 626 627 cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); 628 629 (void)xsnprintf(attic, PATH_MAX, "%s/%s", 630 repo, CVS_PATH_ATTIC); 631 632 if (mkdir(attic, 0755) == -1 && errno != EEXIST) 633 fatal("cvs_commit_local: failed to create Attic"); 634 635 (void)xsnprintf(attic, PATH_MAX, "%s/%s/%s%s", repo, 636 CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 637 638 if (rename(cf->file_rpath, attic) == -1) 639 fatal("cvs_commit_local: failed to move %s to Attic", 640 cf->file_path); 641 642 if (cvs_server_active == 1) 643 cvs_server_update_entry("Remove-entry", cf); 644 } 645 646 if (verbosity > 1) 647 cvs_printf("done\n"); 648 else { 649 cvs_log(LP_NOTICE, "checking in '%s'; revision %s -> %s", 650 cf->file_path, rbuf, nbuf); 651 } 652 653 if (line_list != NULL) { 654 fi = xcalloc(1, sizeof(*fi)); 655 fi->file_path = xstrdup(cf->file_path); 656 fi->crevstr = xstrdup(rbuf); 657 fi->nrevstr = xstrdup(nbuf); 658 if (tag != NULL) 659 fi->tag_new = xstrdup(tag); 660 TAILQ_INSERT_TAIL(&files_info, fi, flist); 661 } 662 663 switch (cf->file_status) { 664 case FILE_MODIFIED: 665 histtype = CVS_HISTORY_COMMIT_MODIFIED; 666 break; 667 case FILE_ADDED: 668 histtype = CVS_HISTORY_COMMIT_ADDED; 669 break; 670 case FILE_REMOVED: 671 histtype = CVS_HISTORY_COMMIT_REMOVED; 672 break; 673 default: 674 histtype = -1; 675 break; 676 } 677 678 free(crev); 679 680 if (histtype != -1) 681 cvs_history_add(histtype, cf, NULL); 682 else 683 cvs_log(LP_NOTICE, "histtype was -1 for %s", cf->file_path); 684 } 685 686 static BUF * 687 commit_diff(struct cvs_file *cf, RCSNUM *rev, int reverse) 688 { 689 int fd1, fd2, d; 690 char *p1, *p2; 691 BUF *b; 692 693 (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 694 695 if (cf->file_status == FILE_MODIFIED || 696 cf->file_status == FILE_ADDED) { 697 b = buf_load_fd(cf->fd); 698 fd1 = buf_write_stmp(b, p1, NULL); 699 buf_free(b); 700 } else { 701 fd1 = rcs_rev_write_stmp(cf->file_rcs, rev, p1, 0); 702 } 703 704 (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 705 fd2 = rcs_rev_write_stmp(cf->file_rcs, rev, p2, RCS_KWEXP_NONE); 706 707 b = buf_alloc(128); 708 709 diff_format = D_RCSDIFF; 710 711 if (reverse == 1) 712 d = diffreg(p2, p1, fd2, fd1, b, D_FORCEASCII); 713 else 714 d = diffreg(p1, p2, fd1, fd2, b, D_FORCEASCII); 715 if (d == D_ERROR) 716 fatal("commit_diff: failed to get RCS patch"); 717 718 close(fd1); 719 close(fd2); 720 721 free(p1); 722 free(p2); 723 724 return (b); 725 } 726 727 static void 728 commit_desc_set(struct cvs_file *cf) 729 { 730 BUF *bp; 731 int fd; 732 char desc_path[PATH_MAX], *desc; 733 734 (void)xsnprintf(desc_path, PATH_MAX, "%s/%s/%s%s", 735 cf->file_wd, CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT); 736 737 if ((fd = open(desc_path, O_RDONLY)) == -1) 738 return; 739 740 bp = buf_load_fd(fd); 741 buf_putc(bp, '\0'); 742 desc = buf_release(bp); 743 744 rcs_desc_set(cf->file_rcs, desc); 745 746 (void)close(fd); 747 (void)cvs_unlink(desc_path); 748 749 free(desc); 750 } 751