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 1.4 kit. 7 * 8 * Commit Files 9 * 10 * "commit" commits the present version to the RCS repository, AFTER 11 * having done a test on conflicts. 12 * 13 * The call is: cvs commit [options] files... 14 * 15 */ 16 17 #include <assert.h> 18 #include "cvs.h" 19 #include "getline.h" 20 #include "edit.h" 21 #include "fileattr.h" 22 23 static Dtype check_direntproc PROTO ((void *callerdat, char *dir, 24 char *repos, char *update_dir, 25 List *entries)); 26 static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 27 static int check_filesdoneproc PROTO ((void *callerdat, int err, 28 char *repos, char *update_dir, 29 List *entries)); 30 static int checkaddfile PROTO((char *file, char *repository, char *tag, 31 char *options, RCSNode **rcsnode)); 32 static Dtype commit_direntproc PROTO ((void *callerdat, char *dir, 33 char *repos, char *update_dir, 34 List *entries)); 35 static int commit_dirleaveproc PROTO ((void *callerdat, char *dir, 36 int err, char *update_dir, 37 List *entries)); 38 static int commit_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 39 static int commit_filesdoneproc PROTO ((void *callerdat, int err, 40 char *repository, char *update_dir, 41 List *entries)); 42 static int finaladd PROTO((struct file_info *finfo, char *revision, char *tag, 43 char *options)); 44 static int findmaxrev PROTO((Node * p, void *closure)); 45 static int lock_RCS PROTO((char *user, RCSNode *rcs, char *rev, 46 char *repository)); 47 static int precommit_list_proc PROTO((Node * p, void *closure)); 48 static int precommit_proc PROTO((char *repository, char *filter)); 49 static int remove_file PROTO ((struct file_info *finfo, char *tag, 50 char *message)); 51 static void fix_rcs_modes PROTO((char *rcs, char *user)); 52 static void fixaddfile PROTO((char *file, char *repository)); 53 static void fixbranch PROTO((RCSNode *, char *branch)); 54 static void unlockrcs PROTO((RCSNode *rcs)); 55 static void ci_delproc PROTO((Node *p)); 56 static void masterlist_delproc PROTO((Node *p)); 57 static void locate_rcs PROTO((char *file, char *repository, char *rcs)); 58 59 struct commit_info 60 { 61 Ctype status; /* as returned from Classify_File() */ 62 char *rev; /* a numeric rev, if we know it */ 63 char *tag; /* any sticky tag, or -r option */ 64 char *options; /* Any sticky -k option */ 65 }; 66 struct master_lists 67 { 68 List *ulist; /* list for Update_Logfile */ 69 List *cilist; /* list with commit_info structs */ 70 }; 71 72 static int force_ci = 0; 73 static int got_message; 74 static int run_module_prog = 1; 75 static int aflag; 76 static char *tag; 77 static char *write_dirtag; 78 static char *logfile; 79 static List *mulist; 80 static char *message; 81 static time_t last_register_time; 82 83 84 static const char *const commit_usage[] = 85 { 86 "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n", 87 "\t-n\tDo not run the module program (if any).\n", 88 "\t-R\tProcess directories recursively.\n", 89 "\t-l\tLocal directory only (not recursive).\n", 90 "\t-f\tForce the file to be committed; disables recursion.\n", 91 "\t-F file\tRead the log message from file.\n", 92 "\t-m msg\tLog message.\n", 93 "\t-r rev\tCommit to this branch or trunk revision.\n", 94 NULL 95 }; 96 97 #ifdef CLIENT_SUPPORT 98 /* Identify a file which needs "? foo" or a Questionable request. */ 99 struct question { 100 /* The two fields for the Directory request. */ 101 char *dir; 102 char *repos; 103 104 /* The file name. */ 105 char *file; 106 107 struct question *next; 108 }; 109 110 struct find_data { 111 List *ulist; 112 int argc; 113 char **argv; 114 115 /* This is used from dirent to filesdone time, for each directory, 116 to make a list of files we have already seen. */ 117 List *ignlist; 118 119 /* Linked list of files which need "? foo" or a Questionable request. */ 120 struct question *questionables; 121 122 /* Only good within functions called from the filesdoneproc. Stores 123 the repository (pointer into storage managed by the recursion 124 processor. */ 125 char *repository; 126 }; 127 128 static Dtype find_dirent_proc PROTO ((void *callerdat, char *dir, 129 char *repository, char *update_dir, 130 List *entries)); 131 132 static Dtype 133 find_dirent_proc (callerdat, dir, repository, update_dir, entries) 134 void *callerdat; 135 char *dir; 136 char *repository; 137 char *update_dir; 138 List *entries; 139 { 140 struct find_data *find_data = (struct find_data *)callerdat; 141 142 /* initialize the ignore list for this directory */ 143 find_data->ignlist = getlist (); 144 return R_PROCESS; 145 } 146 147 /* Here as a static until we get around to fixing ignore_files to pass 148 it along as an argument. */ 149 static struct find_data *find_data_static; 150 151 static void find_ignproc PROTO ((char *, char *)); 152 153 static void 154 find_ignproc (file, dir) 155 char *file; 156 char *dir; 157 { 158 struct question *p; 159 160 p = (struct question *) xmalloc (sizeof (struct question)); 161 p->dir = xstrdup (dir); 162 p->repos = xstrdup (find_data_static->repository); 163 p->file = xstrdup (file); 164 p->next = find_data_static->questionables; 165 find_data_static->questionables = p; 166 } 167 168 static int find_filesdoneproc PROTO ((void *callerdat, int err, 169 char *repository, char *update_dir, 170 List *entries)); 171 172 static int 173 find_filesdoneproc (callerdat, err, repository, update_dir, entries) 174 void *callerdat; 175 int err; 176 char *repository; 177 char *update_dir; 178 List *entries; 179 { 180 struct find_data *find_data = (struct find_data *)callerdat; 181 find_data->repository = repository; 182 183 /* if this directory has an ignore list, process it then free it */ 184 if (find_data->ignlist) 185 { 186 find_data_static = find_data; 187 ignore_files (find_data->ignlist, entries, update_dir, find_ignproc); 188 dellist (&find_data->ignlist); 189 } 190 191 find_data->repository = NULL; 192 193 return err; 194 } 195 196 static int find_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 197 198 /* Machinery to find out what is modified, added, and removed. It is 199 possible this should be broken out into a new client_classify function; 200 merging it with classify_file is almost sure to be a mess, though, 201 because classify_file has all kinds of repository processing. */ 202 static int 203 find_fileproc (callerdat, finfo) 204 void *callerdat; 205 struct file_info *finfo; 206 { 207 Vers_TS *vers; 208 enum classify_type status; 209 Node *node; 210 struct find_data *args = (struct find_data *)callerdat; 211 struct logfile_info *data; 212 struct file_info xfinfo; 213 214 /* if this directory has an ignore list, add this file to it */ 215 if (args->ignlist) 216 { 217 Node *p; 218 219 p = getnode (); 220 p->type = FILES; 221 p->key = xstrdup (finfo->file); 222 if (addnode (args->ignlist, p) != 0) 223 freenode (p); 224 } 225 226 xfinfo = *finfo; 227 xfinfo.repository = NULL; 228 xfinfo.rcs = NULL; 229 230 vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0); 231 if (vers->ts_user == NULL 232 && vers->vn_user != NULL 233 && vers->vn_user[0] == '-') 234 /* FIXME: If vn_user is starts with "-" but ts_user is 235 non-NULL, what classify_file does is print "%s should be 236 removed and is still there". I'm not sure what it does 237 then. We probably should do the same. */ 238 status = T_REMOVED; 239 else if (vers->vn_user == NULL) 240 { 241 if (vers->ts_user == NULL) 242 error (0, 0, "nothing known about `%s'", finfo->fullname); 243 else 244 error (0, 0, "use `cvs add' to create an entry for %s", 245 finfo->fullname); 246 return 1; 247 } 248 else if (vers->ts_user != NULL 249 && vers->vn_user != NULL 250 && vers->vn_user[0] == '0') 251 /* FIXME: If vn_user is "0" but ts_user is NULL, what classify_file 252 does is print "new-born %s has disappeared" and removes the entry. 253 We probably should do the same. */ 254 status = T_ADDED; 255 else if (vers->ts_user != NULL 256 && vers->ts_rcs != NULL 257 && strcmp (vers->ts_user, vers->ts_rcs) != 0) 258 status = T_MODIFIED; 259 else 260 { 261 /* This covers unmodified files, as well as a variety of other 262 cases. FIXME: we probably should be printing a message and 263 returning 1 for many of those cases (but I'm not sure 264 exactly which ones). */ 265 return 0; 266 } 267 268 node = getnode (); 269 node->key = xstrdup (finfo->fullname); 270 271 data = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); 272 data->type = status; 273 data->tag = xstrdup (vers->tag); 274 275 node->type = UPDATE; 276 node->delproc = update_delproc; 277 node->data = (char *) data; 278 (void)addnode (args->ulist, node); 279 280 ++args->argc; 281 282 freevers_ts (&vers); 283 return 0; 284 } 285 286 static int copy_ulist PROTO ((Node *, void *)); 287 288 static int 289 copy_ulist (node, data) 290 Node *node; 291 void *data; 292 { 293 struct find_data *args = (struct find_data *)data; 294 args->argv[args->argc++] = node->key; 295 return 0; 296 } 297 #endif /* CLIENT_SUPPORT */ 298 299 int 300 commit (argc, argv) 301 int argc; 302 char **argv; 303 { 304 int c; 305 int err = 0; 306 int local = 0; 307 308 if (argc == -1) 309 usage (commit_usage); 310 311 #ifdef CVS_BADROOT 312 /* 313 * For log purposes, do not allow "root" to commit files. If you look 314 * like root, but are really logged in as a non-root user, it's OK. 315 */ 316 if (geteuid () == (uid_t) 0) 317 { 318 struct passwd *pw; 319 320 if ((pw = (struct passwd *) getpwnam (getcaller ())) == NULL) 321 error (1, 0, "you are unknown to this system"); 322 if (pw->pw_uid == (uid_t) 0) 323 error (1, 0, "cannot commit files as 'root'"); 324 } 325 #endif /* CVS_BADROOT */ 326 327 optind = 1; 328 while ((c = getopt (argc, argv, "nlRm:fF:r:")) != -1) 329 { 330 switch (c) 331 { 332 case 'n': 333 run_module_prog = 0; 334 break; 335 case 'm': 336 #ifdef FORCE_USE_EDITOR 337 use_editor = TRUE; 338 #else 339 use_editor = FALSE; 340 #endif 341 if (message) 342 { 343 free (message); 344 message = NULL; 345 } 346 347 message = xstrdup(optarg); 348 break; 349 case 'r': 350 if (tag) 351 free (tag); 352 tag = xstrdup (optarg); 353 break; 354 case 'l': 355 local = 1; 356 break; 357 case 'R': 358 local = 0; 359 break; 360 case 'f': 361 force_ci = 1; 362 local = 1; /* also disable recursion */ 363 break; 364 case 'F': 365 #ifdef FORCE_USE_EDITOR 366 use_editor = TRUE; 367 #else 368 use_editor = FALSE; 369 #endif 370 logfile = optarg; 371 break; 372 case '?': 373 default: 374 usage (commit_usage); 375 break; 376 } 377 } 378 argc -= optind; 379 argv += optind; 380 381 /* numeric specified revision means we ignore sticky tags... */ 382 if (tag && isdigit (*tag)) 383 { 384 aflag = 1; 385 /* strip trailing dots */ 386 while (tag[strlen (tag) - 1] == '.') 387 tag[strlen (tag) - 1] = '\0'; 388 } 389 390 /* some checks related to the "-F logfile" option */ 391 if (logfile) 392 { 393 int n, logfd; 394 struct stat statbuf; 395 396 if (message) 397 error (1, 0, "cannot specify both a message and a log file"); 398 399 /* FIXME: Why is this binary? Needs more investigation. */ 400 if ((logfd = CVS_OPEN (logfile, O_RDONLY | OPEN_BINARY)) < 0) 401 error (1, errno, "cannot open log file %s", logfile); 402 403 if (fstat(logfd, &statbuf) < 0) 404 error (1, errno, "cannot find size of log file %s", logfile); 405 406 message = xmalloc (statbuf.st_size + 1); 407 408 /* FIXME: Should keep reading until EOF, rather than assuming the 409 first read gets the whole thing. */ 410 if ((n = read (logfd, message, statbuf.st_size + 1)) < 0) 411 error (1, errno, "cannot read log message from %s", logfile); 412 413 (void) close (logfd); 414 message[n] = '\0'; 415 } 416 417 #ifdef CLIENT_SUPPORT 418 if (client_active) 419 { 420 struct find_data find_args; 421 422 ign_setup (); 423 424 find_args.ulist = getlist (); 425 find_args.argc = 0; 426 find_args.questionables = NULL; 427 find_args.ignlist = NULL; 428 find_args.repository = NULL; 429 430 err = start_recursion (find_fileproc, find_filesdoneproc, 431 find_dirent_proc, (DIRLEAVEPROC) NULL, 432 (void *)&find_args, 433 argc, argv, local, W_LOCAL, 0, 0, 434 (char *)NULL, 0); 435 if (err) 436 error (1, 0, "correct above errors first!"); 437 438 if (find_args.argc == 0) 439 /* Nothing to commit. Exit now without contacting the 440 server (note that this means that we won't print "? 441 foo" for files which merit it, because we don't know 442 what is in the CVSROOT/cvsignore file). */ 443 return 0; 444 445 /* Now we keep track of which files we actually are going to 446 operate on, and only work with those files in the future. 447 This saves time--we don't want to search the file system 448 of the working directory twice. */ 449 find_args.argv = (char **) xmalloc (find_args.argc * sizeof (char **)); 450 find_args.argc = 0; 451 walklist (find_args.ulist, copy_ulist, &find_args); 452 453 /* Do this before calling do_editor; don't ask for a log 454 message if we can't talk to the server. But do it after we 455 have made the checks that we can locally (to more quickly 456 catch syntax errors, the case where no files are modified, 457 added or removed, etc.). 458 459 On the other hand, calling start_server before do_editor 460 means that we chew up server resources the whole time that 461 the user has the editor open (hours or days if the user 462 forgets about it), which seems dubious. */ 463 start_server (); 464 465 /* 466 * We do this once, not once for each directory as in normal CVS. 467 * The protocol is designed this way. This is a feature. 468 */ 469 if (use_editor) 470 do_editor (".", &message, (char *)NULL, find_args.ulist); 471 472 /* We always send some sort of message, even if empty. */ 473 option_with_arg ("-m", message); 474 475 /* OK, now process all the questionable files we have been saving 476 up. */ 477 { 478 struct question *p; 479 struct question *q; 480 481 p = find_args.questionables; 482 while (p != NULL) 483 { 484 if (ign_inhibit_server || !supported_request ("Questionable")) 485 { 486 cvs_output ("? ", 2); 487 if (p->dir[0] != '\0') 488 { 489 cvs_output (p->dir, 0); 490 cvs_output ("/", 1); 491 } 492 cvs_output (p->file, 0); 493 cvs_output ("\n", 1); 494 } 495 else 496 { 497 send_to_server ("Directory ", 0); 498 send_to_server (p->dir[0] == '\0' ? "." : p->dir, 0); 499 send_to_server ("\012", 1); 500 send_to_server (p->repos, 0); 501 send_to_server ("\012", 1); 502 503 send_to_server ("Questionable ", 0); 504 send_to_server (p->file, 0); 505 send_to_server ("\012", 1); 506 } 507 free (p->dir); 508 free (p->repos); 509 free (p->file); 510 q = p->next; 511 free (p); 512 p = q; 513 } 514 } 515 516 if (local) 517 send_arg("-l"); 518 if (force_ci) 519 send_arg("-f"); 520 if (!run_module_prog) 521 send_arg("-n"); 522 option_with_arg ("-r", tag); 523 524 /* Sending only the names of the files which were modified, added, 525 or removed means that the server will only do an up-to-date 526 check on those files. This is different from local CVS and 527 previous versions of client/server CVS, but it probably is a Good 528 Thing, or at least Not Such A Bad Thing. */ 529 send_file_names (find_args.argc, find_args.argv, 0); 530 send_files (find_args.argc, find_args.argv, local, 0); 531 532 send_to_server ("ci\012", 0); 533 return get_responses_and_close (); 534 } 535 #endif 536 537 if (tag != NULL) 538 tag_check_valid (tag, argc, argv, local, aflag, ""); 539 540 /* XXX - this is not the perfect check for this */ 541 if (argc <= 0) 542 write_dirtag = tag; 543 544 wrap_setup (); 545 546 lock_tree_for_write (argc, argv, local, aflag); 547 548 /* 549 * Set up the master update list 550 */ 551 mulist = getlist (); 552 553 /* 554 * Run the recursion processor to verify the files are all up-to-date 555 */ 556 err = start_recursion (check_fileproc, check_filesdoneproc, 557 check_direntproc, (DIRLEAVEPROC) NULL, NULL, argc, 558 argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1); 559 if (err) 560 { 561 lock_tree_cleanup (); 562 error (1, 0, "correct above errors first!"); 563 } 564 565 /* 566 * Run the recursion processor to commit the files 567 */ 568 if (noexec == 0) 569 err = start_recursion (commit_fileproc, commit_filesdoneproc, 570 commit_direntproc, commit_dirleaveproc, NULL, 571 argc, argv, local, W_LOCAL, aflag, 0, 572 (char *) NULL, 1); 573 574 /* 575 * Unlock all the dirs and clean up 576 */ 577 lock_tree_cleanup (); 578 dellist (&mulist); 579 580 if (last_register_time) 581 { 582 time_t now; 583 584 (void) time (&now); 585 if (now == last_register_time) 586 { 587 sleep (1); /* to avoid time-stamp races */ 588 } 589 } 590 591 return (err); 592 } 593 594 /* 595 * Check to see if a file is ok to commit and make sure all files are 596 * up-to-date 597 */ 598 /* ARGSUSED */ 599 static int 600 check_fileproc (callerdat, finfo) 601 void *callerdat; 602 struct file_info *finfo; 603 { 604 Ctype status; 605 char *xdir; 606 Node *p; 607 List *ulist, *cilist; 608 Vers_TS *vers; 609 struct commit_info *ci; 610 struct logfile_info *li; 611 int save_noexec, save_quiet, save_really_quiet; 612 613 save_noexec = noexec; 614 save_quiet = quiet; 615 save_really_quiet = really_quiet; 616 noexec = quiet = really_quiet = 1; 617 618 /* handle specified numeric revision specially */ 619 if (tag && isdigit (*tag)) 620 { 621 /* If the tag is for the trunk, make sure we're at the head */ 622 if (numdots (tag) < 2) 623 { 624 status = Classify_File (finfo, (char *) NULL, (char *) NULL, 625 (char *) NULL, 1, aflag, &vers, 0); 626 if (status == T_UPTODATE || status == T_MODIFIED || 627 status == T_ADDED) 628 { 629 Ctype xstatus; 630 631 freevers_ts (&vers); 632 xstatus = Classify_File (finfo, tag, (char *) NULL, 633 (char *) NULL, 1, aflag, &vers, 0); 634 if (xstatus == T_REMOVE_ENTRY) 635 status = T_MODIFIED; 636 else if (status == T_MODIFIED && xstatus == T_CONFLICT) 637 status = T_MODIFIED; 638 else 639 status = xstatus; 640 } 641 } 642 else 643 { 644 char *xtag, *cp; 645 646 /* 647 * The revision is off the main trunk; make sure we're 648 * up-to-date with the head of the specified branch. 649 */ 650 xtag = xstrdup (tag); 651 if ((numdots (xtag) & 1) != 0) 652 { 653 cp = strrchr (xtag, '.'); 654 *cp = '\0'; 655 } 656 status = Classify_File (finfo, xtag, (char *) NULL, 657 (char *) NULL, 1, aflag, &vers, 0); 658 if ((status == T_REMOVE_ENTRY || status == T_CONFLICT) 659 && (cp = strrchr (xtag, '.')) != NULL) 660 { 661 /* pluck one more dot off the revision */ 662 *cp = '\0'; 663 freevers_ts (&vers); 664 status = Classify_File (finfo, xtag, (char *) NULL, 665 (char *) NULL, 1, aflag, &vers, 0); 666 if (status == T_UPTODATE || status == T_REMOVE_ENTRY) 667 status = T_MODIFIED; 668 } 669 /* now, muck with vers to make the tag correct */ 670 free (vers->tag); 671 vers->tag = xstrdup (tag); 672 free (xtag); 673 } 674 } 675 else 676 status = Classify_File (finfo, tag, (char *) NULL, (char *) NULL, 677 1, 0, &vers, 0); 678 noexec = save_noexec; 679 quiet = save_quiet; 680 really_quiet = save_really_quiet; 681 682 /* 683 * If the force-commit option is enabled, and the file in question 684 * appears to be up-to-date, just make it look modified so that 685 * it will be committed. 686 */ 687 if (force_ci && status == T_UPTODATE) 688 status = T_MODIFIED; 689 690 switch (status) 691 { 692 case T_CHECKOUT: 693 #ifdef SERVER_SUPPORT 694 case T_PATCH: 695 #endif 696 case T_NEEDS_MERGE: 697 case T_CONFLICT: 698 case T_REMOVE_ENTRY: 699 error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname); 700 freevers_ts (&vers); 701 return (1); 702 case T_MODIFIED: 703 case T_ADDED: 704 case T_REMOVED: 705 /* 706 * some quick sanity checks; if no numeric -r option specified: 707 * - can't have a sticky date 708 * - can't have a sticky tag that is not a branch 709 * Also, 710 * - if status is T_REMOVED, can't have a numeric tag 711 * - if status is T_ADDED, rcs file must not exist unless on 712 * a branch 713 * - if status is T_ADDED, can't have a non-trunk numeric rev 714 * - if status is T_MODIFIED and a Conflict marker exists, don't 715 * allow the commit if timestamp is identical or if we find 716 * an RCS_MERGE_PAT in the file. 717 */ 718 if (!tag || !isdigit (*tag)) 719 { 720 if (vers->date) 721 { 722 error (0, 0, 723 "cannot commit with sticky date for file `%s'", 724 finfo->fullname); 725 freevers_ts (&vers); 726 return (1); 727 } 728 if (status == T_MODIFIED && vers->tag && 729 !RCS_isbranch (finfo->rcs, vers->tag)) 730 { 731 error (0, 0, 732 "sticky tag `%s' for file `%s' is not a branch", 733 vers->tag, finfo->fullname); 734 freevers_ts (&vers); 735 return (1); 736 } 737 } 738 if (status == T_MODIFIED && !force_ci && vers->ts_conflict) 739 { 740 char *filestamp; 741 int retcode; 742 743 /* 744 * We found a "conflict" marker. 745 * 746 * If the timestamp on the file is the same as the 747 * timestamp stored in the Entries file, we block the commit. 748 */ 749 #ifdef SERVER_SUPPORT 750 if (server_active) 751 retcode = vers->ts_conflict[0] != '='; 752 else { 753 filestamp = time_stamp (finfo->file); 754 retcode = strcmp (vers->ts_conflict, filestamp); 755 free (filestamp); 756 } 757 #else 758 filestamp = time_stamp (finfo->file); 759 retcode = strcmp (vers->ts_conflict, filestamp); 760 free (filestamp); 761 #endif 762 if (retcode == 0) 763 { 764 error (0, 0, 765 "file `%s' had a conflict and has not been modified", 766 finfo->fullname); 767 freevers_ts (&vers); 768 return (1); 769 } 770 771 /* 772 * If the timestamps differ, look for Conflict indicators 773 * in the file to see if we should block the commit anyway 774 */ 775 run_setup ("%s", GREP); 776 run_arg (RCS_MERGE_PAT); 777 run_arg (finfo->file); 778 retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY); 779 780 if (retcode == -1) 781 { 782 error (1, errno, 783 "fork failed while examining conflict in `%s'", 784 finfo->fullname); 785 } 786 else if (retcode == 0) 787 { 788 error (0, 0, 789 "file `%s' still contains conflict indicators", 790 finfo->fullname); 791 freevers_ts (&vers); 792 return (1); 793 } 794 } 795 796 if (status == T_REMOVED && vers->tag && isdigit (*vers->tag)) 797 { 798 error (0, 0, 799 "cannot remove file `%s' which has a numeric sticky tag of `%s'", 800 finfo->fullname, vers->tag); 801 freevers_ts (&vers); 802 return (1); 803 } 804 if (status == T_ADDED) 805 { 806 if (vers->tag == NULL) 807 { 808 char rcs[PATH_MAX]; 809 810 /* Don't look in the attic; if it exists there we 811 will move it back out in checkaddfile. */ 812 sprintf(rcs, "%s/%s%s", finfo->repository, finfo->file, 813 RCSEXT); 814 if (isreadable (rcs)) 815 { 816 error (0, 0, 817 "cannot add file `%s' when RCS file `%s' already exists", 818 finfo->fullname, rcs); 819 freevers_ts (&vers); 820 return (1); 821 } 822 } 823 if (vers->tag && isdigit (*vers->tag) && 824 numdots (vers->tag) > 1) 825 { 826 error (0, 0, 827 "cannot add file `%s' with revision `%s'; must be on trunk", 828 finfo->fullname, vers->tag); 829 freevers_ts (&vers); 830 return (1); 831 } 832 } 833 834 /* done with consistency checks; now, to get on with the commit */ 835 if (finfo->update_dir[0] == '\0') 836 xdir = "."; 837 else 838 xdir = finfo->update_dir; 839 if ((p = findnode (mulist, xdir)) != NULL) 840 { 841 ulist = ((struct master_lists *) p->data)->ulist; 842 cilist = ((struct master_lists *) p->data)->cilist; 843 } 844 else 845 { 846 struct master_lists *ml; 847 848 ulist = getlist (); 849 cilist = getlist (); 850 p = getnode (); 851 p->key = xstrdup (xdir); 852 p->type = UPDATE; 853 ml = (struct master_lists *) 854 xmalloc (sizeof (struct master_lists)); 855 ml->ulist = ulist; 856 ml->cilist = cilist; 857 p->data = (char *) ml; 858 p->delproc = masterlist_delproc; 859 (void) addnode (mulist, p); 860 } 861 862 /* first do ulist, then cilist */ 863 p = getnode (); 864 p->key = xstrdup (finfo->file); 865 p->type = UPDATE; 866 p->delproc = update_delproc; 867 li = ((struct logfile_info *) 868 xmalloc (sizeof (struct logfile_info))); 869 li->type = status; 870 li->tag = xstrdup (vers->tag); 871 p->data = (char *) li; 872 (void) addnode (ulist, p); 873 874 p = getnode (); 875 p->key = xstrdup (finfo->file); 876 p->type = UPDATE; 877 p->delproc = ci_delproc; 878 ci = (struct commit_info *) xmalloc (sizeof (struct commit_info)); 879 ci->status = status; 880 if (vers->tag) 881 if (isdigit (*vers->tag)) 882 ci->rev = xstrdup (vers->tag); 883 else 884 ci->rev = RCS_whatbranch (finfo->rcs, vers->tag); 885 else 886 ci->rev = (char *) NULL; 887 ci->tag = xstrdup (vers->tag); 888 ci->options = xstrdup(vers->options); 889 p->data = (char *) ci; 890 (void) addnode (cilist, p); 891 break; 892 case T_UNKNOWN: 893 error (0, 0, "nothing known about `%s'", finfo->fullname); 894 freevers_ts (&vers); 895 return (1); 896 case T_UPTODATE: 897 break; 898 default: 899 error (0, 0, "CVS internal error: unknown status %d", status); 900 break; 901 } 902 903 freevers_ts (&vers); 904 return (0); 905 } 906 907 /* 908 * Print warm fuzzies while examining the dirs 909 */ 910 /* ARGSUSED */ 911 static Dtype 912 check_direntproc (callerdat, dir, repos, update_dir, entries) 913 void *callerdat; 914 char *dir; 915 char *repos; 916 char *update_dir; 917 List *entries; 918 { 919 if (!quiet) 920 error (0, 0, "Examining %s", update_dir); 921 922 return (R_PROCESS); 923 } 924 925 /* 926 * Walklist proc to run pre-commit checks 927 */ 928 static int 929 precommit_list_proc (p, closure) 930 Node *p; 931 void *closure; 932 { 933 struct logfile_info *li; 934 935 li = (struct logfile_info *) p->data; 936 if (li->type == T_ADDED 937 || li->type == T_MODIFIED 938 || li->type == T_REMOVED) 939 { 940 run_arg (p->key); 941 } 942 return (0); 943 } 944 945 /* 946 * Callback proc for pre-commit checking 947 */ 948 static List *ulist; 949 static int 950 precommit_proc (repository, filter) 951 char *repository; 952 char *filter; 953 { 954 /* see if the filter is there, only if it's a full path */ 955 if (isabsolute (filter)) 956 { 957 char *s, *cp; 958 959 s = xstrdup (filter); 960 for (cp = s; *cp; cp++) 961 if (isspace (*cp)) 962 { 963 *cp = '\0'; 964 break; 965 } 966 if (!isfile (s)) 967 { 968 error (0, errno, "cannot find pre-commit filter `%s'", s); 969 free (s); 970 return (1); /* so it fails! */ 971 } 972 free (s); 973 } 974 975 run_setup ("%s %s", filter, repository); 976 (void) walklist (ulist, precommit_list_proc, NULL); 977 return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY)); 978 } 979 980 /* 981 * Run the pre-commit checks for the dir 982 */ 983 /* ARGSUSED */ 984 static int 985 check_filesdoneproc (callerdat, err, repos, update_dir, entries) 986 void *callerdat; 987 int err; 988 char *repos; 989 char *update_dir; 990 List *entries; 991 { 992 int n; 993 Node *p; 994 995 /* find the update list for this dir */ 996 p = findnode (mulist, update_dir); 997 if (p != NULL) 998 ulist = ((struct master_lists *) p->data)->ulist; 999 else 1000 ulist = (List *) NULL; 1001 1002 /* skip the checks if there's nothing to do */ 1003 if (ulist == NULL || ulist->list->next == ulist->list) 1004 return (err); 1005 1006 /* run any pre-commit checks */ 1007 if ((n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, 1)) > 0) 1008 { 1009 error (0, 0, "Pre-commit check failed"); 1010 err += n; 1011 } 1012 1013 return (err); 1014 } 1015 1016 /* 1017 * Do the work of committing a file 1018 */ 1019 static int maxrev; 1020 static char sbranch[PATH_MAX]; 1021 1022 /* ARGSUSED */ 1023 static int 1024 commit_fileproc (callerdat, finfo) 1025 void *callerdat; 1026 struct file_info *finfo; 1027 { 1028 Node *p; 1029 int err = 0; 1030 List *ulist, *cilist; 1031 struct commit_info *ci; 1032 1033 if (finfo->update_dir[0] == '\0') 1034 p = findnode (mulist, "."); 1035 else 1036 p = findnode (mulist, finfo->update_dir); 1037 1038 /* 1039 * if p is null, there were file type command line args which were 1040 * all up-to-date so nothing really needs to be done 1041 */ 1042 if (p == NULL) 1043 return (0); 1044 ulist = ((struct master_lists *) p->data)->ulist; 1045 cilist = ((struct master_lists *) p->data)->cilist; 1046 1047 /* 1048 * At this point, we should have the commit message unless we were called 1049 * with files as args from the command line. In that latter case, we 1050 * need to get the commit message ourselves 1051 */ 1052 if (use_editor && !got_message) 1053 { 1054 got_message = 1; 1055 do_editor (finfo->update_dir, &message, finfo->repository, ulist); 1056 } 1057 1058 p = findnode (cilist, finfo->file); 1059 if (p == NULL) 1060 return (0); 1061 1062 ci = (struct commit_info *) p->data; 1063 if (ci->status == T_MODIFIED) 1064 { 1065 if (finfo->rcs == NULL) 1066 error (1, 0, "internal error: no parsed RCS file"); 1067 if (lock_RCS (finfo->file, finfo->rcs, ci->rev, 1068 finfo->repository) != 0) 1069 { 1070 unlockrcs (finfo->rcs); 1071 err = 1; 1072 goto out; 1073 } 1074 } 1075 else if (ci->status == T_ADDED) 1076 { 1077 if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options, 1078 &finfo->rcs) != 0) 1079 { 1080 fixaddfile (finfo->file, finfo->repository); 1081 err = 1; 1082 goto out; 1083 } 1084 1085 /* adding files with a tag, now means adding them on a branch. 1086 Since the branch test was done in check_fileproc for 1087 modified files, we need to stub it in again here. */ 1088 1089 if (ci->tag) 1090 { 1091 if (finfo->rcs == NULL) 1092 error (1, 0, "internal error: no parsed RCS file"); 1093 ci->rev = RCS_whatbranch (finfo->rcs, ci->tag); 1094 err = Checkin ('A', finfo, finfo->rcs->path, ci->rev, 1095 ci->tag, ci->options, message); 1096 if (err != 0) 1097 { 1098 unlockrcs (finfo->rcs); 1099 fixbranch (finfo->rcs, sbranch); 1100 } 1101 1102 (void) time (&last_register_time); 1103 1104 ci->status = T_UPTODATE; 1105 } 1106 } 1107 1108 /* 1109 * Add the file for real 1110 */ 1111 if (ci->status == T_ADDED) 1112 { 1113 char *xrev = (char *) NULL; 1114 1115 if (ci->rev == NULL) 1116 { 1117 /* find the max major rev number in this directory */ 1118 maxrev = 0; 1119 (void) walklist (finfo->entries, findmaxrev, NULL); 1120 if (maxrev == 0) 1121 maxrev = 1; 1122 xrev = xmalloc (20); 1123 (void) sprintf (xrev, "%d", maxrev); 1124 } 1125 1126 /* XXX - an added file with symbolic -r should add tag as well */ 1127 err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options); 1128 if (xrev) 1129 free (xrev); 1130 } 1131 else if (ci->status == T_MODIFIED) 1132 { 1133 err = Checkin ('M', finfo, 1134 finfo->rcs->path, ci->rev, ci->tag, 1135 ci->options, message); 1136 1137 (void) time (&last_register_time); 1138 1139 if (err != 0) 1140 { 1141 unlockrcs (finfo->rcs); 1142 fixbranch (finfo->rcs, sbranch); 1143 } 1144 } 1145 else if (ci->status == T_REMOVED) 1146 { 1147 err = remove_file (finfo, ci->tag, message); 1148 #ifdef SERVER_SUPPORT 1149 if (server_active) { 1150 server_scratch_entry_only (); 1151 server_updated (finfo, 1152 NULL, 1153 1154 /* Doesn't matter, it won't get checked. */ 1155 SERVER_UPDATED, 1156 1157 (struct stat *) NULL, 1158 (unsigned char *) NULL); 1159 } 1160 #endif 1161 } 1162 1163 /* Clearly this is right for T_MODIFIED. I haven't thought so much 1164 about T_ADDED or T_REMOVED. */ 1165 notify_do ('C', finfo->file, getcaller (), NULL, NULL, finfo->repository); 1166 1167 out: 1168 if (err != 0) 1169 { 1170 /* on failure, remove the file from ulist */ 1171 p = findnode (ulist, finfo->file); 1172 if (p) 1173 delnode (p); 1174 } 1175 1176 return (err); 1177 } 1178 1179 /* 1180 * Log the commit and clean up the update list 1181 */ 1182 /* ARGSUSED */ 1183 static int 1184 commit_filesdoneproc (callerdat, err, repository, update_dir, entries) 1185 void *callerdat; 1186 int err; 1187 char *repository; 1188 char *update_dir; 1189 List *entries; 1190 { 1191 Node *p; 1192 List *ulist; 1193 1194 p = findnode (mulist, update_dir); 1195 if (p == NULL) 1196 return (err); 1197 1198 ulist = ((struct master_lists *) p->data)->ulist; 1199 1200 got_message = 0; 1201 1202 1203 Update_Logfile (repository, message, (FILE *) 0, ulist); 1204 1205 /* Build the administrative files if necessary. */ 1206 { 1207 char *p; 1208 1209 if (strncmp (CVSroot_directory, repository, 1210 strlen (CVSroot_directory)) != 0) 1211 error (0, 0, "internal error: repository (%s) doesn't begin with root (%s)", repository, CVSroot_directory); 1212 p = repository + strlen (CVSroot_directory); 1213 if (*p == '/') 1214 ++p; 1215 if (strcmp ("CVSROOT", p) == 0) 1216 { 1217 /* "Database" might a little bit grandiose and/or vague, 1218 but "checked-out copies of administrative files, unless 1219 in the case of modules and you are using ndbm in which 1220 case modules.{pag,dir,db}" is verbose and excessively 1221 focused on how the database is implemented. */ 1222 1223 cvs_output (program_name, 0); 1224 cvs_output (" ", 1); 1225 cvs_output (command_name, 0); 1226 cvs_output (": Rebuilding administrative file database\n", 0); 1227 mkmodules (repository); 1228 } 1229 } 1230 1231 if (err == 0 && run_module_prog) 1232 { 1233 FILE *fp; 1234 1235 if ((fp = CVS_FOPEN (CVSADM_CIPROG, "r")) != NULL) 1236 { 1237 char *line; 1238 int line_length; 1239 size_t line_chars_allocated; 1240 char *repository; 1241 1242 line = NULL; 1243 line_chars_allocated = 0; 1244 line_length = getline (&line, &line_chars_allocated, fp); 1245 if (line_length > 0) 1246 { 1247 /* Remove any trailing newline. */ 1248 if (line[line_length - 1] == '\n') 1249 line[--line_length] = '\0'; 1250 repository = Name_Repository ((char *) NULL, update_dir); 1251 run_setup ("%s %s", line, repository); 1252 (void) printf ("%s %s: Executing '", program_name, 1253 command_name); 1254 run_print (stdout); 1255 (void) printf ("'\n"); 1256 (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); 1257 free (repository); 1258 } 1259 else 1260 { 1261 if (ferror (fp)) 1262 error (0, errno, "warning: error reading %s", 1263 CVSADM_CIPROG); 1264 } 1265 if (line != NULL) 1266 free (line); 1267 if (fclose (fp) < 0) 1268 error (0, errno, "warning: cannot close %s", CVSADM_CIPROG); 1269 } 1270 else 1271 { 1272 if (! existence_error (errno)) 1273 error (0, errno, "warning: cannot open %s", CVSADM_CIPROG); 1274 } 1275 } 1276 1277 return (err); 1278 } 1279 1280 /* 1281 * Get the log message for a dir and print a warm fuzzy 1282 */ 1283 /* ARGSUSED */ 1284 static Dtype 1285 commit_direntproc (callerdat, dir, repos, update_dir, entries) 1286 void *callerdat; 1287 char *dir; 1288 char *repos; 1289 char *update_dir; 1290 List *entries; 1291 { 1292 Node *p; 1293 List *ulist; 1294 char *real_repos; 1295 1296 /* find the update list for this dir */ 1297 p = findnode (mulist, update_dir); 1298 if (p != NULL) 1299 ulist = ((struct master_lists *) p->data)->ulist; 1300 else 1301 ulist = (List *) NULL; 1302 1303 /* skip the files as an optimization */ 1304 if (ulist == NULL || ulist->list->next == ulist->list) 1305 return (R_SKIP_FILES); 1306 1307 /* print the warm fuzzy */ 1308 if (!quiet) 1309 error (0, 0, "Committing %s", update_dir); 1310 1311 /* get commit message */ 1312 if (use_editor) 1313 { 1314 got_message = 1; 1315 real_repos = Name_Repository (dir, update_dir); 1316 do_editor (update_dir, &message, real_repos, ulist); 1317 free (real_repos); 1318 } 1319 return (R_PROCESS); 1320 } 1321 1322 /* 1323 * Process the post-commit proc if necessary 1324 */ 1325 /* ARGSUSED */ 1326 static int 1327 commit_dirleaveproc (callerdat, dir, err, update_dir, entries) 1328 void *callerdat; 1329 char *dir; 1330 int err; 1331 char *update_dir; 1332 List *entries; 1333 { 1334 /* update the per-directory tag info */ 1335 if (err == 0 && write_dirtag != NULL) 1336 { 1337 WriteTag ((char *) NULL, write_dirtag, (char *) NULL); 1338 #ifdef SERVER_SUPPORT 1339 if (server_active) 1340 server_set_sticky (update_dir, Name_Repository (dir, update_dir), 1341 write_dirtag, (char *) NULL); 1342 #endif 1343 } 1344 1345 return (err); 1346 } 1347 1348 /* 1349 * find the maximum major rev number in an entries file 1350 */ 1351 static int 1352 findmaxrev (p, closure) 1353 Node *p; 1354 void *closure; 1355 { 1356 char *cp; 1357 int thisrev; 1358 Entnode *entdata; 1359 1360 entdata = (Entnode *) p->data; 1361 if (entdata->type != ENT_FILE) 1362 return (0); 1363 cp = strchr (entdata->version, '.'); 1364 if (cp != NULL) 1365 *cp = '\0'; 1366 thisrev = atoi (entdata->version); 1367 if (cp != NULL) 1368 *cp = '.'; 1369 if (thisrev > maxrev) 1370 maxrev = thisrev; 1371 return (0); 1372 } 1373 1374 /* 1375 * Actually remove a file by moving it to the attic 1376 * XXX - if removing a ,v file that is a relative symbolic link to 1377 * another ,v file, we probably should add a ".." component to the 1378 * link to keep it relative after we move it into the attic. 1379 */ 1380 static int 1381 remove_file (finfo, tag, message) 1382 struct file_info *finfo; 1383 char *tag; 1384 char *message; 1385 { 1386 mode_t omask; 1387 int retcode; 1388 char *tmp; 1389 1390 int branch; 1391 int lockflag; 1392 char *corev; 1393 char *rev; 1394 char *prev_rev; 1395 char *old_path; 1396 1397 corev = NULL; 1398 rev = NULL; 1399 prev_rev = NULL; 1400 1401 retcode = 0; 1402 1403 if (finfo->rcs == NULL) 1404 error (1, 0, "internal error: no parsed RCS file"); 1405 1406 branch = 0; 1407 if (tag && !(branch = RCS_isbranch (finfo->rcs, tag))) 1408 { 1409 /* a symbolic tag is specified; just remove the tag from the file */ 1410 if ((retcode = RCS_deltag (finfo->rcs, tag, 1)) != 0) 1411 { 1412 if (!quiet) 1413 error (0, retcode == -1 ? errno : 0, 1414 "failed to remove tag `%s' from `%s'", tag, 1415 finfo->fullname); 1416 return (1); 1417 } 1418 Scratch_Entry (finfo->entries, finfo->file); 1419 return (0); 1420 } 1421 1422 /* we are removing the file from either the head or a branch */ 1423 /* commit a new, dead revision. */ 1424 1425 /* Print message indicating that file is going to be removed. */ 1426 (void) printf ("Removing %s;\n", finfo->fullname); 1427 1428 rev = NULL; 1429 lockflag = 1; 1430 if (branch) 1431 { 1432 char *branchname; 1433 1434 rev = RCS_whatbranch (finfo->rcs, tag); 1435 if (rev == NULL) 1436 { 1437 error (0, 0, "cannot find branch \"%s\".", tag); 1438 return (1); 1439 } 1440 1441 branchname = RCS_getbranch (finfo->rcs, rev, 1); 1442 if (branchname == NULL) 1443 { 1444 /* no revision exists on this branch. use the previous 1445 revision but do not lock. */ 1446 corev = RCS_gettag (finfo->rcs, tag, 1, (int *) NULL); 1447 prev_rev = xstrdup(rev); 1448 lockflag = 0; 1449 } else 1450 { 1451 corev = xstrdup (rev); 1452 prev_rev = xstrdup(branchname); 1453 free (branchname); 1454 } 1455 1456 } else /* Not a branch */ 1457 { 1458 /* Get current head revision of file. */ 1459 prev_rev = RCS_head (finfo->rcs); 1460 } 1461 1462 /* if removing without a tag or a branch, then make sure the default 1463 branch is the trunk. */ 1464 if (!tag && !branch) 1465 { 1466 if (RCS_setbranch (finfo->rcs, NULL) != 0) 1467 { 1468 error (0, 0, "cannot change branch to default for %s", 1469 finfo->fullname); 1470 return (1); 1471 } 1472 } 1473 1474 #ifdef SERVER_SUPPORT 1475 if (server_active) { 1476 /* If this is the server, there will be a file sitting in the 1477 temp directory which is the kludgy way in which server.c 1478 tells time_stamp that the file is no longer around. Remove 1479 it so we can create temp files with that name (ignore errors). */ 1480 unlink_file (finfo->file); 1481 } 1482 #endif 1483 1484 /* check something out. Generally this is the head. If we have a 1485 particular rev, then name it. */ 1486 retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL, 1487 (char *) NULL, (char *) NULL, RUN_TTY); 1488 if (retcode != 0) 1489 { 1490 if (!quiet) 1491 error (0, retcode == -1 ? errno : 0, 1492 "failed to check out `%s'", finfo->fullname); 1493 return (1); 1494 } 1495 1496 /* Except when we are creating a branch, lock the revision so that 1497 we can check in the new revision. */ 1498 if (lockflag) 1499 RCS_lock (finfo->rcs, rev ? corev : NULL, 0); 1500 1501 if (corev != NULL) 1502 free (corev); 1503 1504 retcode = RCS_checkin (finfo->rcs->path, finfo->file, message, rev, 1505 RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); 1506 if (retcode != 0) 1507 { 1508 if (!quiet) 1509 error (0, retcode == -1 ? errno : 0, 1510 "failed to commit dead revision for `%s'", finfo->fullname); 1511 return (1); 1512 } 1513 1514 if (rev != NULL) 1515 free (rev); 1516 1517 old_path = finfo->rcs->path; 1518 if (!branch) 1519 { 1520 /* this was the head; really move it into the Attic */ 1521 tmp = xmalloc(strlen(finfo->repository) + 1522 sizeof('/') + 1523 sizeof(CVSATTIC) + 1524 sizeof('/') + 1525 strlen(finfo->file) + 1526 sizeof(RCSEXT) + 1); 1527 (void) sprintf (tmp, "%s/%s", finfo->repository, CVSATTIC); 1528 omask = umask (cvsumask); 1529 (void) CVS_MKDIR (tmp, 0777); 1530 (void) umask (omask); 1531 (void) sprintf (tmp, "%s/%s/%s%s", finfo->repository, CVSATTIC, finfo->file, RCSEXT); 1532 1533 if (strcmp (finfo->rcs->path, tmp) != 0 1534 && CVS_RENAME (finfo->rcs->path, tmp) == -1 1535 && (isreadable (finfo->rcs->path) || !isreadable (tmp))) 1536 { 1537 free(tmp); 1538 return (1); 1539 } 1540 /* The old value of finfo->rcs->path is in old_path, and is 1541 freed below. */ 1542 finfo->rcs->path = tmp; 1543 } 1544 1545 /* Print message that file was removed. */ 1546 (void) printf ("%s <-- %s\n", old_path, finfo->file); 1547 (void) printf ("new revision: delete; "); 1548 (void) printf ("previous revision: %s\n", prev_rev); 1549 (void) printf ("done\n"); 1550 free(prev_rev); 1551 1552 if (old_path != finfo->rcs->path) 1553 free (old_path); 1554 1555 Scratch_Entry (finfo->entries, finfo->file); 1556 return (0); 1557 } 1558 1559 /* 1560 * Do the actual checkin for added files 1561 */ 1562 static int 1563 finaladd (finfo, rev, tag, options) 1564 struct file_info *finfo; 1565 char *rev; 1566 char *tag; 1567 char *options; 1568 { 1569 int ret; 1570 char tmp[PATH_MAX]; 1571 char rcs[PATH_MAX]; 1572 1573 locate_rcs (finfo->file, finfo->repository, rcs); 1574 ret = Checkin ('A', finfo, rcs, rev, tag, options, message); 1575 if (ret == 0) 1576 { 1577 (void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); 1578 (void) unlink_file (tmp); 1579 } 1580 else 1581 fixaddfile (finfo->file, finfo->repository); 1582 1583 (void) time (&last_register_time); 1584 1585 return (ret); 1586 } 1587 1588 /* 1589 * Unlock an rcs file 1590 */ 1591 static void 1592 unlockrcs (rcs) 1593 RCSNode *rcs; 1594 { 1595 int retcode; 1596 1597 if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0) 1598 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 1599 "could not unlock %s", rcs->path); 1600 } 1601 1602 /* 1603 * remove a partially added file. if we can parse it, leave it alone. 1604 */ 1605 static void 1606 fixaddfile (file, repository) 1607 char *file; 1608 char *repository; 1609 { 1610 RCSNode *rcsfile; 1611 char rcs[PATH_MAX]; 1612 int save_really_quiet; 1613 1614 locate_rcs (file, repository, rcs); 1615 save_really_quiet = really_quiet; 1616 really_quiet = 1; 1617 if ((rcsfile = RCS_parsercsfile (rcs)) == NULL) 1618 (void) unlink_file (rcs); 1619 else 1620 freercsnode (&rcsfile); 1621 really_quiet = save_really_quiet; 1622 } 1623 1624 /* 1625 * put the branch back on an rcs file 1626 */ 1627 static void 1628 fixbranch (rcs, branch) 1629 RCSNode *rcs; 1630 char *branch; 1631 { 1632 int retcode; 1633 1634 if (branch != NULL && branch[0] != '\0') 1635 { 1636 if ((retcode = RCS_setbranch (rcs, branch)) != 0) 1637 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 1638 "cannot restore branch to %s for %s", branch, rcs->path); 1639 } 1640 } 1641 1642 /* 1643 * do the initial part of a file add for the named file. if adding 1644 * with a tag, put the file in the Attic and point the symbolic tag 1645 * at the committed revision. 1646 */ 1647 1648 static int 1649 checkaddfile (file, repository, tag, options, rcsnode) 1650 char *file; 1651 char *repository; 1652 char *tag; 1653 char *options; 1654 RCSNode **rcsnode; 1655 { 1656 char rcs[PATH_MAX]; 1657 char fname[PATH_MAX]; 1658 mode_t omask; 1659 int retcode = 0; 1660 int newfile = 0; 1661 RCSNode *rcsfile = NULL; 1662 1663 if (tag) 1664 { 1665 (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); 1666 if (! isreadable (rcs)) 1667 { 1668 (void) sprintf(rcs, "%s/%s", repository, CVSATTIC); 1669 omask = umask (cvsumask); 1670 if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST) 1671 error (1, errno, "cannot make directory `%s'", rcs);; 1672 (void) umask (omask); 1673 (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, 1674 RCSEXT); 1675 } 1676 } 1677 else 1678 locate_rcs (file, repository, rcs); 1679 1680 if (isreadable(rcs)) 1681 { 1682 /* file has existed in the past. Prepare to resurrect. */ 1683 char oldfile[PATH_MAX]; 1684 char *rev; 1685 1686 if ((rcsfile = *rcsnode) == NULL) 1687 { 1688 error (0, 0, "could not find parsed rcsfile %s", file); 1689 return (1); 1690 } 1691 1692 if (tag == NULL) 1693 { 1694 /* we are adding on the trunk, so move the file out of the 1695 Attic. */ 1696 strcpy (oldfile, rcs); 1697 sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); 1698 1699 if (strcmp (oldfile, rcs) == 0 1700 || CVS_RENAME (oldfile, rcs) != 0 1701 || isreadable (oldfile) 1702 || !isreadable (rcs)) 1703 { 1704 error (0, 0, "failed to move `%s' out of the attic.", 1705 file); 1706 return (1); 1707 } 1708 free (rcsfile->path); 1709 rcsfile->path = xstrdup (rcs); 1710 } 1711 1712 rev = RCS_getversion (rcsfile, tag, NULL, 1, (int *) NULL); 1713 /* and lock it */ 1714 if (lock_RCS (file, rcsfile, rev, repository)) { 1715 error (0, 0, "cannot lock `%s'.", rcs); 1716 free (rev); 1717 return (1); 1718 } 1719 1720 free (rev); 1721 } else { 1722 /* this is the first time we have ever seen this file; create 1723 an rcs file. */ 1724 run_setup ("%s%s -x,v/ -i", Rcsbin, RCS); 1725 1726 (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); 1727 /* If the file does not exist, no big deal. In particular, the 1728 server does not (yet at least) create CVSEXT_LOG files. */ 1729 if (isfile (fname)) 1730 run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG); 1731 1732 /* Set RCS keyword expansion options. */ 1733 if (options && options[0] == '-' && options[1] == 'k') 1734 run_arg (options); 1735 run_arg (rcs); 1736 if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0) 1737 { 1738 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 1739 "could not create %s", rcs); 1740 return (1); 1741 } 1742 newfile = 1; 1743 } 1744 1745 /* when adding a file for the first time, and using a tag, we need 1746 to create a dead revision on the trunk. */ 1747 if (tag && newfile) 1748 { 1749 char *tmp; 1750 1751 /* move the new file out of the way. */ 1752 (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); 1753 rename_file (file, fname); 1754 copy_file (DEVNULL, file); 1755 1756 tmp = xmalloc (strlen (file) + strlen (tag) + 80); 1757 /* commit a dead revision. */ 1758 (void) sprintf (tmp, "file %s was initially added on branch %s.", 1759 file, tag); 1760 retcode = RCS_checkin (rcs, NULL, tmp, NULL, 1761 RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); 1762 free (tmp); 1763 if (retcode != 0) 1764 { 1765 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 1766 "could not create initial dead revision %s", rcs); 1767 return (1); 1768 } 1769 1770 /* put the new file back where it was */ 1771 rename_file (fname, file); 1772 1773 assert (rcsfile == NULL); 1774 rcsfile = RCS_parse (file, repository); 1775 if (rcsfile == NULL) 1776 { 1777 error (0, 0, "could not read %s", rcs); 1778 return (1); 1779 } 1780 if (rcsnode != NULL) 1781 { 1782 assert (*rcsnode == NULL); 1783 *rcsnode = rcsfile; 1784 } 1785 1786 /* and lock it once again. */ 1787 if (lock_RCS (file, rcsfile, NULL, repository)) { 1788 error (0, 0, "cannot lock `%s'.", rcs); 1789 return (1); 1790 } 1791 } 1792 1793 if (tag != NULL) 1794 { 1795 /* when adding with a tag, we need to stub a branch, if it 1796 doesn't already exist. */ 1797 1798 if (rcsfile == NULL) 1799 { 1800 if (rcsnode != NULL && *rcsnode != NULL) 1801 rcsfile = *rcsnode; 1802 else 1803 { 1804 rcsfile = RCS_parse (file, repository); 1805 if (rcsfile == NULL) 1806 { 1807 error (0, 0, "could not read %s", rcs); 1808 return (1); 1809 } 1810 } 1811 } 1812 1813 if (!RCS_nodeisbranch (rcsfile, tag)) { 1814 /* branch does not exist. Stub it. */ 1815 char *head; 1816 char *magicrev; 1817 1818 head = RCS_getversion (rcsfile, NULL, NULL, 0, (int *) NULL); 1819 magicrev = RCS_magicrev (rcsfile, head); 1820 1821 retcode = RCS_settag (rcsfile, tag, magicrev); 1822 1823 free (head); 1824 free (magicrev); 1825 1826 if (retcode != 0) 1827 { 1828 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 1829 "could not stub branch %s for %s", tag, rcs); 1830 return (1); 1831 } 1832 } 1833 else 1834 { 1835 /* lock the branch. (stubbed branches need not be locked.) */ 1836 if (lock_RCS (file, rcsfile, NULL, repository)) { 1837 error (0, 0, "cannot lock `%s'.", rcs); 1838 return (1); 1839 } 1840 } 1841 1842 if (rcsnode && *rcsnode != rcsfile) 1843 { 1844 freercsnode(rcsnode); 1845 *rcsnode = rcsfile; 1846 } 1847 } 1848 1849 fileattr_newfile (file); 1850 1851 fix_rcs_modes (rcs, file); 1852 return (0); 1853 } 1854 1855 /* 1856 * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it 1857 * couldn't. If the RCS file currently has a branch as the head, we must 1858 * move the head back to the trunk before locking the file, and be sure to 1859 * put the branch back as the head if there are any errors. 1860 */ 1861 static int 1862 lock_RCS (user, rcs, rev, repository) 1863 char *user; 1864 RCSNode *rcs; 1865 char *rev; 1866 char *repository; 1867 { 1868 char *branch = NULL; 1869 int err = 0; 1870 1871 /* 1872 * For a specified, numeric revision of the form "1" or "1.1", (or when 1873 * no revision is specified ""), definitely move the branch to the trunk 1874 * before locking the RCS file. 1875 * 1876 * The assumption is that if there is more than one revision on the trunk, 1877 * the head points to the trunk, not a branch... and as such, it's not 1878 * necessary to move the head in this case. 1879 */ 1880 if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2)) 1881 { 1882 branch = xstrdup (rcs->branch); 1883 if (branch != NULL) 1884 { 1885 if (RCS_setbranch (rcs, NULL) != 0) 1886 { 1887 error (0, 0, "cannot change branch to default for %s", 1888 rcs->path); 1889 if (branch) 1890 free (branch); 1891 return (1); 1892 } 1893 } 1894 err = RCS_lock(rcs, NULL, 0); 1895 } 1896 else 1897 { 1898 (void) RCS_lock(rcs, rev, 1); 1899 } 1900 1901 if (err == 0) 1902 { 1903 if (branch) 1904 { 1905 (void) strcpy (sbranch, branch); 1906 free (branch); 1907 } 1908 else 1909 sbranch[0] = '\0'; 1910 return (0); 1911 } 1912 1913 /* try to restore the branch if we can on error */ 1914 if (branch != NULL) 1915 fixbranch (rcs, branch); 1916 1917 if (branch) 1918 free (branch); 1919 return (1); 1920 } 1921 1922 /* 1923 * Called when "add"ing files to the RCS respository, as it is necessary to 1924 * preserve the file modes in the same fashion that RCS does. This would be 1925 * automatic except that we are placing the RCS ,v file very far away from 1926 * the user file, and I can't seem to convince RCS of the location of the 1927 * user file. So we munge it here, after the ,v file has been successfully 1928 * initialized with "rcs -i". 1929 */ 1930 static void 1931 fix_rcs_modes (rcs, user) 1932 char *rcs; 1933 char *user; 1934 { 1935 struct stat sb; 1936 1937 if ( CVS_STAT (user, &sb) != -1) 1938 (void) chmod (rcs, (int) sb.st_mode & ~0222); 1939 } 1940 1941 /* 1942 * free an UPDATE node's data 1943 */ 1944 void 1945 update_delproc (p) 1946 Node *p; 1947 { 1948 struct logfile_info *li; 1949 1950 li = (struct logfile_info *) p->data; 1951 if (li->tag) 1952 free (li->tag); 1953 free (li); 1954 } 1955 1956 /* 1957 * Free the commit_info structure in p. 1958 */ 1959 static void 1960 ci_delproc (p) 1961 Node *p; 1962 { 1963 struct commit_info *ci; 1964 1965 ci = (struct commit_info *) p->data; 1966 if (ci->rev) 1967 free (ci->rev); 1968 if (ci->tag) 1969 free (ci->tag); 1970 if (ci->options) 1971 free (ci->options); 1972 free (ci); 1973 } 1974 1975 /* 1976 * Free the commit_info structure in p. 1977 */ 1978 static void 1979 masterlist_delproc (p) 1980 Node *p; 1981 { 1982 struct master_lists *ml; 1983 1984 ml = (struct master_lists *) p->data; 1985 dellist (&ml->ulist); 1986 dellist (&ml->cilist); 1987 free (ml); 1988 } 1989 1990 /* 1991 * Find an RCS file in the repository. 1992 */ 1993 static void 1994 locate_rcs (file, repository, rcs) 1995 char *file; 1996 char *repository; 1997 char *rcs; 1998 { 1999 (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); 2000 if (!isreadable (rcs)) 2001 { 2002 (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT); 2003 if (!isreadable (rcs)) 2004 (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); 2005 } 2006 } 2007