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