add.c revision 25839
1356290Sjkim/* 2318899Sjkim * Copyright (c) 1992, Brian Berliner and Jeff Polk 3318899Sjkim * Copyright (c) 1989-1992, Brian Berliner 4318899Sjkim * 5318899Sjkim * You may distribute under the terms of the GNU General Public License as 6318899Sjkim * specified in the README file that comes with the CVS 1.4 kit. 7318899Sjkim * 8318899Sjkim * Add 9318899Sjkim * 10318899Sjkim * Adds a file or directory to the RCS source repository. For a file, 11318899Sjkim * the entry is marked as "needing to be added" in the user's own CVS 12318899Sjkim * directory, and really added to the repository when it is committed. 13318899Sjkim * For a directory, it is added at the appropriate place in the source 14318899Sjkim * repository and a CVS directory is generated within the directory. 15318899Sjkim * 16318899Sjkim * The -m option is currently the only supported option. Some may wish to 17318899Sjkim * supply standard "rcs" options here, but I've found that this causes more 18318899Sjkim * trouble than anything else. 19318899Sjkim * 20318899Sjkim * The user files or directories must already exist. For a directory, it must 21318899Sjkim * not already have a CVS file in it. 22318899Sjkim * 23318899Sjkim * An "add" on a file that has been "remove"d but not committed will cause the 24318899Sjkim * file to be resurrected. 25318899Sjkim */ 26318899Sjkim 27318899Sjkim#include "cvs.h" 28318899Sjkim#include "savecwd.h" 29318899Sjkim 30318899Sjkimstatic int add_directory PROTO((char *repository, List *, char *dir)); 31318899Sjkimstatic int build_entry PROTO((char *repository, char *user, char *options, 32318899Sjkim char *message, List * entries, char *tag)); 33318899Sjkim 34318899Sjkimstatic const char *const add_usage[] = 35318899Sjkim{ 36318899Sjkim "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", 37318899Sjkim "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n", 38318899Sjkim "\t-m\tUse \"message\" for the creation log.\n", 39318899Sjkim NULL 40318899Sjkim}; 41318899Sjkim 42318899Sjkimstatic char *combine_dir PROTO ((char *, char *)); 43318899Sjkim 44318899Sjkim/* Given a directory DIR and a subdirectory within it, SUBDIR, combine 45318899Sjkim the two into a new directory name. Returns a newly malloc'd string. 46318899Sjkim For now this is a fairly simple affair, but perhaps it will want 47318899Sjkim to have grander ambitions in the context of VMS or others (or perhaps 48318899Sjkim not, perhaps that should all be hidden inside CVS_FOPEN and libc and so 49318899Sjkim on, and CVS should just see foo/bar/baz style pathnames). */ 50318899Sjkimstatic char * 51318899Sjkimcombine_dir (dir, subdir) 52318899Sjkim char *dir; 53318899Sjkim char *subdir; 54318899Sjkim{ 55318899Sjkim char *retval; 56318899Sjkim size_t dir_len; 57344604Sjkim 58344604Sjkim dir_len = strlen (dir); 59344604Sjkim retval = xmalloc (dir_len + strlen (subdir) + 10); 60344604Sjkim if (dir_len >= 2 61344604Sjkim && dir[dir_len - 1] == '.' 62344604Sjkim && ISDIRSEP (dir[dir_len - 2])) 63318899Sjkim { 64344604Sjkim /* The dir name has an extraneous "." at the end. 65344604Sjkim I'm not completely sure that this is the best place 66344604Sjkim to strip it off--it is possible that Name_Repository 67344604Sjkim should do so, or it shouldn't be in the CVS/Repository 68318899Sjkim file in the first place. Fixing it here seems like 69318899Sjkim a safe, small change, but I'm not sure it catches 70344604Sjkim all the cases. */ 71318899Sjkim strncpy (retval, dir, dir_len - 2); 72318899Sjkim retval[dir_len - 2] = '\0'; 73318899Sjkim } 74318899Sjkim else 75318899Sjkim { 76318899Sjkim strcpy (retval, dir); 77318899Sjkim } 78318899Sjkim strcat (retval, "/"); 79318899Sjkim strcat (retval, subdir); 80318899Sjkim return retval; 81318899Sjkim} 82318899Sjkim 83318899Sjkimint 84318899Sjkimadd (argc, argv) 85318899Sjkim int argc; 86318899Sjkim char **argv; 87318899Sjkim{ 88318899Sjkim char *message = NULL; 89318899Sjkim char *user; 90318899Sjkim int i; 91318899Sjkim char *repository; 92318899Sjkim int c; 93318899Sjkim int err = 0; 94318899Sjkim int added_files = 0; 95318899Sjkim char *options = NULL; 96318899Sjkim List *entries; 97318899Sjkim Vers_TS *vers; 98318899Sjkim 99318899Sjkim if (argc == 1 || argc == -1) 100318899Sjkim usage (add_usage); 101318899Sjkim 102318899Sjkim wrap_setup (); 103318899Sjkim 104318899Sjkim /* parse args */ 105318899Sjkim optind = 1; 106318899Sjkim while ((c = getopt (argc, argv, "+k:m:")) != -1) 107318899Sjkim { 108318899Sjkim switch (c) 109318899Sjkim { 110318899Sjkim case 'k': 111318899Sjkim if (options) 112318899Sjkim free (options); 113318899Sjkim options = RCS_check_kflag (optarg); 114318899Sjkim break; 115318899Sjkim 116318899Sjkim case 'm': 117318899Sjkim message = xstrdup (optarg); 118318899Sjkim break; 119318899Sjkim case '?': 120318899Sjkim default: 121318899Sjkim usage (add_usage); 122318899Sjkim break; 123318899Sjkim } 124318899Sjkim } 125318899Sjkim argc -= optind; 126318899Sjkim argv += optind; 127318899Sjkim 128318899Sjkim if (argc <= 0) 129318899Sjkim usage (add_usage); 130318899Sjkim 131318899Sjkim /* find the repository associated with our current dir */ 132318899Sjkim repository = Name_Repository ((char *) NULL, (char *) NULL); 133318899Sjkim 134318899Sjkim#ifdef CLIENT_SUPPORT 135318899Sjkim if (client_active) 136356290Sjkim { 137318899Sjkim int i; 138318899Sjkim start_server (); 139318899Sjkim ign_setup (); 140318899Sjkim if (options) send_arg(options); 141318899Sjkim option_with_arg ("-m", message); 142318899Sjkim for (i = 0; i < argc; ++i) 143318899Sjkim /* FIXME: Does this erroneously call Create_Admin in error 144318899Sjkim conditions which are only detected once the server gets its 145318899Sjkim hands on things? */ 146318899Sjkim if (isdir (argv[i])) 147318899Sjkim { 148318899Sjkim char *tag; 149318899Sjkim char *date; 150318899Sjkim int nonbranch; 151318899Sjkim char *rcsdir; 152318899Sjkim 153318899Sjkim /* before we do anything else, see if we have any 154318899Sjkim per-directory tags */ 155318899Sjkim ParseTag (&tag, &date, &nonbranch); 156318899Sjkim 157318899Sjkim rcsdir = combine_dir (repository, argv[i]); 158318899Sjkim 159344604Sjkim strip_trailing_slashes (argv[i]); 160318899Sjkim 161318899Sjkim Create_Admin (argv[i], argv[i], rcsdir, tag, date, nonbranch); 162318899Sjkim 163318899Sjkim if (tag) 164318899Sjkim free (tag); 165344604Sjkim if (date) 166318899Sjkim free (date); 167318899Sjkim free (rcsdir); 168344604Sjkim 169318899Sjkim if (strchr (argv[i], '/') == NULL) 170318899Sjkim Subdir_Register ((List *) NULL, (char *) NULL, argv[i]); 171344604Sjkim else 172318899Sjkim { 173318899Sjkim char *cp, *b; 174318899Sjkim 175318899Sjkim cp = xstrdup (argv[i]); 176318899Sjkim b = strrchr (cp, '/'); 177318899Sjkim *b++ = '\0'; 178318899Sjkim Subdir_Register ((List *) NULL, cp, b); 179318899Sjkim free (cp); 180344604Sjkim } 181344604Sjkim } 182318899Sjkim send_file_names (argc, argv, SEND_EXPAND_WILD); 183318899Sjkim /* FIXME: should be able to pass SEND_NO_CONTENTS, I think. */ 184344604Sjkim send_files (argc, argv, 0, 0, SEND_BUILD_DIRS); 185344604Sjkim send_to_server ("add\012", 0); 186318899Sjkim if (message) 187318899Sjkim free (message); 188318899Sjkim free (repository); 189318899Sjkim return get_responses_and_close (); 190318899Sjkim } 191318899Sjkim#endif 192318899Sjkim 193318899Sjkim entries = Entries_Open (0); 194 195 /* walk the arg list adding files/dirs */ 196 for (i = 0; i < argc; i++) 197 { 198 int begin_err = err; 199#ifdef SERVER_SUPPORT 200 int begin_added_files = added_files; 201#endif 202 struct file_info finfo; 203 204 user = argv[i]; 205 strip_trailing_slashes (user); 206 if (strchr (user, '/') != NULL) 207 { 208 error (0, 0, 209 "cannot add files with '/' in their name; %s not added", user); 210 err++; 211 continue; 212 } 213 214 memset (&finfo, 0, sizeof finfo); 215 finfo.file = user; 216 finfo.update_dir = ""; 217 finfo.fullname = user; 218 finfo.repository = repository; 219 finfo.entries = entries; 220 finfo.rcs = NULL; 221 222 /* We pass force_tag_match as 1. If the directory has a 223 sticky branch tag, and there is already an RCS file which 224 does not have that tag, then the head revision is 225 meaningless to us. */ 226 vers = Version_TS (&finfo, options, NULL, NULL, 1, 0); 227 if (vers->vn_user == NULL) 228 { 229 /* No entry available, ts_rcs is invalid */ 230 if (vers->vn_rcs == NULL) 231 { 232 /* There is no RCS file either */ 233 if (vers->ts_user == NULL) 234 { 235 /* There is no user file either */ 236 error (0, 0, "nothing known about %s", user); 237 err++; 238 } 239 else if (!isdir (user) || wrap_name_has (user, WRAP_TOCVS)) 240 { 241 /* 242 * See if a directory exists in the repository with 243 * the same name. If so, blow this request off. 244 */ 245 char *dname = xmalloc (strlen (repository) + strlen (user) 246 + 10); 247 (void) sprintf (dname, "%s/%s", repository, user); 248 if (isdir (dname)) 249 { 250 error (0, 0, 251 "cannot add file `%s' since the directory", 252 user); 253 error (0, 0, "`%s' already exists in the repository", 254 dname); 255 error (1, 0, "illegal filename overlap"); 256 } 257 free (dname); 258 259 if (vers->options == NULL || *vers->options == '\0') 260 { 261 /* No options specified on command line (or in 262 rcs file if it existed, e.g. the file exists 263 on another branch). Check for a value from 264 the wrapper stuff. */ 265 if (wrap_name_has (user, WRAP_RCSOPTION)) 266 { 267 if (vers->options) 268 free (vers->options); 269 vers->options = wrap_rcsoption (user, 1); 270 } 271 } 272 273 if (vers->nonbranch) 274 { 275 error (0, 0, 276 "cannot add file on non-branch tag %s", 277 vers->tag); 278 ++err; 279 } 280 else 281 { 282 /* There is a user file, so build the entry for it */ 283 if (build_entry (repository, user, vers->options, 284 message, entries, vers->tag) != 0) 285 err++; 286 else 287 { 288 added_files++; 289 if (!quiet) 290 { 291 if (vers->tag) 292 error (0, 0, "\ 293scheduling %s `%s' for addition on branch `%s'", 294 (wrap_name_has (user, WRAP_TOCVS) 295 ? "wrapper" 296 : "file"), 297 user, vers->tag); 298 else 299 error (0, 0, 300 "scheduling %s `%s' for addition", 301 (wrap_name_has (user, WRAP_TOCVS) 302 ? "wrapper" 303 : "file"), 304 user); 305 } 306 } 307 } 308 } 309 } 310 else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 311 { 312 if (isdir (user) && !wrap_name_has (user, WRAP_TOCVS)) 313 { 314 error (0, 0, "\ 315the directory `%s' cannot be added because a file of the", user); 316 error (1, 0, "\ 317same name already exists in the repository."); 318 } 319 else 320 { 321 if (vers->nonbranch) 322 { 323 error (0, 0, 324 "cannot add file on non-branch tag %s", 325 vers->tag); 326 ++err; 327 } 328 else 329 { 330 if (vers->tag) 331 error (0, 0, "\ 332file `%s' will be added on branch `%s' from version %s", 333 user, vers->tag, vers->vn_rcs); 334 else 335 /* I'm not sure that mentioning 336 vers->vn_rcs makes any sense here; I 337 can't think of a way to word the 338 message which is not confusing. */ 339 error (0, 0, "\ 340re-adding file %s (in place of dead revision %s)", 341 user, vers->vn_rcs); 342 Register (entries, user, "0", vers->ts_user, NULL, 343 vers->tag, NULL, NULL); 344 ++added_files; 345 } 346 } 347 } 348 else 349 { 350 /* 351 * There is an RCS file already, so somebody else must've 352 * added it 353 */ 354 error (0, 0, "%s added independently by second party", user); 355 err++; 356 } 357 } 358 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 359 { 360 361 /* 362 * An entry for a new-born file, ts_rcs is dummy, but that is 363 * inappropriate here 364 */ 365 error (0, 0, "%s has already been entered", user); 366 err++; 367 } 368 else if (vers->vn_user[0] == '-') 369 { 370 /* An entry for a removed file, ts_rcs is invalid */ 371 if (vers->ts_user == NULL) 372 { 373 /* There is no user file (as it should be) */ 374 if (vers->vn_rcs == NULL) 375 { 376 377 /* 378 * There is no RCS file, so somebody else must've removed 379 * it from under us 380 */ 381 error (0, 0, "\ 382cannot resurrect %s; RCS file removed by second party", user); 383 err++; 384 } 385 else 386 { 387 388 /* 389 * There is an RCS file, so remove the "-" from the 390 * version number and restore the file 391 */ 392 char *tmp = xmalloc (strlen (user) + 50); 393 394 (void) strcpy (tmp, vers->vn_user + 1); 395 (void) strcpy (vers->vn_user, tmp); 396 (void) sprintf (tmp, "Resurrected %s", user); 397 Register (entries, user, vers->vn_user, tmp, vers->options, 398 vers->tag, vers->date, vers->ts_conflict); 399 free (tmp); 400 401 /* XXX - bugs here; this really resurrect the head */ 402 /* Note that this depends on the Register above actually 403 having written Entries, or else it won't really 404 check the file out. */ 405 if (update (2, argv + i - 1) == 0) 406 { 407 error (0, 0, "%s, version %s, resurrected", user, 408 vers->vn_user); 409 } 410 else 411 { 412 error (0, 0, "could not resurrect %s", user); 413 err++; 414 } 415 } 416 } 417 else 418 { 419 /* The user file shouldn't be there */ 420 error (0, 0, "\ 421%s should be removed and is still there (or is back again)", user); 422 err++; 423 } 424 } 425 else 426 { 427 /* A normal entry, ts_rcs is valid, so it must already be there */ 428 error (0, 0, "%s already exists, with version number %s", user, 429 vers->vn_user); 430 err++; 431 } 432 freevers_ts (&vers); 433 434 /* passed all the checks. Go ahead and add it if its a directory */ 435 if (begin_err == err 436 && isdir (user) 437 && !wrap_name_has (user, WRAP_TOCVS)) 438 { 439 err += add_directory (repository, entries, user); 440 continue; 441 } 442#ifdef SERVER_SUPPORT 443 if (server_active && begin_added_files != added_files) 444 server_checked_in (user, ".", repository); 445#endif 446 } 447 if (added_files) 448 error (0, 0, "use 'cvs commit' to add %s permanently", 449 (added_files == 1) ? "this file" : "these files"); 450 451 Entries_Close (entries); 452 453 if (message) 454 free (message); 455 free (repository); 456 457 return (err); 458} 459 460/* 461 * The specified user file is really a directory. So, let's make sure that 462 * it is created in the RCS source repository, and that the user's directory 463 * is updated to include a CVS directory. 464 * 465 * Returns 1 on failure, 0 on success. 466 */ 467static int 468add_directory (repository, entries, dir) 469 char *repository; 470 List *entries; 471 char *dir; 472{ 473 char *rcsdir = NULL; 474 struct saved_cwd cwd; 475 char *message = NULL; 476 char *tag, *date; 477 int nonbranch; 478 479 if (strchr (dir, '/') != NULL) 480 { 481 error (0, 0, 482 "directory %s not added; must be a direct sub-directory", dir); 483 return (1); 484 } 485 if (strcmp (dir, CVSADM) == 0) 486 { 487 error (0, 0, "cannot add a `%s' directory", CVSADM); 488 return (1); 489 } 490 491 /* before we do anything else, see if we have any per-directory tags */ 492 ParseTag (&tag, &date, &nonbranch); 493 494 /* now, remember where we were, so we can get back */ 495 if (save_cwd (&cwd)) 496 return (1); 497 if ( CVS_CHDIR (dir) < 0) 498 { 499 error (0, errno, "cannot chdir to %s", dir); 500 return (1); 501 } 502#ifdef SERVER_SUPPORT 503 if (!server_active && isfile (CVSADM)) 504#else 505 if (isfile (CVSADM)) 506#endif 507 { 508 error (0, 0, "%s/%s already exists", dir, CVSADM); 509 goto out; 510 } 511 512 rcsdir = combine_dir (repository, dir); 513 if (isfile (rcsdir) && !isdir (rcsdir)) 514 { 515 error (0, 0, "%s is not a directory; %s not added", rcsdir, dir); 516 goto out; 517 } 518 519 /* setup the log message */ 520 message = xmalloc (strlen (rcsdir) + 80); 521 (void) sprintf (message, "Directory %s added to the repository\n", rcsdir); 522 if (tag) 523 { 524 (void) strcat (message, "--> Using per-directory sticky tag `"); 525 (void) strcat (message, tag); 526 (void) strcat (message, "'\n"); 527 } 528 if (date) 529 { 530 (void) strcat (message, "--> Using per-directory sticky date `"); 531 (void) strcat (message, date); 532 (void) strcat (message, "'\n"); 533 } 534 535 if (!isdir (rcsdir)) 536 { 537 mode_t omask; 538 Node *p; 539 List *ulist; 540 struct logfile_info *li; 541 542 /* There used to be some code here which would prompt for 543 whether to add the directory. The details of that code had 544 bitrotted, but more to the point it can't work 545 client/server, doesn't ask in the right way for GUIs, etc. 546 A better way of making it harder to accidentally add 547 directories would be to have to add and commit directories 548 like for files. The code was #if 0'd at least since CVS 1.5. */ 549 550 if (!noexec) 551 { 552 omask = umask (cvsumask); 553 if (CVS_MKDIR (rcsdir, 0777) < 0) 554 { 555 error (0, errno, "cannot mkdir %s", rcsdir); 556 (void) umask (omask); 557 goto out; 558 } 559 (void) umask (omask); 560 } 561 562 /* 563 * Set up an update list with a single title node for Update_Logfile 564 */ 565 ulist = getlist (); 566 p = getnode (); 567 p->type = UPDATE; 568 p->delproc = update_delproc; 569 p->key = xstrdup ("- New directory"); 570 li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); 571 li->type = T_TITLE; 572 li->tag = xstrdup (tag); 573 li->rev_old = li->rev_new = NULL; 574 p->data = (char *) li; 575 (void) addnode (ulist, p); 576 Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); 577 dellist (&ulist); 578 } 579 580#ifdef SERVER_SUPPORT 581 if (!server_active) 582 Create_Admin (".", dir, rcsdir, tag, date, nonbranch); 583#else 584 Create_Admin (".", dir, rcsdir, tag, date, nonbranch); 585#endif 586 if (tag) 587 free (tag); 588 if (date) 589 free (date); 590 591 if (restore_cwd (&cwd, NULL)) 592 error_exit (); 593 free_cwd (&cwd); 594 595 Subdir_Register (entries, (char *) NULL, dir); 596 597 (void) printf ("%s", message); 598 free (rcsdir); 599 free (message); 600 601 return (0); 602 603out: 604 if (restore_cwd (&cwd, NULL)) 605 error_exit (); 606 free_cwd (&cwd); 607 if (rcsdir != NULL) 608 free (rcsdir); 609 return (0); 610} 611 612/* 613 * Builds an entry for a new file and sets up "CVS/file",[pt] by 614 * interrogating the user. Returns non-zero on error. 615 */ 616static int 617build_entry (repository, user, options, message, entries, tag) 618 char *repository; 619 char *user; 620 char *options; 621 char *message; 622 List *entries; 623 char *tag; 624{ 625 char *fname; 626 char *line; 627 FILE *fp; 628 629 if (noexec) 630 return (0); 631 632 /* 633 * The requested log is read directly from the user and stored in the 634 * file user,t. If the "message" argument is set, use it as the 635 * initial creation log (which typically describes the file). 636 */ 637 fname = xmalloc (strlen (user) + 80); 638 (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG); 639 fp = open_file (fname, "w+"); 640 if (message && fputs (message, fp) == EOF) 641 error (1, errno, "cannot write to %s", fname); 642 if (fclose(fp) == EOF) 643 error(1, errno, "cannot close %s", fname); 644 free (fname); 645 646 /* 647 * Create the entry now, since this allows the user to interrupt us above 648 * without needing to clean anything up (well, we could clean up the 649 * ,t file, but who cares). 650 */ 651 line = xmalloc (strlen (user) + 20); 652 (void) sprintf (line, "Initial %s", user); 653 Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0); 654 free (line); 655 return (0); 656} 657