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