1 /* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (c) 1992, Brian Berliner and Jeff Polk 8 * Portions Copyright (c) 1989-1992, Brian Berliner 9 * 10 * You may distribute under the terms of the GNU General Public License as 11 * specified in the README file that comes with the CVS source distribution. 12 * 13 * Add 14 * 15 * Adds a file or directory to the RCS source repository. For a file, 16 * the entry is marked as "needing to be added" in the user's own CVS 17 * directory, and really added to the repository when it is committed. 18 * For a directory, it is added at the appropriate place in the source 19 * repository and a CVS directory is generated within the directory. 20 * 21 * `cvs add' supports `-k <mode>' and `-m <description>' options. 22 * Some may wish to supply other standard "rcs" options here, but I've 23 * found that this causes more trouble than anything else. 24 * 25 * The user files or directories must already exist. For a directory, it must 26 * not already have a CVS file in it. 27 * 28 * An "add" on a file that has been "remove"d but not committed will cause the 29 * file to be resurrected. 30 */ 31 32 #include <assert.h> 33 #include "cvs.h" 34 #include "save-cwd.h" 35 #include "fileattr.h" 36 37 static int add_directory (struct file_info *finfo); 38 static int build_entry (const char *repository, const char *user, 39 const char *options, const char *message, 40 List * entries, const char *tag); 41 42 static const char *const add_usage[] = 43 { 44 "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", 45 "\t-k rcs-kflag\tUse \"rcs-kflag\" to add the file with the specified\n", 46 "\t\t\tkflag.\n", 47 "\t-m message\tUse \"message\" for the creation log.\n", 48 "(Specify the --help global option for a list of other help options)\n", 49 NULL 50 }; 51 52 int 53 add (int argc, char **argv) 54 { 55 char *message = NULL; 56 int i; 57 char *repository; 58 int c; 59 int err = 0; 60 int added_files = 0; 61 char *options = NULL; 62 List *entries; 63 Vers_TS *vers; 64 struct saved_cwd cwd; 65 /* Nonzero if we found a slash, and are thus adding files in a 66 subdirectory. */ 67 int found_slash = 0; 68 size_t cvsroot_len; 69 70 if (argc == 1 || argc == -1) 71 usage (add_usage); 72 73 wrap_setup (); 74 75 /* parse args */ 76 optind = 0; 77 while ((c = getopt (argc, argv, "+k:m:")) != -1) 78 { 79 switch (c) 80 { 81 case 'k': 82 if (options) free (options); 83 options = RCS_check_kflag (optarg); 84 break; 85 86 case 'm': 87 if (message) free (message); 88 message = xstrdup (optarg); 89 break; 90 case '?': 91 default: 92 usage (add_usage); 93 break; 94 } 95 } 96 argc -= optind; 97 argv += optind; 98 99 if (argc <= 0) 100 usage (add_usage); 101 102 cvsroot_len = strlen (current_parsed_root->directory); 103 104 /* First some sanity checks. I know that the CVS case is (sort of) 105 also handled by add_directory, but we need to check here so the 106 client won't get all confused in send_file_names. */ 107 for (i = 0; i < argc; i++) 108 { 109 int skip_file = 0; 110 111 /* If it were up to me I'd probably make this a fatal error. 112 But some people are really fond of their "cvs add *", and 113 don't seem to object to the warnings. 114 Whatever. */ 115 strip_trailing_slashes (argv[i]); 116 if (strcmp (argv[i], ".") == 0 117 || strcmp (argv[i], "..") == 0 118 || fncmp (argv[i], CVSADM) == 0) 119 { 120 if (!quiet) 121 error (0, 0, "cannot add special file `%s'; skipping", argv[i]); 122 skip_file = 1; 123 } 124 else 125 { 126 char *p; 127 p = argv[i]; 128 while (*p != '\0') 129 { 130 if (ISSLASH (*p)) 131 { 132 found_slash = 1; 133 break; 134 } 135 ++p; 136 } 137 } 138 139 if (skip_file) 140 { 141 int j; 142 143 /* FIXME: We don't do anything about free'ing argv[i]. But 144 the problem is that it is only sometimes allocated (see 145 cvsrc.c). */ 146 147 for (j = i; j < argc - 1; ++j) 148 argv[j] = argv[j + 1]; 149 --argc; 150 /* Check the new argv[i] again. */ 151 --i; 152 ++err; 153 } 154 } 155 156 #ifdef CLIENT_SUPPORT 157 if (current_parsed_root->isremote) 158 { 159 int j; 160 161 if (argc == 0) 162 /* We snipped out all the arguments in the above sanity 163 check. We can just forget the whole thing (and we 164 better, because if we fired up the server and passed it 165 nothing, it would spit back a usage message). */ 166 return err; 167 168 start_server (); 169 ign_setup (); 170 if (options) 171 { 172 send_arg (options); 173 free (options); 174 } 175 option_with_arg ("-m", message); 176 send_arg ("--"); 177 178 /* If !found_slash, refrain from sending "Directory", for 179 CVS 1.9 compatibility. If we only tried to deal with servers 180 which are at least CVS 1.9.26 or so, we wouldn't have to 181 special-case this. */ 182 if (found_slash) 183 { 184 repository = Name_Repository (NULL, NULL); 185 send_a_repository ("", repository, ""); 186 free (repository); 187 } 188 189 for (j = 0; j < argc; ++j) 190 { 191 /* FIXME: Does this erroneously call Create_Admin in error 192 conditions which are only detected once the server gets its 193 hands on things? */ 194 if (isdir (argv[j])) 195 { 196 char *tag; 197 char *date; 198 int nonbranch; 199 char *rcsdir; 200 char *p; 201 char *update_dir; 202 /* This is some mungeable storage into which we can point 203 with p and/or update_dir. */ 204 char *filedir; 205 206 if (save_cwd (&cwd)) 207 error (1, errno, "Failed to save current directory."); 208 209 filedir = xstrdup (argv[j]); 210 /* Deliberately discard the const below since we know we just 211 * allocated filedir and can do what we like with it. 212 */ 213 p = (char *)last_component (filedir); 214 if (p == filedir) 215 { 216 update_dir = ""; 217 } 218 else 219 { 220 p[-1] = '\0'; 221 update_dir = filedir; 222 if (CVS_CHDIR (update_dir) < 0) 223 error (1, errno, 224 "could not chdir to `%s'", update_dir); 225 } 226 227 /* find the repository associated with our current dir */ 228 repository = Name_Repository (NULL, update_dir); 229 230 /* don't add stuff to Emptydir */ 231 if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0 232 && ISSLASH (repository[cvsroot_len]) 233 && strncmp (repository + cvsroot_len + 1, 234 CVSROOTADM, 235 sizeof CVSROOTADM - 1) == 0 236 && ISSLASH (repository[cvsroot_len + sizeof CVSROOTADM]) 237 && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, 238 CVSNULLREPOS) == 0) 239 error (1, 0, "cannot add to `%s'", repository); 240 241 /* before we do anything else, see if we have any 242 per-directory tags */ 243 ParseTag (&tag, &date, &nonbranch); 244 245 rcsdir = Xasprintf ("%s/%s", repository, p); 246 247 Create_Admin (p, argv[j], rcsdir, tag, date, 248 nonbranch, 0, 1); 249 250 if (found_slash) 251 send_a_repository ("", repository, update_dir); 252 253 if (restore_cwd (&cwd)) 254 error (1, errno, 255 "Failed to restore current directory, `%s'.", 256 cwd.name); 257 free_cwd (&cwd); 258 259 if (tag) 260 free (tag); 261 if (date) 262 free (date); 263 free (rcsdir); 264 265 if (p == filedir) 266 Subdir_Register (NULL, NULL, argv[j]); 267 else 268 { 269 Subdir_Register (NULL, update_dir, p); 270 } 271 free (repository); 272 free (filedir); 273 } 274 } 275 send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS); 276 send_file_names (argc, argv, SEND_EXPAND_WILD); 277 send_to_server ("add\012", 0); 278 if (message) 279 free (message); 280 return err + get_responses_and_close (); 281 } 282 #endif 283 284 /* walk the arg list adding files/dirs */ 285 for (i = 0; i < argc; i++) 286 { 287 int begin_err = err; 288 #ifdef SERVER_SUPPORT 289 int begin_added_files = added_files; 290 #endif 291 struct file_info finfo; 292 char *filename, *p; 293 294 memset (&finfo, 0, sizeof finfo); 295 296 if (save_cwd (&cwd)) 297 error (1, errno, "Failed to save current directory."); 298 299 finfo.fullname = xstrdup (argv[i]); 300 filename = xstrdup (argv[i]); 301 /* We know we can discard the const below since we just allocated 302 * filename and can do as we like with it. 303 */ 304 p = (char *)last_component (filename); 305 if (p == filename) 306 { 307 finfo.update_dir = ""; 308 finfo.file = p; 309 } 310 else 311 { 312 p[-1] = '\0'; 313 finfo.update_dir = filename; 314 finfo.file = p; 315 if (CVS_CHDIR (finfo.update_dir) < 0) 316 error (1, errno, "could not chdir to `%s'", finfo.update_dir); 317 } 318 319 /* Add wrappers for this directory. They exist only until 320 the next call to wrap_add_file. */ 321 wrap_add_file (CVSDOTWRAPPER, 1); 322 323 finfo.rcs = NULL; 324 325 /* Find the repository associated with our current dir. */ 326 repository = Name_Repository (NULL, finfo.update_dir); 327 328 /* don't add stuff to Emptydir */ 329 if (strncmp (repository, current_parsed_root->directory, 330 cvsroot_len) == 0 331 && ISSLASH (repository[cvsroot_len]) 332 && strncmp (repository + cvsroot_len + 1, 333 CVSROOTADM, 334 sizeof CVSROOTADM - 1) == 0 335 && ISSLASH (repository[cvsroot_len + sizeof CVSROOTADM]) 336 && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1, 337 CVSNULLREPOS) == 0) 338 error (1, 0, "cannot add to `%s'", repository); 339 340 entries = Entries_Open (0, NULL); 341 342 finfo.repository = repository; 343 finfo.entries = entries; 344 345 /* We pass force_tag_match as 1. If the directory has a 346 sticky branch tag, and there is already an RCS file which 347 does not have that tag, then the head revision is 348 meaningless to us. */ 349 vers = Version_TS (&finfo, options, NULL, NULL, 1, 0); 350 if (vers->vn_user == NULL) 351 { 352 /* No entry available, ts_rcs is invalid */ 353 if (vers->vn_rcs == NULL) 354 { 355 /* There is no RCS file either */ 356 if (vers->ts_user == NULL) 357 { 358 /* There is no user file either */ 359 error (0, 0, "nothing known about `%s'", finfo.fullname); 360 err++; 361 } 362 else if (!isdir (finfo.file) 363 || wrap_name_has (finfo.file, WRAP_TOCVS)) 364 { 365 /* 366 * See if a directory exists in the repository with 367 * the same name. If so, blow this request off. 368 */ 369 char *dname = Xasprintf ("%s/%s", repository, finfo.file); 370 if (isdir (dname)) 371 { 372 error (0, 0, 373 "cannot add file `%s' since the directory", 374 finfo.fullname); 375 error (0, 0, "`%s' already exists in the repository", 376 dname); 377 error (1, 0, "invalid filename overlap"); 378 } 379 free (dname); 380 381 if (vers->options == NULL || *vers->options == '\0') 382 { 383 /* No options specified on command line (or in 384 rcs file if it existed, e.g. the file exists 385 on another branch). Check for a value from 386 the wrapper stuff. */ 387 if (wrap_name_has (finfo.file, WRAP_RCSOPTION)) 388 { 389 if (vers->options) 390 free (vers->options); 391 vers->options = wrap_rcsoption (finfo.file, 1); 392 } 393 } 394 395 if (vers->nonbranch) 396 { 397 error (0, 0, 398 "cannot add file on non-branch tag `%s'", 399 vers->tag); 400 ++err; 401 } 402 else 403 { 404 /* There is a user file, so build the entry for it */ 405 if (build_entry (repository, finfo.file, vers->options, 406 message, entries, vers->tag) != 0) 407 err++; 408 else 409 { 410 added_files++; 411 if (!quiet) 412 { 413 if (vers->tag) 414 error (0, 0, "scheduling %s `%s' for" 415 " addition on branch `%s'", 416 (wrap_name_has (finfo.file, 417 WRAP_TOCVS) 418 ? "wrapper" 419 : "file"), 420 finfo.fullname, vers->tag); 421 else 422 error (0, 0, 423 "scheduling %s `%s' for addition", 424 (wrap_name_has (finfo.file, 425 WRAP_TOCVS) 426 ? "wrapper" 427 : "file"), 428 finfo.fullname); 429 } 430 } 431 } 432 } 433 } 434 else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 435 { 436 if (isdir (finfo.file) 437 && !wrap_name_has (finfo.file, WRAP_TOCVS)) 438 { 439 error (0, 0, 440 "the directory `%s' cannot be added because a file" 441 " of the", finfo.fullname); 442 error (1, 0, "same name already exists in the repository."); 443 } 444 else 445 { 446 if (vers->nonbranch) 447 { 448 error (0, 0, 449 "cannot add file on non-branch tag `%s'", 450 vers->tag); 451 ++err; 452 } 453 else 454 { 455 char *timestamp = NULL; 456 if (vers->ts_user == NULL) 457 { 458 /* If this file does not exist locally, assume that 459 * the last version on the branch is being 460 * resurrected. 461 * 462 * Compute previous revision. We assume that it 463 * exists and that it is not a revision on the 464 * trunk of the form X.1 (1.1, 2.1, 3.1, ...). We 465 * also assume that it is not dead, which seems 466 * fair since we know vers->vn_rcs is dead 467 * and we shouldn't see two dead revisions in a 468 * row. 469 */ 470 char *prev = previous_rev (vers->srcfile, 471 vers->vn_rcs); 472 int status; 473 if (prev == NULL) 474 { 475 /* There is no previous revision. Either: 476 * 477 * * Revision 1.1 was dead, as when a file was 478 * inititially added on a branch, 479 * 480 * or 481 * 482 * * All previous revisions have been deleted. 483 * For instance, via `admin -o'. 484 */ 485 if (!really_quiet) 486 error (0, 0, 487 "File `%s' has no previous revision to resurrect.", 488 finfo.fullname); 489 free (prev); 490 goto skip_this_file; 491 } 492 if (!quiet) 493 error (0, 0, 494 "Resurrecting file `%s' from revision %s.", 495 finfo.fullname, prev); 496 status = RCS_checkout (vers->srcfile, finfo.file, 497 prev, vers->tag, 498 vers->options, RUN_TTY, 499 NULL, NULL); 500 xchmod (finfo.file, 1); 501 if (status != 0) 502 { 503 error (0, 0, "Failed to resurrect revision %s", 504 prev); 505 err++; 506 } 507 else 508 { 509 /* I don't actually set vers->ts_user here 510 * because it would confuse server_update(). 511 */ 512 timestamp = time_stamp (finfo.file); 513 if (!really_quiet) 514 write_letter (&finfo, 'U'); 515 } 516 free (prev); 517 } 518 if (!quiet) 519 { 520 char *bbuf; 521 if (vers->tag) 522 { 523 bbuf = Xasprintf (" on branch `%s'", 524 vers->tag); 525 } 526 else 527 bbuf = ""; 528 error (0, 0, 529 "Re-adding file `%s'%s after dead revision %s.", 530 finfo.fullname, bbuf, vers->vn_rcs); 531 if (vers->tag) 532 free (bbuf); 533 } 534 Register (entries, finfo.file, "0", 535 timestamp ? timestamp : vers->ts_user, 536 vers->options, vers->tag, vers->date, NULL); 537 if (timestamp) free (timestamp); 538 #ifdef SERVER_SUPPORT 539 if (server_active && vers->ts_user == NULL) 540 { 541 /* If we resurrected the file from the archive, we 542 * need to tell the client about it. 543 */ 544 server_updated (&finfo, vers, 545 SERVER_UPDATED, 546 (mode_t) -1, NULL, NULL); 547 /* This is kinda hacky or, at least, it renders the 548 * name "begin_added_files" obsolete, but we want 549 * the added_files to be counted without triggering 550 * the check that causes server_checked_in() to be 551 * called below since we have already called 552 * server_updated() to complete the resurrection. 553 */ 554 ++begin_added_files; 555 } 556 #endif 557 ++added_files; 558 } 559 } 560 } 561 else 562 { 563 /* 564 * There is an RCS file already, so somebody else must've 565 * added it 566 */ 567 error (0, 0, "`%s' added independently by second party", 568 finfo.fullname); 569 err++; 570 } 571 } 572 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 573 { 574 575 /* 576 * An entry for a new-born file, ts_rcs is dummy, but that is 577 * inappropriate here 578 */ 579 if (!quiet) 580 error (0, 0, "`%s' has already been entered", finfo.fullname); 581 err++; 582 } 583 else if (vers->vn_user[0] == '-') 584 { 585 /* An entry for a removed file, ts_rcs is invalid */ 586 if (vers->ts_user == NULL) 587 { 588 /* There is no user file (as it should be) */ 589 if (vers->vn_rcs == NULL) 590 { 591 592 /* 593 * There is no RCS file, so somebody else must've removed 594 * it from under us 595 */ 596 error (0, 0, 597 "cannot resurrect `%s'; RCS file removed by" 598 " second party", finfo.fullname); 599 err++; 600 } 601 else 602 { 603 int status; 604 /* 605 * There is an RCS file, so remove the "-" from the 606 * version number and restore the file 607 */ 608 char *tmp = xstrdup (vers->vn_user + 1); 609 (void) strcpy (vers->vn_user, tmp); 610 free (tmp); 611 status = RCS_checkout (vers->srcfile, finfo.file, 612 vers->vn_user, vers->tag, 613 vers->options, RUN_TTY, 614 NULL, NULL); 615 xchmod (finfo.file, cvswrite); 616 if (status != 0) 617 { 618 error (0, 0, "Failed to resurrect revision %s.", 619 vers->vn_user); 620 err++; 621 tmp = NULL; 622 } 623 else 624 { 625 /* I don't actually set vers->ts_user here because it 626 * would confuse server_update(). 627 */ 628 tmp = time_stamp (finfo.file); 629 write_letter (&finfo, 'U'); 630 if (!quiet) 631 error (0, 0, "`%s', version %s, resurrected", 632 finfo.fullname, vers->vn_user); 633 } 634 Register (entries, finfo.file, vers->vn_user, 635 tmp, vers->options, 636 vers->tag, vers->date, NULL); 637 if (tmp) free (tmp); 638 #ifdef SERVER_SUPPORT 639 if (server_active) 640 { 641 /* If we resurrected the file from the archive, we 642 * need to tell the client about it. 643 */ 644 server_updated (&finfo, vers, 645 SERVER_UPDATED, 646 (mode_t) -1, NULL, NULL); 647 } 648 /* We don't increment added_files here because this isn't 649 * a change that needs to be committed. 650 */ 651 #endif 652 } 653 } 654 else 655 { 656 /* The user file shouldn't be there */ 657 error (0, 0, "\ 658 `%s' should be removed and is still there (or is back again)", finfo.fullname); 659 err++; 660 } 661 } 662 else 663 { 664 /* A normal entry, ts_rcs is valid, so it must already be there */ 665 if (!quiet) 666 error (0, 0, "`%s' already exists, with version number %s", 667 finfo.fullname, 668 vers->vn_user); 669 err++; 670 } 671 freevers_ts (&vers); 672 673 /* passed all the checks. Go ahead and add it if its a directory */ 674 if (begin_err == err 675 && isdir (finfo.file) 676 && !wrap_name_has (finfo.file, WRAP_TOCVS)) 677 { 678 err += add_directory (&finfo); 679 } 680 else 681 { 682 #ifdef SERVER_SUPPORT 683 if (server_active && begin_added_files != added_files) 684 server_checked_in (finfo.file, finfo.update_dir, repository); 685 #endif 686 } 687 688 skip_this_file: 689 free (repository); 690 Entries_Close (entries); 691 692 if (restore_cwd (&cwd)) 693 error (1, errno, "Failed to restore current directory, `%s'.", 694 cwd.name); 695 free_cwd (&cwd); 696 697 /* It's okay to discard the const to free this - we allocated this 698 * above. The const is for everybody else. 699 */ 700 free ((char *) finfo.fullname); 701 free (filename); 702 } 703 if (added_files && !really_quiet) 704 error (0, 0, "use `%s commit' to add %s permanently", 705 program_name, 706 (added_files == 1) ? "this file" : "these files"); 707 708 if (message) 709 free (message); 710 if (options) 711 free (options); 712 713 return err; 714 } 715 716 717 718 /* 719 * The specified user file is really a directory. So, let's make sure that 720 * it is created in the RCS source repository, and that the user's directory 721 * is updated to include a CVS directory. 722 * 723 * Returns 1 on failure, 0 on success. 724 */ 725 static int 726 add_directory (struct file_info *finfo) 727 { 728 const char *repository = finfo->repository; 729 List *entries = finfo->entries; 730 const char *dir = finfo->file; 731 732 char *rcsdir = NULL; 733 struct saved_cwd cwd; 734 char *message = NULL; 735 char *tag, *date; 736 int nonbranch; 737 char *attrs; 738 739 if (strchr (dir, '/') != NULL) 740 { 741 /* "Can't happen". */ 742 error (0, 0, 743 "directory %s not added; must be a direct sub-directory", dir); 744 return 1; 745 } 746 if (fncmp (dir, CVSADM) == 0) 747 { 748 error (0, 0, "cannot add a `%s' directory", CVSADM); 749 return 1; 750 } 751 752 /* before we do anything else, see if we have any per-directory tags */ 753 ParseTag (&tag, &date, &nonbranch); 754 755 /* Remember the default attributes from this directory, so we can apply 756 them to the new directory. */ 757 fileattr_startdir (repository); 758 attrs = fileattr_getall (NULL); 759 fileattr_free (); 760 761 /* now, remember where we were, so we can get back */ 762 if (save_cwd (&cwd)) 763 { 764 error (0, errno, "Failed to save current directory."); 765 return 1; 766 } 767 if (CVS_CHDIR (dir) < 0) 768 { 769 error (0, errno, "cannot chdir to %s", finfo->fullname); 770 return 1; 771 } 772 if (!server_active && isfile (CVSADM)) 773 { 774 error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM); 775 goto out; 776 } 777 778 rcsdir = Xasprintf ("%s/%s", repository, dir); 779 if (isfile (rcsdir) && !isdir (rcsdir)) 780 { 781 error (0, 0, "%s is not a directory; %s not added", rcsdir, 782 finfo->fullname); 783 goto out; 784 } 785 786 /* setup the log message */ 787 message = Xasprintf ("Directory %s added to the repository\n%s%s%s%s%s%s", 788 rcsdir, 789 tag ? "--> Using per-directory sticky tag `" : "", 790 tag ? tag : "", tag ? "'\n" : "", 791 date ? "--> Using per-directory sticky date `" : "", 792 date ? date : "", date ? "'\n" : ""); 793 794 if (!isdir (rcsdir)) 795 { 796 mode_t omask; 797 Node *p; 798 List *ulist; 799 struct logfile_info *li; 800 801 /* There used to be some code here which would prompt for 802 whether to add the directory. The details of that code had 803 bitrotted, but more to the point it can't work 804 client/server, doesn't ask in the right way for GUIs, etc. 805 A better way of making it harder to accidentally add 806 directories would be to have to add and commit directories 807 like for files. The code was #if 0'd at least since CVS 1.5. */ 808 809 if (!noexec) 810 { 811 omask = umask (cvsumask); 812 if (CVS_MKDIR (rcsdir, 0777) < 0) 813 { 814 error (0, errno, "cannot mkdir %s", rcsdir); 815 (void) umask (omask); 816 goto out; 817 } 818 (void) umask (omask); 819 } 820 821 /* Now set the default file attributes to the ones we inherited 822 from the parent directory. */ 823 fileattr_startdir (rcsdir); 824 fileattr_setall (NULL, attrs); 825 fileattr_write (); 826 fileattr_free (); 827 if (attrs != NULL) 828 free (attrs); 829 830 /* 831 * Set up an update list with a single title node for Update_Logfile 832 */ 833 ulist = getlist (); 834 p = getnode (); 835 p->type = UPDATE; 836 p->delproc = update_delproc; 837 p->key = xstrdup ("- New directory"); 838 li = xmalloc (sizeof (struct logfile_info)); 839 li->type = T_TITLE; 840 li->tag = xstrdup (tag); 841 li->rev_old = li->rev_new = NULL; 842 p->data = li; 843 (void) addnode (ulist, p); 844 Update_Logfile (rcsdir, message, NULL, ulist); 845 dellist (&ulist); 846 } 847 848 if (server_active) 849 WriteTemplate (finfo->fullname, 1, rcsdir); 850 else 851 Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1); 852 853 if (tag) 854 free (tag); 855 if (date) 856 free (date); 857 858 if (restore_cwd (&cwd)) 859 error (1, errno, "Failed to restore current directory, `%s'.", 860 cwd.name); 861 free_cwd (&cwd); 862 863 Subdir_Register (entries, NULL, dir); 864 865 if (!really_quiet) 866 cvs_output (message, 0); 867 868 free (rcsdir); 869 free (message); 870 871 return 0; 872 873 out: 874 if (restore_cwd (&cwd)) 875 error (1, errno, "Failed to restore current directory, `%s'.", 876 cwd.name); 877 free_cwd (&cwd); 878 if (message) free (message); 879 if (rcsdir != NULL) 880 free (rcsdir); 881 return 0; 882 } 883 884 885 886 /* 887 * Builds an entry for a new file and sets up "CVS/file",[pt] by 888 * interrogating the user. Returns non-zero on error. 889 */ 890 static int 891 build_entry (const char *repository, const char *user, const char *options, 892 const char *message, List *entries, const char *tag) 893 { 894 char *fname; 895 char *line; 896 FILE *fp; 897 898 if (noexec) 899 return 0; 900 901 /* 902 * The requested log is read directly from the user and stored in the 903 * file user,t. If the "message" argument is set, use it as the 904 * initial creation log (which typically describes the file). 905 */ 906 fname = Xasprintf ("%s/%s%s", CVSADM, user, CVSEXT_LOG); 907 fp = xfopen (fname, "w+"); 908 if (message && fputs (message, fp) == EOF) 909 error (1, errno, "cannot write to %s", fname); 910 if (fclose (fp) == EOF) 911 error (1, errno, "cannot close %s", fname); 912 free (fname); 913 914 /* 915 * Create the entry now, since this allows the user to interrupt us above 916 * without needing to clean anything up (well, we could clean up the 917 * ,t file, but who cares). 918 */ 919 line = Xasprintf ("Initial %s", user); 920 Register (entries, user, "0", line, options, tag, NULL, NULL); 921 free (line); 922 return 0; 923 } 924