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