add.c revision 128266
162053Smarkm/* 262765Smarkm * Copyright (c) 1992, Brian Berliner and Jeff Polk 362053Smarkm * Copyright (c) 1989-1992, Brian Berliner 462053Smarkm * 562053Smarkm * You may distribute under the terms of the GNU General Public License as 662053Smarkm * specified in the README file that comes with the CVS source distribution. 762053Smarkm * 862053Smarkm * Add 962053Smarkm * 1062053Smarkm * Adds a file or directory to the RCS source repository. For a file, 1162053Smarkm * the entry is marked as "needing to be added" in the user's own CVS 1262053Smarkm * directory, and really added to the repository when it is committed. 1362053Smarkm * For a directory, it is added at the appropriate place in the source 1462053Smarkm * repository and a CVS directory is generated within the directory. 1562053Smarkm * 1662053Smarkm * The -m option is currently the only supported option. Some may wish to 1762053Smarkm * supply standard "rcs" options here, but I've found that this causes more 1862053Smarkm * trouble than anything else. 1962053Smarkm * 2062053Smarkm * The user files or directories must already exist. For a directory, it must 2162053Smarkm * not already have a CVS file in it. 2262053Smarkm * 2362053Smarkm * An "add" on a file that has been "remove"d but not committed will cause the 2462053Smarkm * file to be resurrected. 2562053Smarkm */ 2662053Smarkm 2762053Smarkm#include <assert.h> 2862053Smarkm#include "cvs.h" 2962053Smarkm#include "savecwd.h" 3062765Smarkm#include "fileattr.h" 3162053Smarkm 3262053Smarkmstatic int add_directory PROTO ((struct file_info *finfo)); 3362053Smarkmstatic int build_entry PROTO((const char *repository, const char *user, 3462053Smarkm const char *options, const char *message, 3562053Smarkm List * entries, const char *tag)); 3662053Smarkm 3762053Smarkmstatic const char *const add_usage[] = 3862053Smarkm{ 3967112Smarkm "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", 4066044Srwatson "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n", 4167112Smarkm "\t-m\tUse \"message\" for the creation log.\n", 4262053Smarkm "(Specify the --help global option for a list of other help options)\n", 4367112Smarkm NULL 4462053Smarkm}; 4562053Smarkm 4662053Smarkmint 4762053Smarkmadd (argc, argv) 4862053Smarkm int argc; 4967112Smarkm char **argv; 5067112Smarkm{ 5162053Smarkm char *message = NULL; 5263855Smarkm int i; 5362765Smarkm char *repository; 5462765Smarkm int c; 5565686Smarkm int err = 0; 5667112Smarkm int added_files = 0; 5762053Smarkm char *options = NULL; 5862053Smarkm List *entries; 5962053Smarkm Vers_TS *vers; 6062149Smarkm struct saved_cwd cwd; 6162053Smarkm /* Nonzero if we found a slash, and are thus adding files in a 6262053Smarkm subdirectory. */ 6363855Smarkm int found_slash = 0; 6462053Smarkm size_t cvsroot_len; 6562765Smarkm 6662765Smarkm if (argc == 1 || argc == -1) 6765686Smarkm usage (add_usage); 6867112Smarkm 6962053Smarkm wrap_setup (); 7062053Smarkm 7162053Smarkm /* parse args */ 7262053Smarkm optind = 0; 7362053Smarkm while ((c = getopt (argc, argv, "+k:m:")) != -1) 7462053Smarkm { 7562053Smarkm switch (c) 7662053Smarkm { 7762053Smarkm case 'k': 7862053Smarkm if (options) 7962053Smarkm free (options); 8062765Smarkm options = RCS_check_kflag (optarg); 8165686Smarkm break; 8262053Smarkm 8362053Smarkm case 'm': 8462053Smarkm message = xstrdup (optarg); 8562840Smarkm break; 8662840Smarkm case '?': 8762840Smarkm default: 8862840Smarkm usage (add_usage); 8962840Smarkm break; 9062840Smarkm } 9162840Smarkm } 9263855Smarkm argc -= optind; 9362840Smarkm argv += optind; 9462840Smarkm 9562053Smarkm if (argc <= 0) 9662053Smarkm usage (add_usage); 9763855Smarkm 9863855Smarkm cvsroot_len = strlen (current_parsed_root->directory); 9963855Smarkm 10063855Smarkm /* First some sanity checks. I know that the CVS case is (sort of) 10163855Smarkm also handled by add_directory, but we need to check here so the 10263855Smarkm client won't get all confused in send_file_names. */ 10363855Smarkm for (i = 0; i < argc; i++) 10463855Smarkm { 10563855Smarkm int skip_file = 0; 10662765Smarkm 10762053Smarkm /* If it were up to me I'd probably make this a fatal error. 10862053Smarkm But some people are really fond of their "cvs add *", and 10962053Smarkm don't seem to object to the warnings. 11062840Smarkm Whatever. */ 11162053Smarkm strip_trailing_slashes (argv[i]); 11267112Smarkm if (strcmp (argv[i], ".") == 0 11367112Smarkm || strcmp (argv[i], "..") == 0 11462053Smarkm || fncmp (argv[i], CVSADM) == 0) 11567112Smarkm { 11667112Smarkm if (!quiet) 11767112Smarkm error (0, 0, "cannot add special file `%s'; skipping", argv[i]); 11867112Smarkm skip_file = 1; 11967112Smarkm } 12067112Smarkm else 12167112Smarkm { 12267112Smarkm char *p; 12367112Smarkm p = argv[i]; 12467112Smarkm while (*p != '\0') 12567112Smarkm { 12667112Smarkm if (ISDIRSEP (*p)) 12767112Smarkm { 12862053Smarkm found_slash = 1; 12962053Smarkm break; 13062053Smarkm } 13162053Smarkm ++p; 13262765Smarkm } 13362053Smarkm } 13462053Smarkm 13562053Smarkm if (skip_file) 13662840Smarkm { 13762053Smarkm int j; 13862765Smarkm 13962053Smarkm /* FIXME: We don't do anything about free'ing argv[i]. But 14062053Smarkm the problem is that it is only sometimes allocated (see 14162765Smarkm cvsrc.c). */ 14262053Smarkm 14362053Smarkm for (j = i; j < argc - 1; ++j) 14463306Smarkm argv[j] = argv[j + 1]; 14562053Smarkm --argc; 14662765Smarkm /* Check the new argv[i] again. */ 14762053Smarkm --i; 14862053Smarkm ++err; 14962053Smarkm } 15062053Smarkm } 15165686Smarkm 15265686Smarkm#ifdef CLIENT_SUPPORT 15365686Smarkm if (current_parsed_root->isremote) 15465686Smarkm { 15565686Smarkm int j; 15665686Smarkm 15767112Smarkm if (argc == 0) 15867112Smarkm /* We snipped out all the arguments in the above sanity 15967112Smarkm check. We can just forget the whole thing (and we 16067112Smarkm better, because if we fired up the server and passed it 16167112Smarkm nothing, it would spit back a usage message). */ 16267112Smarkm return err; 16367112Smarkm 16467112Smarkm start_server (); 16567112Smarkm ign_setup (); 16667112Smarkm if (options) 16767112Smarkm { 16867112Smarkm send_arg (options); 16967112Smarkm free (options); 17067112Smarkm } 17167112Smarkm option_with_arg ("-m", message); 17262053Smarkm send_arg ("--"); 17362053Smarkm 17465686Smarkm /* If !found_slash, refrain from sending "Directory", for 17565686Smarkm CVS 1.9 compatibility. If we only tried to deal with servers 17662053Smarkm which are at least CVS 1.9.26 or so, we wouldn't have to 17762053Smarkm special-case this. */ 17865686Smarkm if (found_slash) 17965686Smarkm { 18065686Smarkm repository = Name_Repository (NULL, NULL); 18162053Smarkm send_a_repository ("", repository, ""); 18262053Smarkm free (repository); 18362765Smarkm } 18462149Smarkm 18562765Smarkm for (j = 0; j < argc; ++j) 18665686Smarkm { 18762053Smarkm /* FIXME: Does this erroneously call Create_Admin in error 18862053Smarkm conditions which are only detected once the server gets its 18962053Smarkm hands on things? */ 19062765Smarkm /* FIXME-also: if filenames are case-insensitive on the 19162765Smarkm client, and the directory in the repository already 19265686Smarkm exists and is named "foo", and the command is "cvs add 19362053Smarkm FOO", this call to Create_Admin puts the wrong thing in 19462053Smarkm CVS/Repository and so a subsequent "cvs update" will 19562053Smarkm give an error. The fix will be to have the server report 19662053Smarkm back what it actually did (e.g. use tagged text for the 19762053Smarkm "Directory %s added" message), and then Create_Admin, 19862053Smarkm which should also fix the error handling concerns. */ 19962053Smarkm 20062053Smarkm if (isdir (argv[j])) 20162053Smarkm { 20262053Smarkm char *tag; 20362053Smarkm char *date; 204 int nonbranch; 205 char *rcsdir; 206 char *p; 207 char *update_dir; 208 /* This is some mungeable storage into which we can point 209 with p and/or update_dir. */ 210 char *filedir; 211 212 if (save_cwd (&cwd)) 213 error_exit (); 214 215 filedir = xstrdup (argv[j]); 216 /* Deliberately discard the const below since we know we just 217 * allocated filedir and can do what we like with it. 218 */ 219 p = (char *)last_component (filedir); 220 if (p == filedir) 221 { 222 update_dir = ""; 223 } 224 else 225 { 226 p[-1] = '\0'; 227 update_dir = filedir; 228 if (CVS_CHDIR (update_dir) < 0) 229 error (1, errno, 230 "could not chdir to %s", update_dir); 231 } 232 233 /* find the repository associated with our current dir */ 234 repository = Name_Repository (NULL, update_dir); 235 236 /* don't add stuff to Emptydir */ 237 if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0 238 && ISDIRSEP (repository[cvsroot_len]) 239 && strncmp (repository + cvsroot_len + 1, 240 CVSROOTADM, 241 sizeof CVSROOTADM - 1) == 0 242 && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM]) 243 && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, 244 CVSNULLREPOS) == 0) 245 error (1, 0, "cannot add to %s", repository); 246 247 /* before we do anything else, see if we have any 248 per-directory tags */ 249 ParseTag (&tag, &date, &nonbranch); 250 251 rcsdir = xmalloc (strlen (repository) + strlen (p) + 5); 252 sprintf (rcsdir, "%s/%s", repository, p); 253 254 Create_Admin (p, argv[j], rcsdir, tag, date, 255 nonbranch, 0, 1); 256 257 if (found_slash) 258 send_a_repository ("", repository, update_dir); 259 260 if (restore_cwd (&cwd, NULL)) 261 error_exit (); 262 free_cwd (&cwd); 263 264 if (tag) 265 free (tag); 266 if (date) 267 free (date); 268 free (rcsdir); 269 270 if (p == filedir) 271 Subdir_Register ((List *) NULL, (char *) NULL, argv[j]); 272 else 273 { 274 Subdir_Register ((List *) NULL, update_dir, p); 275 } 276 free (repository); 277 free (filedir); 278 } 279 } 280 send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS); 281 send_file_names (argc, argv, SEND_EXPAND_WILD); 282 send_to_server ("add\012", 0); 283 if (message) 284 free (message); 285 return err + get_responses_and_close (); 286 } 287#endif 288 289 /* walk the arg list adding files/dirs */ 290 for (i = 0; i < argc; i++) 291 { 292 int begin_err = err; 293#ifdef SERVER_SUPPORT 294 int begin_added_files = added_files; 295#endif 296 struct file_info finfo; 297 char *filename, *p; 298 299 memset (&finfo, 0, sizeof finfo); 300 301 if (save_cwd (&cwd)) 302 error_exit (); 303 304 finfo.fullname = xstrdup (argv[i]); 305 filename = xstrdup (argv[i]); 306 /* We know we can discard the const below since we just allocated 307 * filename and can do as we like with it. 308 */ 309 p = (char *)last_component (filename); 310 if (p == filename) 311 { 312 finfo.update_dir = ""; 313 finfo.file = p; 314 } 315 else 316 { 317 p[-1] = '\0'; 318 finfo.update_dir = filename; 319 finfo.file = p; 320 if (CVS_CHDIR (finfo.update_dir) < 0) 321 error (1, errno, "could not chdir to %s", finfo.update_dir); 322 } 323 324 /* Add wrappers for this directory. They exist only until 325 the next call to wrap_add_file. */ 326 wrap_add_file (CVSDOTWRAPPER, 1); 327 328 finfo.rcs = NULL; 329 330 /* Find the repository associated with our current dir. */ 331 repository = Name_Repository (NULL, finfo.update_dir); 332 333 /* don't add stuff to Emptydir */ 334 if (strncmp (repository, current_parsed_root->directory, 335 cvsroot_len) == 0 336 && ISDIRSEP (repository[cvsroot_len]) 337 && strncmp (repository + cvsroot_len + 1, 338 CVSROOTADM, 339 sizeof CVSROOTADM - 1) == 0 340 && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM]) 341 && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, 342 CVSNULLREPOS) == 0) 343 error (1, 0, "cannot add to %s", repository); 344 345 entries = Entries_Open (0, NULL); 346 347 finfo.repository = repository; 348 finfo.entries = entries; 349 350 /* We pass force_tag_match as 1. If the directory has a 351 sticky branch tag, and there is already an RCS file which 352 does not have that tag, then the head revision is 353 meaningless to us. */ 354 vers = Version_TS (&finfo, options, NULL, NULL, 1, 0); 355 356 if (vers->vn_user == NULL) 357 { 358 /* No entry available, ts_rcs is invalid */ 359 if (vers->vn_rcs == NULL) 360 { 361 /* There is no RCS file either */ 362 if (vers->ts_user == NULL) 363 { 364 /* There is no user file either */ 365 error (0, 0, "nothing known about %s", finfo.fullname); 366 err++; 367 } 368 else if (!isdir (finfo.file) 369 || wrap_name_has (finfo.file, WRAP_TOCVS)) 370 { 371 /* 372 * See if a directory exists in the repository with 373 * the same name. If so, blow this request off. 374 */ 375 char *dname = xmalloc (strlen (repository) 376 + strlen (finfo.file) 377 + 10); 378 (void) sprintf (dname, "%s/%s", repository, finfo.file); 379 if (isdir (dname)) 380 { 381 error (0, 0, 382 "cannot add file `%s' since the directory", 383 finfo.fullname); 384 error (0, 0, "`%s' already exists in the repository", 385 dname); 386 error (1, 0, "illegal filename overlap"); 387 } 388 free (dname); 389 390 if (vers->options == NULL || *vers->options == '\0') 391 { 392 /* No options specified on command line (or in 393 rcs file if it existed, e.g. the file exists 394 on another branch). Check for a value from 395 the wrapper stuff. */ 396 if (wrap_name_has (finfo.file, WRAP_RCSOPTION)) 397 { 398 if (vers->options) 399 free (vers->options); 400 vers->options = wrap_rcsoption (finfo.file, 1); 401 } 402 } 403 404 if (vers->nonbranch) 405 { 406 error (0, 0, 407 "cannot add file on non-branch tag %s", 408 vers->tag); 409 ++err; 410 } 411 else 412 { 413 /* There is a user file, so build the entry for it */ 414 if (build_entry (repository, finfo.file, vers->options, 415 message, entries, vers->tag) != 0) 416 err++; 417 else 418 { 419 added_files++; 420 if (!quiet) 421 { 422 if (vers->tag) 423 error (0, 0, "\ 424scheduling %s `%s' for addition on branch `%s'", 425 (wrap_name_has (finfo.file, 426 WRAP_TOCVS) 427 ? "wrapper" 428 : "file"), 429 finfo.fullname, vers->tag); 430 else 431 error (0, 0, 432 "scheduling %s `%s' for addition", 433 (wrap_name_has (finfo.file, 434 WRAP_TOCVS) 435 ? "wrapper" 436 : "file"), 437 finfo.fullname); 438 } 439 } 440 } 441 } 442 } 443 else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 444 { 445 if (isdir (finfo.file) 446 && !wrap_name_has (finfo.file, WRAP_TOCVS)) 447 { 448 error (0, 0, "\ 449the directory `%s' cannot be added because a file of the", finfo.fullname); 450 error (1, 0, "\ 451same name already exists in the repository."); 452 } 453 else 454 { 455 if (vers->nonbranch) 456 { 457 error (0, 0, 458 "cannot add file on non-branch tag %s", 459 vers->tag); 460 ++err; 461 } 462 else 463 { 464 char *timestamp = NULL; 465 if (vers->ts_user == NULL) 466 { 467 /* If this file does not exist locally, assume that 468 * the last version on the branch is being 469 * resurrected. 470 * 471 * Compute previous revision. We assume that it 472 * exists and that it is not a revision on the 473 * trunk of the form X.1 (1.1, 2.1, 3.1, ...). We 474 * also assume that it is not dead, which seems 475 * fair since we know vers->vn_rcs is dead 476 * and we shouldn't see two dead revisions in a 477 * row. 478 */ 479 char *prev = previous_rev (vers->srcfile, 480 vers->vn_rcs); 481 int status; 482 assert (prev != NULL); 483 if (!quiet) 484 error (0, 0, 485"Resurrecting file `%s' from revision %s.", 486 finfo.fullname, prev); 487 status = RCS_checkout (vers->srcfile, finfo.file, 488 prev, vers->tag, 489 vers->options, RUN_TTY, 490 NULL, NULL); 491 xchmod (finfo.file, 1); 492 if (status != 0) 493 { 494 error (0, 0, "Failed to resurrect revision %s", 495 prev); 496 err++; 497 } 498 else 499 { 500 /* I don't actually set vers->ts_user here 501 * because it would confuse server_update(). 502 */ 503 timestamp = time_stamp (finfo.file); 504 if (!really_quiet) 505 write_letter (&finfo, 'U'); 506 } 507 free (prev); 508 } 509 if (!quiet) 510 { 511 if (vers->tag) 512 error (0, 0, 513"file `%s' will be added on branch `%s' from version %s", 514 finfo.fullname, vers->tag, 515 vers->vn_rcs); 516 else 517 /* I'm not sure that mentioning 518 vers->vn_rcs makes any sense here; I 519 can't think of a way to word the 520 message which is not confusing. */ 521 error (0, 0, 522"Re-adding file `%s' (in place of dead revision %s).", 523 finfo.fullname, vers->vn_rcs); 524 } 525 Register (entries, finfo.file, "0", 526 timestamp ? timestamp : vers->ts_user, 527 vers->options, vers->tag, vers->date, NULL); 528 if (timestamp) free (timestamp); 529#ifdef SERVER_SUPPORT 530 if (server_active && vers->ts_user == NULL) 531 { 532 /* If we resurrected the file from the archive, we 533 * need to tell the client about it. 534 */ 535 server_updated (&finfo, vers, 536 SERVER_UPDATED, 537 (mode_t) -1, NULL, NULL); 538 /* This is kinda hacky or, at least, it renders the 539 * name "begin_added_files" obsolete, but we want 540 * the added_files to be counted without triggering 541 * the check that causes server_checked_in() to be 542 * called below since we have already called 543 * server_updated() to complete the resurrection. 544 */ 545 ++begin_added_files; 546 } 547#endif 548 ++added_files; 549 } 550 } 551 } 552 else 553 { 554 /* 555 * There is an RCS file already, so somebody else must've 556 * added it 557 */ 558 error (0, 0, "%s added independently by second party", 559 finfo.fullname); 560 err++; 561 } 562 } 563 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 564 { 565 566 /* 567 * An entry for a new-born file, ts_rcs is dummy, but that is 568 * inappropriate here 569 */ 570 if (!quiet) 571 error (0, 0, "%s has already been entered", finfo.fullname); 572 err++; 573 } 574 else if (vers->vn_user[0] == '-') 575 { 576 /* An entry for a removed file, ts_rcs is invalid */ 577 if (vers->ts_user == NULL) 578 { 579 /* There is no user file (as it should be) */ 580 if (vers->vn_rcs == NULL) 581 { 582 583 /* 584 * There is no RCS file, so somebody else must've removed 585 * it from under us 586 */ 587 error (0, 0, "\ 588cannot resurrect %s; RCS file removed by second party", finfo.fullname); 589 err++; 590 } 591 else 592 { 593 int status; 594 /* 595 * There is an RCS file, so remove the "-" from the 596 * version number and restore the file 597 */ 598 char *tmp = xmalloc (strlen (vers->vn_user)); 599 (void) strcpy (tmp, vers->vn_user + 1); 600 (void) strcpy (vers->vn_user, tmp); 601 free(tmp); 602 status = RCS_checkout (vers->srcfile, finfo.file, 603 vers->vn_user, vers->tag, 604 vers->options, RUN_TTY, 605 NULL, NULL); 606 xchmod (finfo.file, 1); 607 if (status != 0) 608 { 609 error (0, 0, "Failed to resurrect revision %s", 610 vers->vn_user); 611 err++; 612 tmp = NULL; 613 } 614 else 615 { 616 /* I don't actually set vers->ts_user here because it 617 * would confuse server_update(). 618 */ 619 tmp = time_stamp (finfo.file); 620 write_letter (&finfo, 'U'); 621 if (!quiet) 622 error (0, 0, "%s, version %s, resurrected", 623 finfo.fullname, vers->vn_user); 624 } 625 Register (entries, finfo.file, vers->vn_user, 626 tmp, vers->options, 627 vers->tag, vers->date, NULL); 628 if (tmp) free (tmp); 629#ifdef SERVER_SUPPORT 630 if (server_active) 631 { 632 /* If we resurrected the file from the archive, we 633 * need to tell the client about it. 634 */ 635 server_updated (&finfo, vers, 636 SERVER_UPDATED, 637 (mode_t) -1, NULL, NULL); 638 } 639 /* We don't increment added_files here because this isn't 640 * a change that needs to be committed. 641 */ 642#endif 643 } 644 } 645 else 646 { 647 /* The user file shouldn't be there */ 648 error (0, 0, "\ 649%s should be removed and is still there (or is back again)", finfo.fullname); 650 err++; 651 } 652 } 653 else 654 { 655 /* A normal entry, ts_rcs is valid, so it must already be there */ 656 if (!quiet) 657 error (0, 0, "%s already exists, with version number %s", 658 finfo.fullname, 659 vers->vn_user); 660 err++; 661 } 662 freevers_ts (&vers); 663 664 /* passed all the checks. Go ahead and add it if its a directory */ 665 if (begin_err == err 666 && isdir (finfo.file) 667 && !wrap_name_has (finfo.file, WRAP_TOCVS)) 668 { 669 err += add_directory (&finfo); 670 } 671 else 672 { 673#ifdef SERVER_SUPPORT 674 if (server_active && begin_added_files != added_files) 675 server_checked_in (finfo.file, finfo.update_dir, repository); 676#endif 677 } 678 free (repository); 679 Entries_Close (entries); 680 681 if (restore_cwd (&cwd, NULL)) 682 error_exit (); 683 free_cwd (&cwd); 684 685 /* It's okay to discard the const to free this - we allocated this 686 * above. The const is for everybody else. 687 */ 688 free ((char *) finfo.fullname); 689 free ((char *) filename); 690 } 691 if (added_files && !really_quiet) 692 error (0, 0, "use '%s commit' to add %s permanently", 693 program_name, 694 (added_files == 1) ? "this file" : "these files"); 695 696 if (message) 697 free (message); 698 if (options) 699 free (options); 700 701 return (err); 702} 703 704/* 705 * The specified user file is really a directory. So, let's make sure that 706 * it is created in the RCS source repository, and that the user's directory 707 * is updated to include a CVS directory. 708 * 709 * Returns 1 on failure, 0 on success. 710 */ 711static int 712add_directory (finfo) 713 struct file_info *finfo; 714{ 715 const char *repository = finfo->repository; 716 List *entries = finfo->entries; 717 const char *dir = finfo->file; 718 719 char *rcsdir = NULL; 720 struct saved_cwd cwd; 721 char *message = NULL; 722 char *tag, *date; 723 int nonbranch; 724 char *attrs; 725 726 if (strchr (dir, '/') != NULL) 727 { 728 /* "Can't happen". */ 729 error (0, 0, 730 "directory %s not added; must be a direct sub-directory", dir); 731 return (1); 732 } 733 if (fncmp (dir, CVSADM) == 0) 734 { 735 error (0, 0, "cannot add a `%s' directory", CVSADM); 736 return (1); 737 } 738 739 /* before we do anything else, see if we have any per-directory tags */ 740 ParseTag (&tag, &date, &nonbranch); 741 742 /* Remember the default attributes from this directory, so we can apply 743 them to the new directory. */ 744 fileattr_startdir (repository); 745 attrs = fileattr_getall (NULL); 746 fileattr_free (); 747 748 /* now, remember where we were, so we can get back */ 749 if (save_cwd (&cwd)) 750 return 1; 751 if (CVS_CHDIR (dir) < 0) 752 { 753 error (0, errno, "cannot chdir to %s", finfo->fullname); 754 return 1; 755 } 756#ifdef SERVER_SUPPORT 757 if (!server_active && isfile (CVSADM)) 758#else 759 if (isfile (CVSADM)) 760#endif 761 { 762 error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM); 763 goto out; 764 } 765 766 rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5); 767 sprintf (rcsdir, "%s/%s", repository, dir); 768 if (isfile (rcsdir) && !isdir (rcsdir)) 769 { 770 error (0, 0, "%s is not a directory; %s not added", rcsdir, 771 finfo->fullname); 772 goto out; 773 } 774 775 /* setup the log message */ 776 message = xmalloc (strlen (rcsdir) 777 + 80 778 + (tag == NULL ? 0 : strlen (tag) + 80) 779 + (date == NULL ? 0 : strlen (date) + 80)); 780 (void) sprintf (message, "Directory %s added to the repository\n", 781 rcsdir); 782 if (tag) 783 { 784 (void) strcat (message, "--> Using per-directory sticky tag `"); 785 (void) strcat (message, tag); 786 (void) strcat (message, "'\n"); 787 } 788 if (date) 789 { 790 (void) strcat (message, "--> Using per-directory sticky date `"); 791 (void) strcat (message, date); 792 (void) strcat (message, "'\n"); 793 } 794 795 if (!isdir (rcsdir)) 796 { 797 mode_t omask; 798 Node *p; 799 List *ulist; 800 struct logfile_info *li; 801 802 /* There used to be some code here which would prompt for 803 whether to add the directory. The details of that code had 804 bitrotted, but more to the point it can't work 805 client/server, doesn't ask in the right way for GUIs, etc. 806 A better way of making it harder to accidentally add 807 directories would be to have to add and commit directories 808 like for files. The code was #if 0'd at least since CVS 1.5. */ 809 810 if (!noexec) 811 { 812 omask = umask (cvsumask); 813 if (CVS_MKDIR (rcsdir, 0777) < 0) 814 { 815 error (0, errno, "cannot mkdir %s", rcsdir); 816 (void) umask (omask); 817 goto out; 818 } 819 (void) umask (omask); 820 } 821 822 /* Now set the default file attributes to the ones we inherited 823 from the parent directory. */ 824 fileattr_startdir (rcsdir); 825 fileattr_setall (NULL, attrs); 826 fileattr_write (); 827 fileattr_free (); 828 if (attrs != NULL) 829 free (attrs); 830 831 /* 832 * Set up an update list with a single title node for Update_Logfile 833 */ 834 ulist = getlist (); 835 p = getnode (); 836 p->type = UPDATE; 837 p->delproc = update_delproc; 838 p->key = xstrdup ("- New directory"); 839 li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); 840 li->type = T_TITLE; 841 li->tag = xstrdup (tag); 842 li->rev_old = li->rev_new = NULL; 843 p->data = li; 844 (void) addnode (ulist, p); 845 Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); 846 dellist (&ulist); 847 } 848 849#ifdef SERVER_SUPPORT 850 if (!server_active) 851#endif 852 Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1); 853 if (tag) 854 free (tag); 855 if (date) 856 free (date); 857 858 if (restore_cwd (&cwd, NULL)) 859 error_exit (); 860 free_cwd (&cwd); 861 862 Subdir_Register (entries, (char *) NULL, dir); 863 864 if (!really_quiet) 865 cvs_output (message, 0); 866 867 free (rcsdir); 868 free (message); 869 870 return 0; 871 872out: 873 if (restore_cwd (&cwd, NULL)) 874 error_exit (); 875 free_cwd (&cwd); 876 if (rcsdir != NULL) 877 free (rcsdir); 878 return (0); 879} 880 881 882 883/* 884 * Builds an entry for a new file and sets up "CVS/file",[pt] by 885 * interrogating the user. Returns non-zero on error. 886 */ 887static int 888build_entry (repository, user, options, message, entries, tag) 889 const char *repository; 890 const char *user; 891 const char *options; 892 const char *message; 893 List *entries; 894 const char *tag; 895{ 896 char *fname; 897 char *line; 898 FILE *fp; 899 900 if (noexec) 901 return (0); 902 903 /* 904 * The requested log is read directly from the user and stored in the 905 * file user,t. If the "message" argument is set, use it as the 906 * initial creation log (which typically describes the file). 907 */ 908 fname = xmalloc (strlen (user) + 80); 909 (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG); 910 fp = open_file (fname, "w+"); 911 if (message && fputs (message, fp) == EOF) 912 error (1, errno, "cannot write to %s", fname); 913 if (fclose(fp) == EOF) 914 error(1, errno, "cannot close %s", fname); 915 free (fname); 916 917 /* 918 * Create the entry now, since this allows the user to interrupt us above 919 * without needing to clean anything up (well, we could clean up the 920 * ,t file, but who cares). 921 */ 922 line = xmalloc (strlen (user) + 20); 923 (void) sprintf (line, "Initial %s", user); 924 Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0); 925 free (line); 926 return (0); 927} 928