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