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 * Commit Files 14 * 15 * "commit" commits the present version to the RCS repository, AFTER 16 * having done a test on conflicts. 17 * 18 * The call is: cvs commit [options] files... 19 * 20 */ 21 22 #include "cvs.h" 23 #include "getline.h" 24 #include "edit.h" 25 #include "fileattr.h" 26 #include "hardlink.h" 27 28 static Dtype check_direntproc (void *callerdat, const char *dir, 29 const char *repos, const char *update_dir, 30 List *entries); 31 static int check_fileproc (void *callerdat, struct file_info *finfo); 32 static int check_filesdoneproc (void *callerdat, int err, const char *repos, 33 const char *update_dir, List *entries); 34 static int checkaddfile (const char *file, const char *repository, 35 const char *tag, const char *options, 36 RCSNode **rcsnode); 37 static Dtype commit_direntproc (void *callerdat, const char *dir, 38 const char *repos, const char *update_dir, 39 List *entries); 40 static int commit_dirleaveproc (void *callerdat, const char *dir, int err, 41 const char *update_dir, List *entries); 42 static int commit_fileproc (void *callerdat, struct file_info *finfo); 43 static int commit_filesdoneproc (void *callerdat, int err, 44 const char *repository, 45 const char *update_dir, List *entries); 46 static int finaladd (struct file_info *finfo, char *revision, char *tag, 47 char *options); 48 static int findmaxrev (Node * p, void *closure); 49 static int lock_RCS (const char *user, RCSNode *rcs, const char *rev, 50 const char *repository); 51 static int precommit_list_to_args_proc (Node * p, void *closure); 52 static int precommit_proc (const char *repository, const char *filter, 53 void *closure); 54 static int remove_file (struct file_info *finfo, char *tag, 55 char *message); 56 static void fixaddfile (const char *rcs); 57 static void fixbranch (RCSNode *, char *branch); 58 static void unlockrcs (RCSNode *rcs); 59 static void ci_delproc (Node *p); 60 static void masterlist_delproc (Node *p); 61 62 struct commit_info 63 { 64 Ctype status; /* as returned from Classify_File() */ 65 char *rev; /* a numeric rev, if we know it */ 66 char *tag; /* any sticky tag, or -r option */ 67 char *options; /* Any sticky -k option */ 68 }; 69 struct master_lists 70 { 71 List *ulist; /* list for Update_Logfile */ 72 List *cilist; /* list with commit_info structs */ 73 }; 74 75 static int check_valid_edit = 0; 76 static int force_ci = 0; 77 static int got_message; 78 static int aflag; 79 static char *saved_tag; 80 static char *write_dirtag; 81 static int write_dirnonbranch; 82 static char *logfile; 83 static List *mulist; 84 static char *saved_message; 85 static time_t last_register_time; 86 87 static const char *const commit_usage[] = 88 { 89 "Usage: %s %s [-cRlf] [-m msg | -F logfile] [-r rev] files...\n", 90 " -c Check for valid edits before committing.\n", 91 " -R Process directories recursively.\n", 92 " -l Local directory only (not recursive).\n", 93 " -f Force the file to be committed; disables recursion.\n", 94 " -F logfile Read the log message from file.\n", 95 " -m msg Log message.\n", 96 " -r rev Commit to this branch or trunk revision.\n", 97 "(Specify the --help global option for a list of other help options)\n", 98 NULL 99 }; 100 101 #ifdef CLIENT_SUPPORT 102 /* Identify a file which needs "? foo" or a Questionable request. */ 103 struct question 104 { 105 /* The two fields for the Directory request. */ 106 char *dir; 107 char *repos; 108 109 /* The file name. */ 110 char *file; 111 112 struct question *next; 113 }; 114 115 struct find_data 116 { 117 List *ulist; 118 int argc; 119 char **argv; 120 121 /* This is used from dirent to filesdone time, for each directory, 122 to make a list of files we have already seen. */ 123 List *ignlist; 124 125 /* Linked list of files which need "? foo" or a Questionable request. */ 126 struct question *questionables; 127 128 /* Only good within functions called from the filesdoneproc. Stores 129 the repository (pointer into storage managed by the recursion 130 processor. */ 131 const char *repository; 132 133 /* Non-zero if we should force the commit. This is enabled by 134 either -f or -r options, unlike force_ci which is just -f. */ 135 int force; 136 }; 137 138 139 140 static Dtype 141 find_dirent_proc (void *callerdat, const char *dir, const char *repository, 142 const char *update_dir, List *entries) 143 { 144 struct find_data *find_data = callerdat; 145 146 /* This check seems to slowly be creeping throughout CVS (update 147 and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995. My guess 148 is that it (or some variant thereof) should go in all the 149 dirent procs. Unless someone has some better idea... */ 150 if (!isdir (dir)) 151 return R_SKIP_ALL; 152 153 /* initialize the ignore list for this directory */ 154 find_data->ignlist = getlist (); 155 156 /* Print the same warm fuzzy as in check_direntproc, since that 157 code will never be run during client/server operation and we 158 want the messages to match. */ 159 if (!quiet) 160 error (0, 0, "Examining %s", update_dir); 161 162 return R_PROCESS; 163 } 164 165 166 167 /* Here as a static until we get around to fixing ignore_files to pass 168 it along as an argument. */ 169 static struct find_data *find_data_static; 170 171 172 173 static void 174 find_ignproc (const char *file, const char *dir) 175 { 176 struct question *p; 177 178 p = xmalloc (sizeof (struct question)); 179 p->dir = xstrdup (dir); 180 p->repos = xstrdup (find_data_static->repository); 181 p->file = xstrdup (file); 182 p->next = find_data_static->questionables; 183 find_data_static->questionables = p; 184 } 185 186 187 188 static int 189 find_filesdoneproc (void *callerdat, int err, const char *repository, 190 const char *update_dir, List *entries) 191 { 192 struct find_data *find_data = callerdat; 193 find_data->repository = repository; 194 195 /* if this directory has an ignore list, process it then free it */ 196 if (find_data->ignlist) 197 { 198 find_data_static = find_data; 199 ignore_files (find_data->ignlist, entries, update_dir, find_ignproc); 200 dellist (&find_data->ignlist); 201 } 202 203 find_data->repository = NULL; 204 205 return err; 206 } 207 208 209 210 /* Machinery to find out what is modified, added, and removed. It is 211 possible this should be broken out into a new client_classify function; 212 merging it with classify_file is almost sure to be a mess, though, 213 because classify_file has all kinds of repository processing. */ 214 static int 215 find_fileproc (void *callerdat, struct file_info *finfo) 216 { 217 Vers_TS *vers; 218 enum classify_type status; 219 Node *node; 220 struct find_data *args = callerdat; 221 struct logfile_info *data; 222 struct file_info xfinfo; 223 224 /* if this directory has an ignore list, add this file to it */ 225 if (args->ignlist) 226 { 227 Node *p; 228 229 p = getnode (); 230 p->type = FILES; 231 p->key = xstrdup (finfo->file); 232 if (addnode (args->ignlist, p) != 0) 233 freenode (p); 234 } 235 236 xfinfo = *finfo; 237 xfinfo.repository = NULL; 238 xfinfo.rcs = NULL; 239 240 vers = Version_TS (&xfinfo, NULL, saved_tag, NULL, 0, 0); 241 if (vers->vn_user == NULL) 242 { 243 if (vers->ts_user == NULL) 244 error (0, 0, "nothing known about `%s'", finfo->fullname); 245 else 246 error (0, 0, "use `%s add' to create an entry for `%s'", 247 program_name, finfo->fullname); 248 freevers_ts (&vers); 249 return 1; 250 } 251 if (vers->vn_user[0] == '-') 252 { 253 if (vers->ts_user != NULL) 254 { 255 error (0, 0, 256 "`%s' should be removed and is still there (or is back" 257 " again)", finfo->fullname); 258 freevers_ts (&vers); 259 return 1; 260 } 261 /* else */ 262 status = T_REMOVED; 263 } 264 else if (strcmp (vers->vn_user, "0") == 0) 265 { 266 if (vers->ts_user == NULL) 267 { 268 /* This happens when one has `cvs add'ed a file, but it no 269 longer exists in the working directory at commit time. 270 FIXME: What classify_file does in this case is print 271 "new-born %s has disappeared" and removes the entry. 272 We probably should do the same. */ 273 if (!really_quiet) 274 error (0, 0, "warning: new-born %s has disappeared", 275 finfo->fullname); 276 status = T_REMOVE_ENTRY; 277 } 278 else 279 status = T_ADDED; 280 } 281 else if (vers->ts_user == NULL) 282 { 283 /* FIXME: What classify_file does in this case is print 284 "%s was lost". We probably should do the same. */ 285 freevers_ts (&vers); 286 return 0; 287 } 288 else if (vers->ts_rcs != NULL 289 && (args->force || strcmp (vers->ts_user, vers->ts_rcs) != 0)) 290 /* If we are forcing commits, pretend that the file is 291 modified. */ 292 status = T_MODIFIED; 293 else 294 { 295 /* This covers unmodified files, as well as a variety of other 296 cases. FIXME: we probably should be printing a message and 297 returning 1 for many of those cases (but I'm not sure 298 exactly which ones). */ 299 freevers_ts (&vers); 300 return 0; 301 } 302 303 node = getnode (); 304 node->key = xstrdup (finfo->fullname); 305 306 data = xmalloc (sizeof (struct logfile_info)); 307 data->type = status; 308 data->tag = xstrdup (vers->tag); 309 data->rev_old = data->rev_new = NULL; 310 311 node->type = UPDATE; 312 node->delproc = update_delproc; 313 node->data = data; 314 (void)addnode (args->ulist, node); 315 316 ++args->argc; 317 318 freevers_ts (&vers); 319 return 0; 320 } 321 322 323 324 static int 325 copy_ulist (Node *node, void *data) 326 { 327 struct find_data *args = data; 328 args->argv[args->argc++] = node->key; 329 return 0; 330 } 331 #endif /* CLIENT_SUPPORT */ 332 333 334 335 #ifdef SERVER_SUPPORT 336 # define COMMIT_OPTIONS "+cnlRm:fF:r:" 337 #else /* !SERVER_SUPPORT */ 338 # define COMMIT_OPTIONS "+clRm:fF:r:" 339 #endif /* SERVER_SUPPORT */ 340 int 341 commit (int argc, char **argv) 342 { 343 int c; 344 int err = 0; 345 int local = 0; 346 347 if (argc == -1) 348 usage (commit_usage); 349 350 #ifdef CVS_BADROOT 351 /* 352 * For log purposes, do not allow "root" to commit files. If you look 353 * like root, but are really logged in as a non-root user, it's OK. 354 */ 355 /* FIXME: Shouldn't this check be much more closely related to the 356 readonly user stuff (CVSROOT/readers, &c). That is, why should 357 root be able to "cvs init", "cvs import", &c, but not "cvs ci"? */ 358 /* Who we are on the client side doesn't affect logging. */ 359 if (geteuid () == (uid_t) 0 && !current_parsed_root->isremote) 360 { 361 struct passwd *pw; 362 363 if ((pw = getpwnam (getcaller ())) == NULL) 364 error (1, 0, 365 "your apparent username (%s) is unknown to this system", 366 getcaller ()); 367 if (pw->pw_uid == (uid_t) 0) 368 error (1, 0, "'root' is not allowed to commit files"); 369 } 370 #endif /* CVS_BADROOT */ 371 372 getoptreset (); 373 while ((c = getopt (argc, argv, COMMIT_OPTIONS)) != -1) 374 { 375 switch (c) 376 { 377 case 'c': 378 check_valid_edit = 1; 379 break; 380 #ifdef SERVER_SUPPORT 381 case 'n': 382 /* Silently ignore -n for compatibility with old 383 * clients. 384 */ 385 break; 386 #endif /* SERVER_SUPPORT */ 387 case 'm': 388 #ifdef FORCE_USE_EDITOR 389 use_editor = 1; 390 #else 391 use_editor = 0; 392 #endif 393 if (saved_message) 394 { 395 free (saved_message); 396 saved_message = NULL; 397 } 398 399 saved_message = xstrdup (optarg); 400 break; 401 case 'r': 402 if (saved_tag) 403 free (saved_tag); 404 saved_tag = xstrdup (optarg); 405 break; 406 case 'l': 407 local = 1; 408 break; 409 case 'R': 410 local = 0; 411 break; 412 case 'f': 413 force_ci = 1; 414 check_valid_edit = 0; 415 local = 1; /* also disable recursion */ 416 break; 417 case 'F': 418 #ifdef FORCE_USE_EDITOR 419 use_editor = 1; 420 #else 421 use_editor = 0; 422 #endif 423 logfile = optarg; 424 break; 425 case '?': 426 default: 427 usage (commit_usage); 428 break; 429 } 430 } 431 argc -= optind; 432 argv += optind; 433 434 /* numeric specified revision means we ignore sticky tags... */ 435 if (saved_tag && isdigit ((unsigned char) *saved_tag)) 436 { 437 char *p = saved_tag + strlen (saved_tag); 438 aflag = 1; 439 /* strip trailing dots and leading zeros */ 440 while (*--p == '.') ; 441 p[1] = '\0'; 442 while (saved_tag[0] == '0' && isdigit ((unsigned char) saved_tag[1])) 443 ++saved_tag; 444 } 445 446 /* some checks related to the "-F logfile" option */ 447 if (logfile) 448 { 449 size_t size = 0, len; 450 451 if (saved_message) 452 error (1, 0, "cannot specify both a message and a log file"); 453 454 get_file (logfile, logfile, "r", &saved_message, &size, &len); 455 } 456 457 #ifdef CLIENT_SUPPORT 458 if (current_parsed_root->isremote) 459 { 460 struct find_data find_args; 461 462 ign_setup (); 463 464 find_args.ulist = getlist (); 465 find_args.argc = 0; 466 find_args.questionables = NULL; 467 find_args.ignlist = NULL; 468 find_args.repository = NULL; 469 470 /* It is possible that only a numeric tag should set this. 471 I haven't really thought about it much. 472 Anyway, I suspect that setting it unnecessarily only causes 473 a little unneeded network traffic. */ 474 find_args.force = force_ci || saved_tag != NULL; 475 476 err = start_recursion 477 (find_fileproc, find_filesdoneproc, find_dirent_proc, NULL, 478 &find_args, argc, argv, local, W_LOCAL, 0, CVS_LOCK_NONE, 479 NULL, 0, NULL ); 480 if (err) 481 error (1, 0, "correct above errors first!"); 482 483 if (find_args.argc == 0) 484 { 485 /* Nothing to commit. Exit now without contacting the 486 server (note that this means that we won't print "? 487 foo" for files which merit it, because we don't know 488 what is in the CVSROOT/cvsignore file). */ 489 dellist (&find_args.ulist); 490 return 0; 491 } 492 493 /* Now we keep track of which files we actually are going to 494 operate on, and only work with those files in the future. 495 This saves time--we don't want to search the file system 496 of the working directory twice. */ 497 if (size_overflow_p (xtimes (find_args.argc, sizeof (char **)))) 498 { 499 find_args.argc = 0; 500 return 0; 501 } 502 find_args.argv = xnmalloc (find_args.argc, sizeof (char **)); 503 find_args.argc = 0; 504 walklist (find_args.ulist, copy_ulist, &find_args); 505 506 /* Do this before calling do_editor; don't ask for a log 507 message if we can't talk to the server. But do it after we 508 have made the checks that we can locally (to more quickly 509 catch syntax errors, the case where no files are modified, 510 added or removed, etc.). 511 512 On the other hand, calling start_server before do_editor 513 means that we chew up server resources the whole time that 514 the user has the editor open (hours or days if the user 515 forgets about it), which seems dubious. */ 516 start_server (); 517 518 /* 519 * We do this once, not once for each directory as in normal CVS. 520 * The protocol is designed this way. This is a feature. 521 */ 522 if (use_editor) 523 do_editor (".", &saved_message, NULL, find_args.ulist); 524 525 /* We always send some sort of message, even if empty. */ 526 option_with_arg ("-m", saved_message ? saved_message : ""); 527 528 /* OK, now process all the questionable files we have been saving 529 up. */ 530 { 531 struct question *p; 532 struct question *q; 533 534 p = find_args.questionables; 535 while (p != NULL) 536 { 537 if (ign_inhibit_server || !supported_request ("Questionable")) 538 { 539 cvs_output ("? ", 2); 540 if (p->dir[0] != '\0') 541 { 542 cvs_output (p->dir, 0); 543 cvs_output ("/", 1); 544 } 545 cvs_output (p->file, 0); 546 cvs_output ("\n", 1); 547 } 548 else 549 { 550 /* This used to send the Directory line of its own accord, 551 * but skipped some of the other processing like checking 552 * for whether the server would accept "Relative-directory" 553 * requests. Relying on send_a_repository() to do this 554 * picks up these checks but also: 555 * 556 * 1. Causes the "Directory" request to be sent only once 557 * per directory. 558 * 2. Causes the global TOPLEVEL_REPOS to be set. 559 * 3. Causes "Static-directory" and "Sticky" requests 560 * to sometimes be sent. 561 * 562 * (1) is almost certainly a plus. (2) & (3) may or may 563 * not be useful sometimes, and will ocassionally cause a 564 * little extra network traffic. The additional network 565 * traffic is probably already saved several times over and 566 * certainly cancelled out via the multiple "Directory" 567 * request suppression of (1). 568 */ 569 send_a_repository (p->dir, p->repos, p->dir); 570 571 send_to_server ("Questionable ", 0); 572 send_to_server (p->file, 0); 573 send_to_server ("\012", 1); 574 } 575 free (p->dir); 576 free (p->repos); 577 free (p->file); 578 q = p->next; 579 free (p); 580 p = q; 581 } 582 } 583 584 if (local) 585 send_arg ("-l"); 586 if (check_valid_edit) 587 send_arg ("-c"); 588 if (force_ci) 589 send_arg ("-f"); 590 option_with_arg ("-r", saved_tag); 591 send_arg ("--"); 592 593 /* FIXME: This whole find_args.force/SEND_FORCE business is a 594 kludge. It would seem to be a server bug that we have to 595 say that files are modified when they are not. This makes 596 "cvs commit -r 2" across a whole bunch of files a very slow 597 operation (and it isn't documented in cvsclient.texi). I 598 haven't looked at the server code carefully enough to be 599 _sure_ why this is needed, but if it is because the "ci" 600 program, which we used to call, wanted the file to exist, 601 then it would be relatively simple to fix in the server. */ 602 send_files (find_args.argc, find_args.argv, local, 0, 603 find_args.force ? SEND_FORCE : 0); 604 605 /* Sending only the names of the files which were modified, added, 606 or removed means that the server will only do an up-to-date 607 check on those files. This is different from local CVS and 608 previous versions of client/server CVS, but it probably is a Good 609 Thing, or at least Not Such A Bad Thing. */ 610 send_file_names (find_args.argc, find_args.argv, 0); 611 free (find_args.argv); 612 dellist (&find_args.ulist); 613 614 send_to_server ("ci\012", 0); 615 err = get_responses_and_close (); 616 if (err != 0 && use_editor && saved_message != NULL) 617 { 618 /* If there was an error, don't nuke the user's carefully 619 constructed prose. This is something of a kludge; a better 620 solution is probably more along the lines of #150 in TODO 621 (doing a second up-to-date check before accepting the 622 log message has also been suggested, but that seems kind of 623 iffy because the real up-to-date check could still fail, 624 another error could occur, &c. Also, a second check would 625 slow things down). */ 626 627 char *fname; 628 FILE *fp; 629 630 fp = cvs_temp_file (&fname); 631 if (fp == NULL) 632 error (1, 0, "cannot create temporary file %s", fname); 633 if (fwrite (saved_message, 1, strlen (saved_message), fp) 634 != strlen (saved_message)) 635 error (1, errno, "cannot write temporary file %s", fname); 636 if (fclose (fp) < 0) 637 error (0, errno, "cannot close temporary file %s", fname); 638 error (0, 0, "saving log message in %s", fname); 639 free (fname); 640 } 641 return err; 642 } 643 #endif 644 645 if (saved_tag != NULL) 646 tag_check_valid (saved_tag, argc, argv, local, aflag, "", false); 647 648 /* XXX - this is not the perfect check for this */ 649 if (argc <= 0) 650 write_dirtag = saved_tag; 651 652 wrap_setup (); 653 654 lock_tree_promotably (argc, argv, local, W_LOCAL, aflag); 655 656 /* 657 * Set up the master update list and hard link list 658 */ 659 mulist = getlist (); 660 661 #ifdef PRESERVE_PERMISSIONS_SUPPORT 662 if (preserve_perms) 663 { 664 hardlist = getlist (); 665 666 /* 667 * We need to save the working directory so that 668 * check_fileproc can construct a full pathname for each file. 669 */ 670 working_dir = xgetcwd (); 671 } 672 #endif 673 674 /* 675 * Run the recursion processor to verify the files are all up-to-date 676 */ 677 err = start_recursion (check_fileproc, check_filesdoneproc, 678 check_direntproc, NULL, NULL, argc, argv, local, 679 W_LOCAL, aflag, CVS_LOCK_NONE, NULL, 1, NULL); 680 if (err) 681 error (1, 0, "correct above errors first!"); 682 683 /* 684 * Run the recursion processor to commit the files 685 */ 686 write_dirnonbranch = 0; 687 if (noexec == 0) 688 err = start_recursion (commit_fileproc, commit_filesdoneproc, 689 commit_direntproc, commit_dirleaveproc, NULL, 690 argc, argv, local, W_LOCAL, aflag, 691 CVS_LOCK_WRITE, NULL, 1, NULL); 692 693 /* 694 * Unlock all the dirs and clean up 695 */ 696 Lock_Cleanup (); 697 dellist (&mulist); 698 699 /* see if we need to sleep before returning to avoid time-stamp races */ 700 if (!server_active && last_register_time) 701 { 702 sleep_past (last_register_time); 703 } 704 705 return err; 706 } 707 708 709 710 /* This routine determines the status of a given file and retrieves 711 the version information that is associated with that file. */ 712 713 static 714 Ctype 715 classify_file_internal (struct file_info *finfo, Vers_TS **vers) 716 { 717 int save_noexec, save_quiet, save_really_quiet; 718 Ctype status; 719 720 /* FIXME: Do we need to save quiet as well as really_quiet? Last 721 time I glanced at Classify_File I only saw it looking at really_quiet 722 not quiet. */ 723 save_noexec = noexec; 724 save_quiet = quiet; 725 save_really_quiet = really_quiet; 726 noexec = quiet = really_quiet = 1; 727 728 /* handle specified numeric revision specially */ 729 if (saved_tag && isdigit ((unsigned char) *saved_tag)) 730 { 731 /* If the tag is for the trunk, make sure we're at the head */ 732 if (numdots (saved_tag) < 2) 733 { 734 status = Classify_File (finfo, NULL, NULL, 735 NULL, 1, aflag, vers, 0); 736 if (status == T_UPTODATE || status == T_MODIFIED || 737 status == T_ADDED) 738 { 739 Ctype xstatus; 740 741 freevers_ts (vers); 742 xstatus = Classify_File (finfo, saved_tag, NULL, 743 NULL, 1, aflag, vers, 0); 744 if (xstatus == T_REMOVE_ENTRY) 745 status = T_MODIFIED; 746 else if (status == T_MODIFIED && xstatus == T_CONFLICT) 747 status = T_MODIFIED; 748 else 749 status = xstatus; 750 } 751 } 752 else 753 { 754 char *xtag, *cp; 755 756 /* 757 * The revision is off the main trunk; make sure we're 758 * up-to-date with the head of the specified branch. 759 */ 760 xtag = xstrdup (saved_tag); 761 if ((numdots (xtag) & 1) != 0) 762 { 763 cp = strrchr (xtag, '.'); 764 *cp = '\0'; 765 } 766 status = Classify_File (finfo, xtag, NULL, 767 NULL, 1, aflag, vers, 0); 768 if ((status == T_REMOVE_ENTRY || status == T_CONFLICT) 769 && (cp = strrchr (xtag, '.')) != NULL) 770 { 771 /* pluck one more dot off the revision */ 772 *cp = '\0'; 773 freevers_ts (vers); 774 status = Classify_File (finfo, xtag, NULL, 775 NULL, 1, aflag, vers, 0); 776 if (status == T_UPTODATE || status == T_REMOVE_ENTRY) 777 status = T_MODIFIED; 778 } 779 /* now, muck with vers to make the tag correct */ 780 free ((*vers)->tag); 781 (*vers)->tag = xstrdup (saved_tag); 782 free (xtag); 783 } 784 } 785 else 786 status = Classify_File (finfo, saved_tag, NULL, NULL, 1, 0, vers, 0); 787 noexec = save_noexec; 788 quiet = save_quiet; 789 really_quiet = save_really_quiet; 790 791 return status; 792 } 793 794 795 796 /* 797 * Check to see if a file is ok to commit and make sure all files are 798 * up-to-date 799 */ 800 /* ARGSUSED */ 801 static int 802 check_fileproc (void *callerdat, struct file_info *finfo) 803 { 804 Ctype status; 805 const char *xdir; 806 Node *p; 807 List *ulist, *cilist; 808 Vers_TS *vers; 809 struct commit_info *ci; 810 struct logfile_info *li; 811 int retval = 1; 812 813 size_t cvsroot_len = strlen (current_parsed_root->directory); 814 815 if (!finfo->repository) 816 { 817 error (0, 0, "nothing known about `%s'", finfo->fullname); 818 return 1; 819 } 820 821 if (strncmp (finfo->repository, current_parsed_root->directory, 822 cvsroot_len) == 0 823 && ISSLASH (finfo->repository[cvsroot_len]) 824 && strncmp (finfo->repository + cvsroot_len + 1, 825 CVSROOTADM, 826 sizeof (CVSROOTADM) - 1) == 0 827 && ISSLASH (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)]) 828 && strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1, 829 CVSNULLREPOS) == 0 830 ) 831 error (1, 0, "cannot check in to %s", finfo->repository); 832 833 status = classify_file_internal (finfo, &vers); 834 835 /* 836 * If the force-commit option is enabled, and the file in question 837 * appears to be up-to-date, just make it look modified so that 838 * it will be committed. 839 */ 840 if (force_ci && status == T_UPTODATE) 841 status = T_MODIFIED; 842 843 switch (status) 844 { 845 case T_CHECKOUT: 846 case T_PATCH: 847 case T_NEEDS_MERGE: 848 case T_REMOVE_ENTRY: 849 error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname); 850 goto out; 851 case T_CONFLICT: 852 case T_MODIFIED: 853 case T_ADDED: 854 case T_REMOVED: 855 { 856 char *editor; 857 858 /* 859 * some quick sanity checks; if no numeric -r option specified: 860 * - can't have a sticky date 861 * - can't have a sticky tag that is not a branch 862 * Also, 863 * - if status is T_REMOVED, file must not exist and its entry 864 * can't have a numeric sticky tag. 865 * - if status is T_ADDED, rcs file must not exist unless on 866 * a branch or head is dead 867 * - if status is T_ADDED, can't have a non-trunk numeric rev 868 * - if status is T_MODIFIED and a Conflict marker exists, don't 869 * allow the commit if timestamp is identical or if we find 870 * an RCS_MERGE_PAT in the file. 871 */ 872 if (!saved_tag || !isdigit ((unsigned char) *saved_tag)) 873 { 874 if (vers->date) 875 { 876 error (0, 0, 877 "cannot commit with sticky date for file `%s'", 878 finfo->fullname); 879 goto out; 880 } 881 if (status == T_MODIFIED && vers->tag && 882 !RCS_isbranch (finfo->rcs, vers->tag)) 883 { 884 error (0, 0, 885 "sticky tag `%s' for file `%s' is not a branch", 886 vers->tag, finfo->fullname); 887 goto out; 888 } 889 } 890 if (status == T_CONFLICT && !force_ci) 891 { 892 error (0, 0, 893 "file `%s' had a conflict and has not been modified", 894 finfo->fullname); 895 goto out; 896 } 897 if (status == T_MODIFIED && !force_ci && file_has_markers (finfo)) 898 { 899 /* Make this a warning, not an error, because we have 900 no way of knowing whether the "conflict indicators" 901 are really from a conflict or whether they are part 902 of the document itself (cvs.texinfo and sanity.sh in 903 CVS itself, for example, tend to want to have strings 904 like ">>>>>>>" at the start of a line). Making people 905 kludge this the way they need to kludge keyword 906 expansion seems undesirable. And it is worse than 907 keyword expansion, because there is no -ko 908 analogue. */ 909 error (0, 0, 910 "\ 911 warning: file `%s' seems to still contain conflict indicators", 912 finfo->fullname); 913 } 914 915 if (status == T_REMOVED) 916 { 917 if (vers->ts_user != NULL) 918 { 919 error (0, 0, 920 "`%s' should be removed and is still there (or is" 921 " back again)", finfo->fullname); 922 goto out; 923 } 924 925 if (vers->tag && isdigit ((unsigned char) *vers->tag)) 926 { 927 /* Remove also tries to forbid this, but we should check 928 here. I'm only _sure_ about somewhat obscure cases 929 (hacking the Entries file, using an old version of 930 CVS for the remove and a new one for the commit), but 931 there might be other cases. */ 932 error (0, 0, 933 "cannot remove file `%s' which has a numeric sticky" 934 " tag of `%s'", finfo->fullname, vers->tag); 935 freevers_ts (&vers); 936 goto out; 937 } 938 } 939 if (status == T_ADDED) 940 { 941 if (vers->tag == NULL) 942 { 943 if (finfo->rcs != NULL && 944 !RCS_isdead (finfo->rcs, finfo->rcs->head)) 945 { 946 error (0, 0, 947 "cannot add file `%s' when RCS file `%s' already exists", 948 finfo->fullname, finfo->rcs->path); 949 goto out; 950 } 951 } 952 else if (isdigit ((unsigned char) *vers->tag) && 953 numdots (vers->tag) > 1) 954 { 955 error (0, 0, 956 "cannot add file `%s' with revision `%s'; must be on trunk", 957 finfo->fullname, vers->tag); 958 goto out; 959 } 960 } 961 962 /* done with consistency checks; now, to get on with the commit */ 963 if (finfo->update_dir[0] == '\0') 964 xdir = "."; 965 else 966 xdir = finfo->update_dir; 967 if ((p = findnode (mulist, xdir)) != NULL) 968 { 969 ulist = ((struct master_lists *) p->data)->ulist; 970 cilist = ((struct master_lists *) p->data)->cilist; 971 } 972 else 973 { 974 struct master_lists *ml; 975 976 ml = xmalloc (sizeof (struct master_lists)); 977 ulist = ml->ulist = getlist (); 978 cilist = ml->cilist = getlist (); 979 980 p = getnode (); 981 p->key = xstrdup (xdir); 982 p->type = UPDATE; 983 p->data = ml; 984 p->delproc = masterlist_delproc; 985 (void) addnode (mulist, p); 986 } 987 988 /* first do ulist, then cilist */ 989 p = getnode (); 990 p->key = xstrdup (finfo->file); 991 p->type = UPDATE; 992 p->delproc = update_delproc; 993 li = xmalloc (sizeof (struct logfile_info)); 994 li->type = status; 995 996 if (check_valid_edit) 997 { 998 char *editors = NULL; 999 1000 editor = NULL; 1001 editors = fileattr_get0 (finfo->file, "_editors"); 1002 if (editors != NULL) 1003 { 1004 char *caller = getcaller (); 1005 char *p = NULL; 1006 char *p0 = NULL; 1007 1008 p = editors; 1009 p0 = p; 1010 while (*p != '\0') 1011 { 1012 p = strchr (p, '>'); 1013 if (p == NULL) 1014 { 1015 break; 1016 } 1017 *p = '\0'; 1018 if (strcmp (caller, p0) == 0) 1019 { 1020 break; 1021 } 1022 p = strchr (p + 1, ','); 1023 if (p == NULL) 1024 { 1025 break; 1026 } 1027 ++p; 1028 p0 = p; 1029 } 1030 1031 if (strcmp (caller, p0) == 0) 1032 { 1033 editor = caller; 1034 } 1035 1036 free (editors); 1037 } 1038 } 1039 1040 if (check_valid_edit && editor == NULL) 1041 { 1042 error (0, 0, "Valid edit does not exist for %s", 1043 finfo->fullname); 1044 freevers_ts (&vers); 1045 return 1; 1046 } 1047 1048 li->tag = xstrdup (vers->tag); 1049 /* If the file was re-added, we want the revision in the commitlog 1050 to be NONE, not the previous dead revision. */ 1051 li->rev_old = status == T_ADDED ? NULL : xstrdup (vers->vn_rcs); 1052 li->rev_new = NULL; 1053 p->data = li; 1054 (void) addnode (ulist, p); 1055 1056 p = getnode (); 1057 p->key = xstrdup (finfo->file); 1058 p->type = UPDATE; 1059 p->delproc = ci_delproc; 1060 ci = xmalloc (sizeof (struct commit_info)); 1061 ci->status = status; 1062 if (vers->tag) 1063 if (isdigit ((unsigned char) *vers->tag)) 1064 ci->rev = xstrdup (vers->tag); 1065 else 1066 ci->rev = RCS_whatbranch (finfo->rcs, vers->tag); 1067 else 1068 ci->rev = NULL; 1069 ci->tag = xstrdup (vers->tag); 1070 ci->options = xstrdup (vers->options); 1071 p->data = ci; 1072 (void) addnode (cilist, p); 1073 1074 #ifdef PRESERVE_PERMISSIONS_SUPPORT 1075 if (preserve_perms) 1076 { 1077 /* Add this file to hardlist, indexed on its inode. When 1078 we are done, we can find out what files are hardlinked 1079 to a given file by looking up its inode in hardlist. */ 1080 char *fullpath; 1081 Node *linkp; 1082 struct hardlink_info *hlinfo; 1083 1084 /* Get the full pathname of the current file. */ 1085 fullpath = Xasprintf ("%s/%s", working_dir, finfo->fullname); 1086 1087 /* To permit following links in subdirectories, files 1088 are keyed on finfo->fullname, not on finfo->name. */ 1089 linkp = lookup_file_by_inode (fullpath); 1090 1091 /* If linkp is NULL, the file doesn't exist... maybe 1092 we're doing a remove operation? */ 1093 if (linkp != NULL) 1094 { 1095 /* Create a new hardlink_info node, which will record 1096 the current file's status and the links listed in its 1097 `hardlinks' delta field. We will append this 1098 hardlink_info node to the appropriate hardlist entry. */ 1099 hlinfo = xmalloc (sizeof (struct hardlink_info)); 1100 hlinfo->status = status; 1101 linkp->data = hlinfo; 1102 } 1103 } 1104 #endif 1105 1106 break; 1107 } 1108 1109 case T_UNKNOWN: 1110 error (0, 0, "nothing known about `%s'", finfo->fullname); 1111 goto out; 1112 case T_UPTODATE: 1113 break; 1114 default: 1115 error (0, 0, "CVS internal error: unknown status %d", status); 1116 break; 1117 } 1118 1119 retval = 0; 1120 1121 out: 1122 1123 freevers_ts (&vers); 1124 return retval; 1125 } 1126 1127 1128 1129 /* 1130 * By default, return the code that tells do_recursion to examine all 1131 * directories 1132 */ 1133 /* ARGSUSED */ 1134 static Dtype 1135 check_direntproc (void *callerdat, const char *dir, const char *repos, 1136 const char *update_dir, List *entries) 1137 { 1138 if (!isdir (dir)) 1139 return R_SKIP_ALL; 1140 1141 if (!quiet) 1142 error (0, 0, "Examining %s", update_dir); 1143 1144 return R_PROCESS; 1145 } 1146 1147 1148 1149 /* 1150 * Walklist proc to generate an arg list from the line in commitinfo 1151 */ 1152 static int 1153 precommit_list_to_args_proc (p, closure) 1154 Node *p; 1155 void *closure; 1156 { 1157 struct format_cmdline_walklist_closure *c = closure; 1158 struct logfile_info *li; 1159 char *arg = NULL; 1160 const char *f; 1161 char *d; 1162 size_t doff; 1163 1164 if (p->data == NULL) return 1; 1165 1166 f = c->format; 1167 d = *c->d; 1168 /* foreach requested attribute */ 1169 while (*f) 1170 { 1171 switch (*f++) 1172 { 1173 case 's': 1174 li = p->data; 1175 if (li->type == T_ADDED 1176 || li->type == T_MODIFIED 1177 || li->type == T_REMOVED) 1178 { 1179 arg = p->key; 1180 } 1181 break; 1182 default: 1183 error (1, 0, 1184 "Unknown format character or not a list attribute: %c", 1185 f[-1]); 1186 /* NOTREACHED */ 1187 break; 1188 } 1189 /* copy the attribute into an argument */ 1190 if (c->quotes) 1191 { 1192 arg = cmdlineescape (c->quotes, arg); 1193 } 1194 else 1195 { 1196 arg = cmdlinequote ('"', arg); 1197 } 1198 doff = d - *c->buf; 1199 expand_string (c->buf, c->length, doff + strlen (arg)); 1200 d = *c->buf + doff; 1201 strncpy (d, arg, strlen (arg)); 1202 d += strlen (arg); 1203 free (arg); 1204 1205 /* and always put the extra space on. we'll have to back up a char 1206 * when we're done, but that seems most efficient 1207 */ 1208 doff = d - *c->buf; 1209 expand_string (c->buf, c->length, doff + 1); 1210 d = *c->buf + doff; 1211 *d++ = ' '; 1212 } 1213 /* correct our original pointer into the buff */ 1214 *c->d = d; 1215 return 0; 1216 } 1217 1218 1219 1220 /* 1221 * Callback proc for pre-commit checking 1222 */ 1223 static int 1224 precommit_proc (const char *repository, const char *filter, void *closure) 1225 { 1226 char *newfilter = NULL; 1227 char *cmdline; 1228 const char *srepos = Short_Repository (repository); 1229 List *ulist = closure; 1230 1231 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS 1232 if (!strchr (filter, '%')) 1233 { 1234 error (0, 0, 1235 "warning: commitinfo line contains no format strings:\n" 1236 " \"%s\"\n" 1237 "Appending defaults (\" %%r/%%p %%s\"), but please be aware that this usage is\n" 1238 "deprecated.", filter); 1239 newfilter = Xasprintf ("%s %%r/%%p %%s", filter); 1240 filter = newfilter; 1241 } 1242 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ 1243 1244 /* 1245 * Cast any NULL arguments as appropriate pointers as this is an 1246 * stdarg function and we need to be certain the caller gets what 1247 * is expected. 1248 */ 1249 cmdline = format_cmdline ( 1250 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS 1251 false, srepos, 1252 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ 1253 filter, 1254 "c", "s", cvs_cmd_name, 1255 #ifdef SERVER_SUPPORT 1256 "R", "s", referrer ? referrer->original : "NONE", 1257 #endif /* SERVER_SUPPORT */ 1258 "p", "s", srepos, 1259 "r", "s", current_parsed_root->directory, 1260 "s", ",", ulist, precommit_list_to_args_proc, 1261 (void *) NULL, 1262 (char *) NULL); 1263 1264 if (newfilter) free (newfilter); 1265 1266 if (!cmdline || !strlen (cmdline)) 1267 { 1268 if (cmdline) free (cmdline); 1269 error (0, 0, "precommit proc resolved to the empty string!"); 1270 return 1; 1271 } 1272 1273 run_setup (cmdline); 1274 free (cmdline); 1275 1276 return run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY| 1277 (server_active ? 0 : RUN_UNSETXID)); 1278 } 1279 1280 1281 1282 /* 1283 * Run the pre-commit checks for the dir 1284 */ 1285 /* ARGSUSED */ 1286 static int 1287 check_filesdoneproc (void *callerdat, int err, const char *repos, 1288 const char *update_dir, List *entries) 1289 { 1290 int n; 1291 Node *p; 1292 List *saved_ulist; 1293 1294 /* find the update list for this dir */ 1295 p = findnode (mulist, update_dir); 1296 if (p != NULL) 1297 saved_ulist = ((struct master_lists *) p->data)->ulist; 1298 else 1299 saved_ulist = NULL; 1300 1301 /* skip the checks if there's nothing to do */ 1302 if (saved_ulist == NULL || saved_ulist->list->next == saved_ulist->list) 1303 return err; 1304 1305 /* run any pre-commit checks */ 1306 n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, PIOPT_ALL, 1307 saved_ulist); 1308 if (n > 0) 1309 { 1310 error (0, 0, "Pre-commit check failed"); 1311 err += n; 1312 } 1313 1314 return err; 1315 } 1316 1317 1318 1319 /* 1320 * Do the work of committing a file 1321 */ 1322 static int maxrev; 1323 static char *sbranch; 1324 1325 /* ARGSUSED */ 1326 static int 1327 commit_fileproc (void *callerdat, struct file_info *finfo) 1328 { 1329 Node *p; 1330 int err = 0; 1331 List *ulist, *cilist; 1332 struct commit_info *ci; 1333 1334 /* Keep track of whether write_dirtag is a branch tag. 1335 Note that if it is a branch tag in some files and a nonbranch tag 1336 in others, treat it as a nonbranch tag. It is possible that case 1337 should elicit a warning or an error. */ 1338 if (write_dirtag != NULL 1339 && finfo->rcs != NULL) 1340 { 1341 char *rev = RCS_getversion (finfo->rcs, write_dirtag, NULL, 1, NULL); 1342 if (rev != NULL 1343 && !RCS_nodeisbranch (finfo->rcs, write_dirtag)) 1344 write_dirnonbranch = 1; 1345 if (rev != NULL) 1346 free (rev); 1347 } 1348 1349 if (finfo->update_dir[0] == '\0') 1350 p = findnode (mulist, "."); 1351 else 1352 p = findnode (mulist, finfo->update_dir); 1353 1354 /* 1355 * if p is null, there were file type command line args which were 1356 * all up-to-date so nothing really needs to be done 1357 */ 1358 if (p == NULL) 1359 return 0; 1360 ulist = ((struct master_lists *) p->data)->ulist; 1361 cilist = ((struct master_lists *) p->data)->cilist; 1362 1363 /* 1364 * At this point, we should have the commit message unless we were called 1365 * with files as args from the command line. In that latter case, we 1366 * need to get the commit message ourselves 1367 */ 1368 if (!got_message) 1369 { 1370 got_message = 1; 1371 if (!server_active && use_editor) 1372 do_editor (finfo->update_dir, &saved_message, 1373 finfo->repository, ulist); 1374 do_verify (&saved_message, finfo->repository, ulist); 1375 } 1376 1377 p = findnode (cilist, finfo->file); 1378 if (p == NULL) 1379 return 0; 1380 1381 ci = p->data; 1382 1383 /* cvsacl patch */ 1384 #ifdef SERVER_SUPPORT 1385 if (use_cvs_acl /* && server_active */) 1386 { 1387 int whichperm; 1388 if (ci->status == T_MODIFIED) 1389 whichperm = 3; 1390 else if (ci->status == T_ADDED) 1391 whichperm = 6; 1392 else if (ci->status == T_REMOVED) 1393 whichperm = 7; 1394 1395 if (!access_allowed (finfo->file, finfo->repository, ci->tag, whichperm, 1396 NULL, NULL, 1)) 1397 { 1398 if (stop_at_first_permission_denied) 1399 error (1, 0, "permission denied for %s", 1400 Short_Repository (finfo->repository)); 1401 else 1402 error (0, 0, "permission denied for %s/%s", 1403 Short_Repository (finfo->repository), finfo->file); 1404 1405 return (0); 1406 } 1407 } 1408 #endif 1409 1410 if (ci->status == T_MODIFIED) 1411 { 1412 if (finfo->rcs == NULL) 1413 error (1, 0, "internal error: no parsed RCS file"); 1414 if (lock_RCS (finfo->file, finfo->rcs, ci->rev, 1415 finfo->repository) != 0) 1416 { 1417 unlockrcs (finfo->rcs); 1418 err = 1; 1419 goto out; 1420 } 1421 } 1422 else if (ci->status == T_ADDED) 1423 { 1424 if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options, 1425 &finfo->rcs) != 0) 1426 { 1427 if (finfo->rcs != NULL) 1428 fixaddfile (finfo->rcs->path); 1429 err = 1; 1430 goto out; 1431 } 1432 1433 /* adding files with a tag, now means adding them on a branch. 1434 Since the branch test was done in check_fileproc for 1435 modified files, we need to stub it in again here. */ 1436 1437 if (ci->tag 1438 1439 /* If numeric, it is on the trunk; check_fileproc enforced 1440 this. */ 1441 && !isdigit ((unsigned char) ci->tag[0])) 1442 { 1443 if (finfo->rcs == NULL) 1444 error (1, 0, "internal error: no parsed RCS file"); 1445 if (ci->rev) 1446 free (ci->rev); 1447 ci->rev = RCS_whatbranch (finfo->rcs, ci->tag); 1448 err = Checkin ('A', finfo, ci->rev, 1449 ci->tag, ci->options, saved_message); 1450 if (err != 0) 1451 { 1452 unlockrcs (finfo->rcs); 1453 fixbranch (finfo->rcs, sbranch); 1454 } 1455 1456 (void) time (&last_register_time); 1457 1458 ci->status = T_UPTODATE; 1459 } 1460 } 1461 1462 /* 1463 * Add the file for real 1464 */ 1465 if (ci->status == T_ADDED) 1466 { 1467 char *xrev = NULL; 1468 1469 if (ci->rev == NULL) 1470 { 1471 /* find the max major rev number in this directory */ 1472 maxrev = 0; 1473 (void) walklist (finfo->entries, findmaxrev, NULL); 1474 if (finfo->rcs->head) 1475 { 1476 /* resurrecting: include dead revision */ 1477 int thisrev = atoi (finfo->rcs->head); 1478 if (thisrev > maxrev) 1479 maxrev = thisrev; 1480 } 1481 if (maxrev == 0) 1482 maxrev = 1; 1483 xrev = Xasprintf ("%d", maxrev); 1484 } 1485 1486 /* XXX - an added file with symbolic -r should add tag as well */ 1487 err = finaladd (finfo, ci->rev ? ci->rev : xrev, ci->tag, ci->options); 1488 if (xrev) 1489 free (xrev); 1490 } 1491 else if (ci->status == T_MODIFIED) 1492 { 1493 err = Checkin ('M', finfo, ci->rev, ci->tag, 1494 ci->options, saved_message); 1495 1496 (void) time (&last_register_time); 1497 1498 if (err != 0) 1499 { 1500 unlockrcs (finfo->rcs); 1501 fixbranch (finfo->rcs, sbranch); 1502 } 1503 } 1504 else if (ci->status == T_REMOVED) 1505 { 1506 err = remove_file (finfo, ci->tag, saved_message); 1507 #ifdef SERVER_SUPPORT 1508 if (server_active) 1509 { 1510 server_scratch_entry_only (); 1511 server_updated (finfo, 1512 NULL, 1513 1514 /* Doesn't matter, it won't get checked. */ 1515 SERVER_UPDATED, 1516 1517 (mode_t) -1, 1518 NULL, 1519 NULL); 1520 } 1521 #endif 1522 } 1523 1524 /* Clearly this is right for T_MODIFIED. I haven't thought so much 1525 about T_ADDED or T_REMOVED. */ 1526 notify_do ('C', finfo->file, finfo->update_dir, getcaller (), NULL, NULL, 1527 finfo->repository); 1528 1529 out: 1530 if (err != 0) 1531 { 1532 /* on failure, remove the file from ulist */ 1533 p = findnode (ulist, finfo->file); 1534 if (p) 1535 delnode (p); 1536 } 1537 else 1538 { 1539 /* On success, retrieve the new version number of the file and 1540 copy it into the log information (see logmsg.c 1541 (logfile_write) for more details). We should only update 1542 the version number for files that have been added or 1543 modified but not removed since classify_file_internal 1544 will return the version number of a file even after it has 1545 been removed from the archive, which is not the behavior we 1546 want for our commitlog messages; we want the old version 1547 number and then "NONE." */ 1548 1549 if (ci->status != T_REMOVED) 1550 { 1551 p = findnode (ulist, finfo->file); 1552 if (p) 1553 { 1554 Vers_TS *vers; 1555 struct logfile_info *li; 1556 1557 (void) classify_file_internal (finfo, &vers); 1558 li = p->data; 1559 li->rev_new = xstrdup (vers->vn_rcs); 1560 freevers_ts (&vers); 1561 } 1562 } 1563 } 1564 if (SIG_inCrSect ()) 1565 SIG_endCrSect (); 1566 1567 return err; 1568 } 1569 1570 1571 1572 /* 1573 * Log the commit and clean up the update list 1574 */ 1575 /* ARGSUSED */ 1576 static int 1577 commit_filesdoneproc (void *callerdat, int err, const char *repository, 1578 const char *update_dir, List *entries) 1579 { 1580 Node *p; 1581 List *ulist; 1582 1583 assert (repository); 1584 1585 p = findnode (mulist, update_dir); 1586 if (p == NULL) 1587 return err; 1588 1589 ulist = ((struct master_lists *) p->data)->ulist; 1590 1591 got_message = 0; 1592 1593 /* Build the administrative files if necessary. */ 1594 { 1595 const char *p; 1596 1597 if (strncmp (current_parsed_root->directory, repository, 1598 strlen (current_parsed_root->directory)) != 0) 1599 error (0, 0, 1600 "internal error: repository (%s) doesn't begin with root (%s)", 1601 repository, current_parsed_root->directory); 1602 p = repository + strlen (current_parsed_root->directory); 1603 if (*p == '/') 1604 ++p; 1605 if (strcmp ("CVSROOT", p) == 0 1606 /* Check for subdirectories because people may want to create 1607 subdirectories and list files therein in checkoutlist. */ 1608 || strncmp ("CVSROOT/", p, strlen ("CVSROOT/")) == 0 1609 ) 1610 { 1611 /* "Database" might a little bit grandiose and/or vague, 1612 but "checked-out copies of administrative files, unless 1613 in the case of modules and you are using ndbm in which 1614 case modules.{pag,dir,db}" is verbose and excessively 1615 focused on how the database is implemented. */ 1616 1617 /* mkmodules requires the absolute name of the CVSROOT directory. 1618 Remove anything after the `CVSROOT' component -- this is 1619 necessary when committing in a subdirectory of CVSROOT. */ 1620 char *admin_dir = xstrdup (repository); 1621 int cvsrootlen = strlen ("CVSROOT"); 1622 assert (admin_dir[p - repository + cvsrootlen] == '\0' 1623 || admin_dir[p - repository + cvsrootlen] == '/'); 1624 admin_dir[p - repository + cvsrootlen] = '\0'; 1625 1626 if (!really_quiet) 1627 { 1628 cvs_output (program_name, 0); 1629 cvs_output (" ", 1); 1630 cvs_output (cvs_cmd_name, 0); 1631 cvs_output (": Rebuilding administrative file database\n", 0); 1632 } 1633 mkmodules (admin_dir); 1634 free (admin_dir); 1635 WriteTemplate (".", 1, repository); 1636 } 1637 } 1638 1639 /* FIXME: This used to be above the block above. The advantage of being 1640 * here is that it is not called until after all possible writes from this 1641 * process are complete. The disadvantage is that a fatal error during 1642 * update of CVSROOT can prevent the loginfo script from being called. 1643 * 1644 * A more general solution I have been considering is calling a generic 1645 * "postwrite" hook from the remove write lock routine. 1646 */ 1647 Update_Logfile (repository, saved_message, NULL, ulist); 1648 1649 return err; 1650 } 1651 1652 1653 1654 /* 1655 * Get the log message for a dir 1656 */ 1657 /* ARGSUSED */ 1658 static Dtype 1659 commit_direntproc (void *callerdat, const char *dir, const char *repos, 1660 const char *update_dir, List *entries) 1661 { 1662 Node *p; 1663 List *ulist; 1664 char *real_repos; 1665 1666 if (!isdir (dir)) 1667 return R_SKIP_ALL; 1668 1669 /* find the update list for this dir */ 1670 p = findnode (mulist, update_dir); 1671 if (p != NULL) 1672 ulist = ((struct master_lists *) p->data)->ulist; 1673 else 1674 ulist = NULL; 1675 1676 /* skip the files as an optimization */ 1677 if (ulist == NULL || ulist->list->next == ulist->list) 1678 return R_SKIP_FILES; 1679 1680 /* get commit message */ 1681 got_message = 1; 1682 real_repos = Name_Repository (dir, update_dir); 1683 if (!server_active && use_editor) 1684 do_editor (update_dir, &saved_message, real_repos, ulist); 1685 do_verify (&saved_message, real_repos, ulist); 1686 free (real_repos); 1687 return R_PROCESS; 1688 } 1689 1690 1691 1692 /* 1693 * Process the post-commit proc if necessary 1694 */ 1695 /* ARGSUSED */ 1696 static int 1697 commit_dirleaveproc (void *callerdat, const char *dir, int err, 1698 const char *update_dir, List *entries) 1699 { 1700 /* update the per-directory tag info */ 1701 /* FIXME? Why? The "commit examples" node of cvs.texinfo briefly 1702 mentions commit -r being sticky, but apparently in the context of 1703 this being a confusing feature! */ 1704 if (err == 0 && write_dirtag != NULL) 1705 { 1706 char *repos = Name_Repository (NULL, update_dir); 1707 WriteTag (NULL, write_dirtag, NULL, write_dirnonbranch, 1708 update_dir, repos); 1709 free (repos); 1710 } 1711 1712 return err; 1713 } 1714 1715 1716 1717 /* 1718 * find the maximum major rev number in an entries file 1719 */ 1720 static int 1721 findmaxrev (Node *p, void *closure) 1722 { 1723 int thisrev; 1724 Entnode *entdata = p->data; 1725 1726 if (entdata->type != ENT_FILE) 1727 return 0; 1728 thisrev = atoi (entdata->version); 1729 if (thisrev > maxrev) 1730 maxrev = thisrev; 1731 return 0; 1732 } 1733 1734 /* 1735 * Actually remove a file by moving it to the attic 1736 * XXX - if removing a ,v file that is a relative symbolic link to 1737 * another ,v file, we probably should add a ".." component to the 1738 * link to keep it relative after we move it into the attic. 1739 1740 Return value is 0 on success, or >0 on error (in which case we have 1741 printed an error message). */ 1742 static int 1743 remove_file (struct file_info *finfo, char *tag, char *message) 1744 { 1745 int retcode; 1746 1747 int branch; 1748 int lockflag; 1749 char *corev; 1750 char *rev; 1751 char *prev_rev; 1752 char *old_path; 1753 1754 corev = NULL; 1755 rev = NULL; 1756 prev_rev = NULL; 1757 1758 retcode = 0; 1759 1760 if (finfo->rcs == NULL) 1761 error (1, 0, "internal error: no parsed RCS file"); 1762 1763 branch = 0; 1764 if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag))) 1765 { 1766 /* a symbolic tag is specified; just remove the tag from the file */ 1767 if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0) 1768 { 1769 if (!quiet) 1770 error (0, retcode == -1 ? errno : 0, 1771 "failed to remove tag `%s' from `%s'", tag, 1772 finfo->fullname); 1773 return 1; 1774 } 1775 RCS_rewrite (finfo->rcs, NULL, NULL); 1776 Scratch_Entry (finfo->entries, finfo->file); 1777 return 0; 1778 } 1779 1780 /* we are removing the file from either the head or a branch */ 1781 /* commit a new, dead revision. */ 1782 1783 rev = NULL; 1784 lockflag = 1; 1785 if (branch) 1786 { 1787 char *branchname; 1788 1789 rev = RCS_whatbranch (finfo->rcs, tag); 1790 if (rev == NULL) 1791 { 1792 error (0, 0, "cannot find branch \"%s\".", tag); 1793 return 1; 1794 } 1795 1796 branchname = RCS_getbranch (finfo->rcs, rev, 1); 1797 if (branchname == NULL) 1798 { 1799 /* no revision exists on this branch. use the previous 1800 revision but do not lock. */ 1801 corev = RCS_gettag (finfo->rcs, tag, 1, NULL); 1802 prev_rev = xstrdup (corev); 1803 lockflag = 0; 1804 } else 1805 { 1806 corev = xstrdup (rev); 1807 prev_rev = xstrdup (branchname); 1808 free (branchname); 1809 } 1810 1811 } else /* Not a branch */ 1812 { 1813 /* Get current head revision of file. */ 1814 prev_rev = RCS_head (finfo->rcs); 1815 } 1816 1817 /* if removing without a tag or a branch, then make sure the default 1818 branch is the trunk. */ 1819 if (!tag && !branch) 1820 { 1821 if (RCS_setbranch (finfo->rcs, NULL) != 0) 1822 { 1823 error (0, 0, "cannot change branch to default for %s", 1824 finfo->fullname); 1825 return 1; 1826 } 1827 RCS_rewrite (finfo->rcs, NULL, NULL); 1828 } 1829 1830 /* check something out. Generally this is the head. If we have a 1831 particular rev, then name it. */ 1832 retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL, 1833 NULL, NULL, RUN_TTY, NULL, NULL); 1834 if (retcode != 0) 1835 { 1836 error (0, 0, 1837 "failed to check out `%s'", finfo->fullname); 1838 return 1; 1839 } 1840 1841 /* Except when we are creating a branch, lock the revision so that 1842 we can check in the new revision. */ 1843 if (lockflag) 1844 { 1845 if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0) 1846 RCS_rewrite (finfo->rcs, NULL, NULL); 1847 } 1848 1849 if (corev != NULL) 1850 free (corev); 1851 1852 retcode = RCS_checkin (finfo->rcs, NULL, finfo->file, message, 1853 rev, 0, RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); 1854 if (retcode != 0) 1855 { 1856 if (!quiet) 1857 error (0, retcode == -1 ? errno : 0, 1858 "failed to commit dead revision for `%s'", finfo->fullname); 1859 return 1; 1860 } 1861 /* At this point, the file has been committed as removed. We should 1862 probably tell the history file about it */ 1863 history_write ('R', NULL, finfo->rcs->head, finfo->file, finfo->repository); 1864 1865 if (rev != NULL) 1866 free (rev); 1867 1868 old_path = xstrdup (finfo->rcs->path); 1869 if (!branch) 1870 RCS_setattic (finfo->rcs, 1); 1871 1872 /* Print message that file was removed. */ 1873 if (!really_quiet) 1874 { 1875 cvs_output (old_path, 0); 1876 cvs_output (" <-- ", 0); 1877 if (finfo->update_dir && strlen (finfo->update_dir)) 1878 { 1879 cvs_output (finfo->update_dir, 0); 1880 cvs_output ("/", 1); 1881 } 1882 cvs_output (finfo->file, 0); 1883 cvs_output ("\nnew revision: delete; previous revision: ", 0); 1884 cvs_output (prev_rev, 0); 1885 cvs_output ("\n", 0); 1886 } 1887 1888 free (prev_rev); 1889 1890 free (old_path); 1891 1892 Scratch_Entry (finfo->entries, finfo->file); 1893 return 0; 1894 } 1895 1896 1897 1898 /* 1899 * Do the actual checkin for added files 1900 */ 1901 static int 1902 finaladd (struct file_info *finfo, char *rev, char *tag, char *options) 1903 { 1904 int ret; 1905 1906 ret = Checkin ('A', finfo, rev, tag, options, saved_message); 1907 if (ret == 0) 1908 { 1909 char *tmp = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); 1910 if (unlink_file (tmp) < 0 1911 && !existence_error (errno)) 1912 error (0, errno, "cannot remove %s", tmp); 1913 free (tmp); 1914 } 1915 else if (finfo->rcs != NULL) 1916 fixaddfile (finfo->rcs->path); 1917 1918 (void) time (&last_register_time); 1919 1920 return ret; 1921 } 1922 1923 1924 1925 /* 1926 * Unlock an rcs file 1927 */ 1928 static void 1929 unlockrcs (RCSNode *rcs) 1930 { 1931 int retcode; 1932 1933 if ((retcode = RCS_unlock (rcs, NULL, 1)) != 0) 1934 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 1935 "could not unlock %s", rcs->path); 1936 else 1937 RCS_rewrite (rcs, NULL, NULL); 1938 } 1939 1940 1941 1942 /* 1943 * remove a partially added file. if we can parse it, leave it alone. 1944 * 1945 * FIXME: Every caller that calls this function can access finfo->rcs (the 1946 * parsed RCSNode data), so we should be able to detect that the file needs 1947 * to be removed without reparsing the file as we do below. 1948 */ 1949 static void 1950 fixaddfile (const char *rcs) 1951 { 1952 RCSNode *rcsfile; 1953 int save_really_quiet; 1954 1955 save_really_quiet = really_quiet; 1956 really_quiet = 1; 1957 if ((rcsfile = RCS_parsercsfile (rcs)) == NULL) 1958 { 1959 if (unlink_file (rcs) < 0) 1960 error (0, errno, "cannot remove %s", rcs); 1961 } 1962 else 1963 freercsnode (&rcsfile); 1964 really_quiet = save_really_quiet; 1965 } 1966 1967 1968 1969 /* 1970 * put the branch back on an rcs file 1971 */ 1972 static void 1973 fixbranch (RCSNode *rcs, char *branch) 1974 { 1975 int retcode; 1976 1977 if (branch != NULL) 1978 { 1979 if ((retcode = RCS_setbranch (rcs, branch)) != 0) 1980 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 1981 "cannot restore branch to %s for %s", branch, rcs->path); 1982 RCS_rewrite (rcs, NULL, NULL); 1983 } 1984 } 1985 1986 1987 1988 /* 1989 * do the initial part of a file add for the named file. if adding 1990 * with a tag, put the file in the Attic and point the symbolic tag 1991 * at the committed revision. 1992 * 1993 * INPUTS 1994 * file The name of the file in the workspace. 1995 * repository The repository directory to expect to find FILE,v in. 1996 * tag The name or rev num of the branch being added to, if any. 1997 * options Any RCS keyword expansion options specified by the user. 1998 * rcsnode A pointer to the pre-parsed RCSNode for this file, if the file 1999 * exists in the repository. If this is NULL, assume the file 2000 * does not yet exist. 2001 * 2002 * RETURNS 2003 * 0 on success. 2004 * 1 on errors, after printing any appropriate error messages. 2005 * 2006 * ERRORS 2007 * This function will return an error when any of the following functions do: 2008 * add_rcs_file 2009 * RCS_setattic 2010 * lock_RCS 2011 * RCS_checkin 2012 * RCS_parse (called to verify the newly created archive file) 2013 * RCS_settag 2014 */ 2015 2016 static int 2017 checkaddfile (const char *file, const char *repository, const char *tag, 2018 const char *options, RCSNode **rcsnode) 2019 { 2020 RCSNode *rcs; 2021 char *fname; 2022 int newfile = 0; /* Set to 1 if we created a new RCS archive. */ 2023 int retval = 1; 2024 int adding_on_branch; 2025 2026 assert (rcsnode != NULL); 2027 2028 /* Callers expect to be able to use either "" or NULL to mean the 2029 default keyword expansion. */ 2030 if (options != NULL && options[0] == '\0') 2031 options = NULL; 2032 if (options != NULL) 2033 assert (options[0] == '-' && options[1] == 'k'); 2034 2035 /* If numeric, it is on the trunk; check_fileproc enforced 2036 this. */ 2037 adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]); 2038 2039 if (*rcsnode == NULL) 2040 { 2041 char *rcsname; 2042 char *desc = NULL; 2043 size_t descalloc = 0; 2044 size_t desclen = 0; 2045 const char *opt; 2046 2047 if (adding_on_branch) 2048 { 2049 mode_t omask; 2050 rcsname = xmalloc (strlen (repository) 2051 + sizeof (CVSATTIC) 2052 + strlen (file) 2053 + sizeof (RCSEXT) 2054 + 3); 2055 (void) sprintf (rcsname, "%s/%s", repository, CVSATTIC); 2056 omask = umask (cvsumask); 2057 if (CVS_MKDIR (rcsname, 0777) != 0 && errno != EEXIST) 2058 error (1, errno, "cannot make directory `%s'", rcsname); 2059 (void) umask (omask); 2060 (void) sprintf (rcsname, 2061 "%s/%s/%s%s", 2062 repository, 2063 CVSATTIC, 2064 file, 2065 RCSEXT); 2066 } 2067 else 2068 rcsname = Xasprintf ("%s/%s%s", repository, file, RCSEXT); 2069 2070 /* this is the first time we have ever seen this file; create 2071 an RCS file. */ 2072 fname = Xasprintf ("%s/%s%s", CVSADM, file, CVSEXT_LOG); 2073 /* If the file does not exist, no big deal. In particular, the 2074 server does not (yet at least) create CVSEXT_LOG files. */ 2075 if (isfile (fname)) 2076 /* FIXME: Should be including update_dir in the appropriate 2077 place here. */ 2078 get_file (fname, fname, "r", &desc, &descalloc, &desclen); 2079 free (fname); 2080 2081 /* From reading the RCS 5.7 source, "rcs -i" adds a newline to the 2082 end of the log message if the message is nonempty. 2083 Do it. RCS also deletes certain whitespace, in cleanlogmsg, 2084 which we don't try to do here. */ 2085 if (desclen > 0) 2086 { 2087 expand_string (&desc, &descalloc, desclen + 1); 2088 desc[desclen++] = '\012'; 2089 } 2090 2091 /* Set RCS keyword expansion options. */ 2092 if (options != NULL) 2093 opt = options + 2; 2094 else 2095 opt = NULL; 2096 2097 if (add_rcs_file (NULL, rcsname, file, NULL, opt, 2098 NULL, NULL, 0, NULL, 2099 desc, desclen, NULL, 0) != 0) 2100 { 2101 if (rcsname != NULL) 2102 free (rcsname); 2103 goto out; 2104 } 2105 rcs = RCS_parsercsfile (rcsname); 2106 newfile = 1; 2107 if (rcsname != NULL) 2108 free (rcsname); 2109 if (desc != NULL) 2110 free (desc); 2111 *rcsnode = rcs; 2112 } 2113 else 2114 { 2115 /* file has existed in the past. Prepare to resurrect. */ 2116 char *rev; 2117 char *oldexpand; 2118 2119 rcs = *rcsnode; 2120 2121 oldexpand = RCS_getexpand (rcs); 2122 if ((oldexpand != NULL 2123 && options != NULL 2124 && strcmp (options + 2, oldexpand) != 0) 2125 || (oldexpand == NULL && options != NULL)) 2126 { 2127 /* We tell the user about this, because it means that the 2128 old revisions will no longer retrieve the way that they 2129 used to. */ 2130 error (0, 0, "changing keyword expansion mode to %s", options); 2131 RCS_setexpand (rcs, options + 2); 2132 } 2133 2134 if (!adding_on_branch) 2135 { 2136 /* We are adding on the trunk, so move the file out of the 2137 Attic. */ 2138 if (!(rcs->flags & INATTIC)) 2139 { 2140 error (0, 0, "warning: expected %s to be in Attic", 2141 rcs->path); 2142 } 2143 2144 /* Begin a critical section around the code that spans the 2145 first commit on the trunk of a file that's already been 2146 committed on a branch. */ 2147 SIG_beginCrSect (); 2148 2149 if (RCS_setattic (rcs, 0)) 2150 { 2151 goto out; 2152 } 2153 } 2154 2155 rev = RCS_getversion (rcs, tag, NULL, 1, NULL); 2156 /* and lock it */ 2157 if (lock_RCS (file, rcs, rev, repository)) 2158 { 2159 error (0, 0, "cannot lock revision %s in `%s'.", 2160 rev ? rev : tag ? tag : "HEAD", rcs->path); 2161 if (rev != NULL) 2162 free (rev); 2163 goto out; 2164 } 2165 2166 if (rev != NULL) 2167 free (rev); 2168 } 2169 2170 /* when adding a file for the first time, and using a tag, we need 2171 to create a dead revision on the trunk. */ 2172 if (adding_on_branch) 2173 { 2174 if (newfile) 2175 { 2176 char *tmp; 2177 FILE *fp; 2178 int retcode; 2179 2180 /* move the new file out of the way. */ 2181 fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file); 2182 rename_file (file, fname); 2183 2184 /* Create empty FILE. Can't use copy_file with a DEVNULL 2185 argument -- copy_file now ignores device files. */ 2186 fp = fopen (file, "w"); 2187 if (fp == NULL) 2188 error (1, errno, "cannot open %s for writing", file); 2189 if (fclose (fp) < 0) 2190 error (0, errno, "cannot close %s", file); 2191 2192 tmp = Xasprintf ("file %s was initially added on branch %s.", 2193 file, tag); 2194 /* commit a dead revision. */ 2195 retcode = RCS_checkin (rcs, NULL, NULL, tmp, NULL, 0, 2196 RCS_FLAGS_DEAD | RCS_FLAGS_QUIET); 2197 free (tmp); 2198 if (retcode != 0) 2199 { 2200 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 2201 "could not create initial dead revision %s", rcs->path); 2202 free (fname); 2203 goto out; 2204 } 2205 2206 /* put the new file back where it was */ 2207 rename_file (fname, file); 2208 free (fname); 2209 2210 /* double-check that the file was written correctly */ 2211 freercsnode (&rcs); 2212 rcs = RCS_parse (file, repository); 2213 if (rcs == NULL) 2214 { 2215 error (0, 0, "could not read %s", rcs->path); 2216 goto out; 2217 } 2218 *rcsnode = rcs; 2219 2220 /* and lock it once again. */ 2221 if (lock_RCS (file, rcs, NULL, repository)) 2222 { 2223 error (0, 0, "cannot lock initial revision in `%s'.", 2224 rcs->path); 2225 goto out; 2226 } 2227 } 2228 2229 /* when adding with a tag, we need to stub a branch, if it 2230 doesn't already exist. */ 2231 if (!RCS_nodeisbranch (rcs, tag)) 2232 { 2233 /* branch does not exist. Stub it. */ 2234 char *head; 2235 char *magicrev; 2236 int retcode; 2237 time_t headtime = -1; 2238 char *revnum, *tmp; 2239 FILE *fp; 2240 time_t t = -1; 2241 struct tm *ct; 2242 2243 fixbranch (rcs, sbranch); 2244 2245 head = RCS_getversion (rcs, NULL, NULL, 0, NULL); 2246 if (!head) 2247 error (1, 0, "No head revision in archive file `%s'.", 2248 rcs->print_path); 2249 magicrev = RCS_magicrev (rcs, head); 2250 2251 /* If this is not a new branch, then we will want a dead 2252 version created before this one. */ 2253 if (!newfile) 2254 headtime = RCS_getrevtime (rcs, head, 0, 0); 2255 2256 retcode = RCS_settag (rcs, tag, magicrev); 2257 RCS_rewrite (rcs, NULL, NULL); 2258 2259 free (head); 2260 free (magicrev); 2261 2262 if (retcode != 0) 2263 { 2264 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 2265 "could not stub branch %s for %s", tag, rcs->path); 2266 goto out; 2267 } 2268 /* We need to add a dead version here to avoid -rtag -Dtime 2269 checkout problems between when the head version was 2270 created and now. */ 2271 if (!newfile && headtime != -1) 2272 { 2273 /* move the new file out of the way. */ 2274 fname = Xasprintf ("%s/%s%s", CVSADM, CVSPREFIX, file); 2275 rename_file (file, fname); 2276 2277 /* Create empty FILE. Can't use copy_file with a DEVNULL 2278 argument -- copy_file now ignores device files. */ 2279 fp = fopen (file, "w"); 2280 if (fp == NULL) 2281 error (1, errno, "cannot open %s for writing", file); 2282 if (fclose (fp) < 0) 2283 error (0, errno, "cannot close %s", file); 2284 2285 /* As we will be hacking the delta date, put the time 2286 this was added into the log message. */ 2287 t = time (NULL); 2288 ct = gmtime (&t); 2289 tmp = Xasprintf ("file %s was added on branch %s on %d-%02d-%02d %02d:%02d:%02d +0000", 2290 file, tag, 2291 ct->tm_year + (ct->tm_year < 100 ? 0 : 1900), 2292 ct->tm_mon + 1, ct->tm_mday, 2293 ct->tm_hour, ct->tm_min, ct->tm_sec); 2294 2295 /* commit a dead revision. */ 2296 revnum = RCS_whatbranch (rcs, tag); 2297 retcode = RCS_checkin (rcs, NULL, NULL, tmp, revnum, headtime, 2298 RCS_FLAGS_DEAD | 2299 RCS_FLAGS_QUIET | 2300 RCS_FLAGS_USETIME); 2301 free (revnum); 2302 free (tmp); 2303 2304 if (retcode != 0) 2305 { 2306 error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, 2307 "could not created dead stub %s for %s", tag, 2308 rcs->path); 2309 goto out; 2310 } 2311 2312 /* put the new file back where it was */ 2313 rename_file (fname, file); 2314 free (fname); 2315 2316 /* double-check that the file was written correctly */ 2317 freercsnode (&rcs); 2318 rcs = RCS_parse (file, repository); 2319 if (rcs == NULL) 2320 { 2321 error (0, 0, "could not read %s", rcs->path); 2322 goto out; 2323 } 2324 *rcsnode = rcs; 2325 } 2326 } 2327 else 2328 { 2329 /* lock the branch. (stubbed branches need not be locked.) */ 2330 if (lock_RCS (file, rcs, NULL, repository)) 2331 { 2332 error (0, 0, "cannot lock head revision in `%s'.", rcs->path); 2333 goto out; 2334 } 2335 } 2336 2337 if (*rcsnode != rcs) 2338 { 2339 freercsnode (rcsnode); 2340 *rcsnode = rcs; 2341 } 2342 } 2343 2344 fileattr_newfile (file); 2345 2346 /* At this point, we used to set the file mode of the RCS file 2347 based on the mode of the file in the working directory. If we 2348 are creating the RCS file for the first time, add_rcs_file does 2349 this already. If we are re-adding the file, then perhaps it is 2350 consistent to preserve the old file mode, just as we preserve 2351 the old keyword expansion mode. 2352 2353 If we decide that we should change the modes, then we can't do 2354 it here anyhow. At this point, the RCS file may be owned by 2355 somebody else, so a chmod will fail. We need to instead do the 2356 chmod after rewriting it. 2357 2358 FIXME: In general, I think the file mode (and the keyword 2359 expansion mode) should be associated with a particular revision 2360 of the file, so that it is possible to have different revisions 2361 of a file have different modes. */ 2362 2363 retval = 0; 2364 2365 out: 2366 if (retval != 0 && SIG_inCrSect ()) 2367 SIG_endCrSect (); 2368 return retval; 2369 } 2370 2371 2372 2373 /* 2374 * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it 2375 * couldn't. If the RCS file currently has a branch as the head, we must 2376 * move the head back to the trunk before locking the file, and be sure to 2377 * put the branch back as the head if there are any errors. 2378 */ 2379 static int 2380 lock_RCS (const char *user, RCSNode *rcs, const char *rev, 2381 const char *repository) 2382 { 2383 char *branch = NULL; 2384 int err = 0; 2385 2386 /* 2387 * For a specified, numeric revision of the form "1" or "1.1", (or when 2388 * no revision is specified ""), definitely move the branch to the trunk 2389 * before locking the RCS file. 2390 * 2391 * The assumption is that if there is more than one revision on the trunk, 2392 * the head points to the trunk, not a branch... and as such, it's not 2393 * necessary to move the head in this case. 2394 */ 2395 if (rev == NULL 2396 || (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2)) 2397 { 2398 branch = xstrdup (rcs->branch); 2399 if (branch != NULL) 2400 { 2401 if (RCS_setbranch (rcs, NULL) != 0) 2402 { 2403 error (0, 0, "cannot change branch to default for %s", 2404 rcs->path); 2405 if (branch) 2406 free (branch); 2407 return 1; 2408 } 2409 } 2410 err = RCS_lock (rcs, NULL, 1); 2411 } 2412 else 2413 { 2414 RCS_lock (rcs, rev, 1); 2415 } 2416 2417 /* We used to call RCS_rewrite here, and that might seem 2418 appropriate in order to write out the locked revision 2419 information. However, such a call would actually serve no 2420 purpose. CVS locks will prevent any interference from other 2421 CVS processes. The comment above rcs_internal_lockfile 2422 explains that it is already unsafe to use RCS and CVS 2423 simultaneously. It follows that writing out the locked 2424 revision information here would add no additional security. 2425 2426 If we ever do care about it, the proper fix is to create the 2427 RCS lock file before calling this function, and maintain it 2428 until the checkin is complete. 2429 2430 The call to RCS_lock is still required at present, since in 2431 some cases RCS_checkin will determine which revision to check 2432 in by looking for a lock. FIXME: This is rather roundabout, 2433 and a more straightforward approach would probably be easier to 2434 understand. */ 2435 2436 if (err == 0) 2437 { 2438 if (sbranch != NULL) 2439 free (sbranch); 2440 sbranch = branch; 2441 return 0; 2442 } 2443 2444 /* try to restore the branch if we can on error */ 2445 if (branch != NULL) 2446 fixbranch (rcs, branch); 2447 2448 if (branch) 2449 free (branch); 2450 return 1; 2451 } 2452 2453 2454 2455 /* 2456 * free an UPDATE node's data 2457 */ 2458 void 2459 update_delproc (Node *p) 2460 { 2461 struct logfile_info *li = p->data; 2462 2463 if (li->tag) 2464 free (li->tag); 2465 if (li->rev_old) 2466 free (li->rev_old); 2467 if (li->rev_new) 2468 free (li->rev_new); 2469 free (li); 2470 } 2471 2472 /* 2473 * Free the commit_info structure in p. 2474 */ 2475 static void 2476 ci_delproc (Node *p) 2477 { 2478 struct commit_info *ci = p->data; 2479 2480 if (ci->rev) 2481 free (ci->rev); 2482 if (ci->tag) 2483 free (ci->tag); 2484 if (ci->options) 2485 free (ci->options); 2486 free (ci); 2487 } 2488 2489 /* 2490 * Free the commit_info structure in p. 2491 */ 2492 static void 2493 masterlist_delproc (Node *p) 2494 { 2495 struct master_lists *ml = p->data; 2496 2497 dellist (&ml->ulist); 2498 dellist (&ml->cilist); 2499 free (ml); 2500 } 2501