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