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