add.c revision 34461
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 * Add 9 * 10 * Adds a file or directory to the RCS source repository. For a file, 11 * the entry is marked as "needing to be added" in the user's own CVS 12 * directory, and really added to the repository when it is committed. 13 * For a directory, it is added at the appropriate place in the source 14 * repository and a CVS directory is generated within the directory. 15 * 16 * The -m option is currently the only supported option. Some may wish to 17 * supply standard "rcs" options here, but I've found that this causes more 18 * trouble than anything else. 19 * 20 * The user files or directories must already exist. For a directory, it must 21 * not already have a CVS file in it. 22 * 23 * An "add" on a file that has been "remove"d but not committed will cause the 24 * file to be resurrected. 25 */ 26 27#include "cvs.h" 28#include "savecwd.h" 29#include "fileattr.h" 30 31static int add_directory PROTO ((struct file_info *finfo)); 32static int build_entry PROTO((char *repository, char *user, char *options, 33 char *message, List * entries, char *tag)); 34 35static const char *const add_usage[] = 36{ 37 "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", 38 "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n", 39 "\t-m\tUse \"message\" for the creation log.\n", 40 "(Specify the --help global option for a list of other help options)\n", 41 NULL 42}; 43 44int 45add (argc, argv) 46 int argc; 47 char **argv; 48{ 49 char *message = NULL; 50 int i; 51 char *repository; 52 int c; 53 int err = 0; 54 int added_files = 0; 55 char *options = NULL; 56 List *entries; 57 Vers_TS *vers; 58 struct saved_cwd cwd; 59 60 if (argc == 1 || argc == -1) 61 usage (add_usage); 62 63 wrap_setup (); 64 65 /* parse args */ 66 optind = 0; 67 while ((c = getopt (argc, argv, "+k:m:")) != -1) 68 { 69 switch (c) 70 { 71 case 'k': 72 if (options) 73 free (options); 74 options = RCS_check_kflag (optarg); 75 break; 76 77 case 'm': 78 message = xstrdup (optarg); 79 break; 80 case '?': 81 default: 82 usage (add_usage); 83 break; 84 } 85 } 86 argc -= optind; 87 argv += optind; 88 89 if (argc <= 0) 90 usage (add_usage); 91 92 /* First some sanity checks. I know that the CVS case is (sort of) 93 also handled by add_directory, but we need to check here so the 94 client won't get all confused in send_file_names. */ 95 for (i = 0; i < argc; i++) 96 { 97 int skip_file = 0; 98 99 /* If it were up to me I'd probably make this a fatal error. 100 But some people are really fond of their "cvs add *", and 101 don't seem to object to the warnings. 102 Whatever. */ 103 strip_trailing_slashes (argv[i]); 104 if (strcmp (argv[i], ".") == 0 105 || strcmp (argv[i], "..") == 0 106 || fncmp (argv[i], CVSADM) == 0) 107 { 108 error (0, 0, "cannot add special file `%s'; skipping", argv[i]); 109 skip_file = 1; 110 } 111 112 if (skip_file) 113 { 114 int j; 115 116 /* FIXME: We don't do anything about free'ing argv[i]. But 117 the problem is that it is only sometimes allocated (see 118 cvsrc.c). */ 119 120 for (j = i; j < argc - 1; ++j) 121 argv[j] = argv[j + 1]; 122 --argc; 123 /* Check the new argv[i] again. */ 124 --i; 125 ++err; 126 } 127 } 128 129#ifdef CLIENT_SUPPORT 130 if (client_active) 131 { 132 int i; 133 134 if (argc == 0) 135 /* We snipped out all the arguments in the above sanity 136 check. We can just forget the whole thing (and we 137 better, because if we fired up the server and passed it 138 nothing, it would spit back a usage message). */ 139 return err; 140 141 start_server (); 142 ign_setup (); 143 if (options) send_arg(options); 144 option_with_arg ("-m", message); 145 146 repository = Name_Repository (NULL, NULL); 147 send_a_repository ("", repository, ""); 148 free (repository); 149 150 for (i = 0; i < argc; ++i) 151 /* FIXME: Does this erroneously call Create_Admin in error 152 conditions which are only detected once the server gets its 153 hands on things? */ 154 if (isdir (argv[i])) 155 { 156 char *tag; 157 char *date; 158 int nonbranch; 159 char *rcsdir; 160 char *p; 161 char *update_dir; 162 /* This is some mungeable storage into which we can point 163 with p and/or update_dir. */ 164 char *filedir; 165 166 if (save_cwd (&cwd)) 167 error_exit (); 168 169 filedir = xstrdup (argv[i]); 170 p = last_component (filedir); 171 if (p == filedir) 172 { 173 update_dir = ""; 174 } 175 else 176 { 177 p[-1] = '\0'; 178 update_dir = filedir; 179 if (CVS_CHDIR (update_dir) < 0) 180 error (1, errno, 181 "could not chdir to %s", update_dir); 182 } 183 184 /* find the repository associated with our current dir */ 185 repository = Name_Repository (NULL, update_dir); 186 187 /* before we do anything else, see if we have any 188 per-directory tags */ 189 ParseTag (&tag, &date, &nonbranch); 190 191 rcsdir = xmalloc (strlen (repository) + strlen (p) + 5); 192 sprintf (rcsdir, "%s/%s", repository, p); 193 194 Create_Admin (p, argv[i], rcsdir, tag, date, 195 nonbranch, 0); 196 197 send_a_repository ("", repository, update_dir); 198 199 if (restore_cwd (&cwd, NULL)) 200 error_exit (); 201 free_cwd (&cwd); 202 203 if (tag) 204 free (tag); 205 if (date) 206 free (date); 207 free (rcsdir); 208 209 if (p == filedir) 210 Subdir_Register ((List *) NULL, (char *) NULL, argv[i]); 211 else 212 { 213 Subdir_Register ((List *) NULL, update_dir, p); 214 } 215 free (repository); 216 free (filedir); 217 } 218 send_file_names (argc, argv, SEND_EXPAND_WILD); 219 send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS); 220 send_to_server ("add\012", 0); 221 if (message) 222 free (message); 223 return err + get_responses_and_close (); 224 } 225#endif 226 227 /* walk the arg list adding files/dirs */ 228 for (i = 0; i < argc; i++) 229 { 230 int begin_err = err; 231#ifdef SERVER_SUPPORT 232 int begin_added_files = added_files; 233#endif 234 struct file_info finfo; 235 char *p; 236 237 memset (&finfo, 0, sizeof finfo); 238 239 if (save_cwd (&cwd)) 240 error_exit (); 241 242 finfo.fullname = xstrdup (argv[i]); 243 p = last_component (argv[i]); 244 if (p == argv[i]) 245 { 246 finfo.update_dir = ""; 247 finfo.file = p; 248 } 249 else 250 { 251 p[-1] = '\0'; 252 finfo.update_dir = argv[i]; 253 finfo.file = p; 254 if (CVS_CHDIR (finfo.update_dir) < 0) 255 error (1, errno, "could not chdir to %s", finfo.update_dir); 256 } 257 258 /* Add wrappers for this directory. They exist only until 259 the next call to wrap_add_file. */ 260 wrap_add_file (CVSDOTWRAPPER, 1); 261 262 finfo.rcs = NULL; 263 264 /* Find the repository associated with our current dir. */ 265 repository = Name_Repository (NULL, finfo.update_dir); 266 267 entries = Entries_Open (0, NULL); 268 269 finfo.repository = repository; 270 finfo.entries = entries; 271 272 /* We pass force_tag_match as 1. If the directory has a 273 sticky branch tag, and there is already an RCS file which 274 does not have that tag, then the head revision is 275 meaningless to us. */ 276 vers = Version_TS (&finfo, options, NULL, NULL, 1, 0); 277 if (vers->vn_user == NULL) 278 { 279 /* No entry available, ts_rcs is invalid */ 280 if (vers->vn_rcs == NULL) 281 { 282 /* There is no RCS file either */ 283 if (vers->ts_user == NULL) 284 { 285 /* There is no user file either */ 286 error (0, 0, "nothing known about %s", finfo.fullname); 287 err++; 288 } 289 else if (!isdir (finfo.file) 290 || wrap_name_has (finfo.file, WRAP_TOCVS)) 291 { 292 /* 293 * See if a directory exists in the repository with 294 * the same name. If so, blow this request off. 295 */ 296 char *dname = xmalloc (strlen (repository) 297 + strlen (finfo.file) 298 + 10); 299 (void) sprintf (dname, "%s/%s", repository, finfo.file); 300 if (isdir (dname)) 301 { 302 error (0, 0, 303 "cannot add file `%s' since the directory", 304 finfo.fullname); 305 error (0, 0, "`%s' already exists in the repository", 306 dname); 307 error (1, 0, "illegal filename overlap"); 308 } 309 free (dname); 310 311 if (vers->options == NULL || *vers->options == '\0') 312 { 313 /* No options specified on command line (or in 314 rcs file if it existed, e.g. the file exists 315 on another branch). Check for a value from 316 the wrapper stuff. */ 317 if (wrap_name_has (finfo.file, WRAP_RCSOPTION)) 318 { 319 if (vers->options) 320 free (vers->options); 321 vers->options = wrap_rcsoption (finfo.file, 1); 322 } 323 } 324 325 if (vers->nonbranch) 326 { 327 error (0, 0, 328 "cannot add file on non-branch tag %s", 329 vers->tag); 330 ++err; 331 } 332 else 333 { 334 /* There is a user file, so build the entry for it */ 335 if (build_entry (repository, finfo.file, vers->options, 336 message, entries, vers->tag) != 0) 337 err++; 338 else 339 { 340 added_files++; 341 if (!quiet) 342 { 343 if (vers->tag) 344 error (0, 0, "\ 345scheduling %s `%s' for addition on branch `%s'", 346 (wrap_name_has (finfo.file, 347 WRAP_TOCVS) 348 ? "wrapper" 349 : "file"), 350 finfo.fullname, vers->tag); 351 else 352 error (0, 0, 353 "scheduling %s `%s' for addition", 354 (wrap_name_has (finfo.file, 355 WRAP_TOCVS) 356 ? "wrapper" 357 : "file"), 358 finfo.fullname); 359 } 360 } 361 } 362 } 363 } 364 else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 365 { 366 if (isdir (finfo.file) 367 && !wrap_name_has (finfo.file, WRAP_TOCVS)) 368 { 369 error (0, 0, "\ 370the directory `%s' cannot be added because a file of the", finfo.fullname); 371 error (1, 0, "\ 372same name already exists in the repository."); 373 } 374 else 375 { 376 if (vers->nonbranch) 377 { 378 error (0, 0, 379 "cannot add file on non-branch tag %s", 380 vers->tag); 381 ++err; 382 } 383 else 384 { 385 if (vers->tag) 386 error (0, 0, "\ 387file `%s' will be added on branch `%s' from version %s", 388 finfo.fullname, vers->tag, vers->vn_rcs); 389 else 390 /* I'm not sure that mentioning 391 vers->vn_rcs makes any sense here; I 392 can't think of a way to word the 393 message which is not confusing. */ 394 error (0, 0, "\ 395re-adding file %s (in place of dead revision %s)", 396 finfo.fullname, vers->vn_rcs); 397 Register (entries, finfo.file, "0", vers->ts_user, 398 NULL, 399 vers->tag, NULL, NULL); 400 ++added_files; 401 } 402 } 403 } 404 else 405 { 406 /* 407 * There is an RCS file already, so somebody else must've 408 * added it 409 */ 410 error (0, 0, "%s added independently by second party", 411 finfo.fullname); 412 err++; 413 } 414 } 415 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 416 { 417 418 /* 419 * An entry for a new-born file, ts_rcs is dummy, but that is 420 * inappropriate here 421 */ 422 error (0, 0, "%s has already been entered", finfo.fullname); 423 err++; 424 } 425 else if (vers->vn_user[0] == '-') 426 { 427 /* An entry for a removed file, ts_rcs is invalid */ 428 if (vers->ts_user == NULL) 429 { 430 /* There is no user file (as it should be) */ 431 if (vers->vn_rcs == NULL) 432 { 433 434 /* 435 * There is no RCS file, so somebody else must've removed 436 * it from under us 437 */ 438 error (0, 0, "\ 439cannot resurrect %s; RCS file removed by second party", finfo.fullname); 440 err++; 441 } 442 else 443 { 444 445 /* 446 * There is an RCS file, so remove the "-" from the 447 * version number and restore the file 448 */ 449 char *tmp = xmalloc (strlen (finfo.file) + 50); 450 451 (void) strcpy (tmp, vers->vn_user + 1); 452 (void) strcpy (vers->vn_user, tmp); 453 (void) sprintf (tmp, "Resurrected %s", finfo.file); 454 Register (entries, finfo.file, vers->vn_user, tmp, 455 vers->options, 456 vers->tag, vers->date, vers->ts_conflict); 457 free (tmp); 458 459 /* XXX - bugs here; this really resurrect the head */ 460 /* Note that this depends on the Register above actually 461 having written Entries, or else it won't really 462 check the file out. */ 463 if (update (2, argv + i - 1) == 0) 464 { 465 error (0, 0, "%s, version %s, resurrected", 466 finfo.fullname, 467 vers->vn_user); 468 } 469 else 470 { 471 error (0, 0, "could not resurrect %s", finfo.fullname); 472 err++; 473 } 474 } 475 } 476 else 477 { 478 /* The user file shouldn't be there */ 479 error (0, 0, "\ 480%s should be removed and is still there (or is back again)", finfo.fullname); 481 err++; 482 } 483 } 484 else 485 { 486 /* A normal entry, ts_rcs is valid, so it must already be there */ 487 error (0, 0, "%s already exists, with version number %s", 488 finfo.fullname, 489 vers->vn_user); 490 err++; 491 } 492 freevers_ts (&vers); 493 494 /* passed all the checks. Go ahead and add it if its a directory */ 495 if (begin_err == err 496 && isdir (finfo.file) 497 && !wrap_name_has (finfo.file, WRAP_TOCVS)) 498 { 499 err += add_directory (&finfo); 500 } 501 else 502 { 503#ifdef SERVER_SUPPORT 504 if (server_active && begin_added_files != added_files) 505 server_checked_in (finfo.file, finfo.update_dir, repository); 506#endif 507 } 508 free (repository); 509 Entries_Close (entries); 510 511 if (restore_cwd (&cwd, NULL)) 512 error_exit (); 513 free_cwd (&cwd); 514 515 free (finfo.fullname); 516 } 517 if (added_files) 518 error (0, 0, "use '%s commit' to add %s permanently", 519 program_name, 520 (added_files == 1) ? "this file" : "these files"); 521 522 if (message) 523 free (message); 524 525 return (err); 526} 527 528/* 529 * The specified user file is really a directory. So, let's make sure that 530 * it is created in the RCS source repository, and that the user's directory 531 * is updated to include a CVS directory. 532 * 533 * Returns 1 on failure, 0 on success. 534 */ 535static int 536add_directory (finfo) 537 struct file_info *finfo; 538{ 539 char *repository = finfo->repository; 540 List *entries = finfo->entries; 541 char *dir = finfo->file; 542 543 char *rcsdir = NULL; 544 struct saved_cwd cwd; 545 char *message = NULL; 546 char *tag, *date; 547 int nonbranch; 548 char *attrs; 549 550 if (strchr (dir, '/') != NULL) 551 { 552 /* "Can't happen". */ 553 error (0, 0, 554 "directory %s not added; must be a direct sub-directory", dir); 555 return (1); 556 } 557 if (fncmp (dir, CVSADM) == 0) 558 { 559 error (0, 0, "cannot add a `%s' directory", CVSADM); 560 return (1); 561 } 562 563 /* before we do anything else, see if we have any per-directory tags */ 564 ParseTag (&tag, &date, &nonbranch); 565 566 /* Remember the default attributes from this directory, so we can apply 567 them to the new directory. */ 568 fileattr_startdir (repository); 569 attrs = fileattr_getall (NULL); 570 fileattr_free (); 571 572 /* now, remember where we were, so we can get back */ 573 if (save_cwd (&cwd)) 574 return (1); 575 if ( CVS_CHDIR (dir) < 0) 576 { 577 error (0, errno, "cannot chdir to %s", finfo->fullname); 578 return (1); 579 } 580#ifdef SERVER_SUPPORT 581 if (!server_active && isfile (CVSADM)) 582#else 583 if (isfile (CVSADM)) 584#endif 585 { 586 error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM); 587 goto out; 588 } 589 590 rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5); 591 sprintf (rcsdir, "%s/%s", repository, dir); 592 if (isfile (rcsdir) && !isdir (rcsdir)) 593 { 594 error (0, 0, "%s is not a directory; %s not added", rcsdir, 595 finfo->fullname); 596 goto out; 597 } 598 599 /* setup the log message */ 600 message = xmalloc (strlen (rcsdir) 601 + 80 602 + (tag == NULL ? 0 : strlen (tag) + 80) 603 + (date == NULL ? 0 : strlen (date) + 80)); 604 (void) sprintf (message, "Directory %s added to the repository\n", rcsdir); 605 if (tag) 606 { 607 (void) strcat (message, "--> Using per-directory sticky tag `"); 608 (void) strcat (message, tag); 609 (void) strcat (message, "'\n"); 610 } 611 if (date) 612 { 613 (void) strcat (message, "--> Using per-directory sticky date `"); 614 (void) strcat (message, date); 615 (void) strcat (message, "'\n"); 616 } 617 618 if (!isdir (rcsdir)) 619 { 620 mode_t omask; 621 Node *p; 622 List *ulist; 623 struct logfile_info *li; 624 625 /* There used to be some code here which would prompt for 626 whether to add the directory. The details of that code had 627 bitrotted, but more to the point it can't work 628 client/server, doesn't ask in the right way for GUIs, etc. 629 A better way of making it harder to accidentally add 630 directories would be to have to add and commit directories 631 like for files. The code was #if 0'd at least since CVS 1.5. */ 632 633 if (!noexec) 634 { 635 omask = umask (cvsumask); 636 if (CVS_MKDIR (rcsdir, 0777) < 0) 637 { 638 error (0, errno, "cannot mkdir %s", rcsdir); 639 (void) umask (omask); 640 goto out; 641 } 642 (void) umask (omask); 643 } 644 645 /* Now set the default file attributes to the ones we inherited 646 from the parent directory. */ 647 fileattr_startdir (rcsdir); 648 fileattr_setall (NULL, attrs); 649 fileattr_write (); 650 fileattr_free (); 651 if (attrs != NULL) 652 free (attrs); 653 654 /* 655 * Set up an update list with a single title node for Update_Logfile 656 */ 657 ulist = getlist (); 658 p = getnode (); 659 p->type = UPDATE; 660 p->delproc = update_delproc; 661 p->key = xstrdup ("- New directory"); 662 li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); 663 li->type = T_TITLE; 664 li->tag = xstrdup (tag); 665 li->rev_old = li->rev_new = NULL; 666 p->data = (char *) li; 667 (void) addnode (ulist, p); 668 Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); 669 dellist (&ulist); 670 } 671 672#ifdef SERVER_SUPPORT 673 if (!server_active) 674 Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0); 675#else 676 Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0); 677#endif 678 if (tag) 679 free (tag); 680 if (date) 681 free (date); 682 683 if (restore_cwd (&cwd, NULL)) 684 error_exit (); 685 free_cwd (&cwd); 686 687 Subdir_Register (entries, (char *) NULL, dir); 688 689 cvs_output (message, 0); 690 691 free (rcsdir); 692 free (message); 693 694 return (0); 695 696out: 697 if (restore_cwd (&cwd, NULL)) 698 error_exit (); 699 free_cwd (&cwd); 700 if (rcsdir != NULL) 701 free (rcsdir); 702 return (0); 703} 704 705/* 706 * Builds an entry for a new file and sets up "CVS/file",[pt] by 707 * interrogating the user. Returns non-zero on error. 708 */ 709static int 710build_entry (repository, user, options, message, entries, tag) 711 char *repository; 712 char *user; 713 char *options; 714 char *message; 715 List *entries; 716 char *tag; 717{ 718 char *fname; 719 char *line; 720 FILE *fp; 721 722 if (noexec) 723 return (0); 724 725 /* 726 * The requested log is read directly from the user and stored in the 727 * file user,t. If the "message" argument is set, use it as the 728 * initial creation log (which typically describes the file). 729 */ 730 fname = xmalloc (strlen (user) + 80); 731 (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG); 732 fp = open_file (fname, "w+"); 733 if (message && fputs (message, fp) == EOF) 734 error (1, errno, "cannot write to %s", fname); 735 if (fclose(fp) == EOF) 736 error(1, errno, "cannot close %s", fname); 737 free (fname); 738 739 /* 740 * Create the entry now, since this allows the user to interrupt us above 741 * without needing to clean anything up (well, we could clean up the 742 * ,t file, but who cares). 743 */ 744 line = xmalloc (strlen (user) + 20); 745 (void) sprintf (line, "Initial %s", user); 746 Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0); 747 free (line); 748 return (0); 749} 750