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