add.c revision 54427
1169240Sjfv/* 2169240Sjfv * Copyright (c) 1992, Brian Berliner and Jeff Polk 3169240Sjfv * Copyright (c) 1989-1992, Brian Berliner 4169240Sjfv * 5169240Sjfv * You may distribute under the terms of the GNU General Public License as 6169240Sjfv * specified in the README file that comes with the CVS source distribution. 7169240Sjfv * 8169240Sjfv * Add 9169240Sjfv * 10169240Sjfv * Adds a file or directory to the RCS source repository. For a file, 11169240Sjfv * the entry is marked as "needing to be added" in the user's own CVS 12169240Sjfv * directory, and really added to the repository when it is committed. 13169240Sjfv * For a directory, it is added at the appropriate place in the source 14169240Sjfv * repository and a CVS directory is generated within the directory. 15169240Sjfv * 16169240Sjfv * The -m option is currently the only supported option. Some may wish to 17169240Sjfv * supply standard "rcs" options here, but I've found that this causes more 18169240Sjfv * trouble than anything else. 19169240Sjfv * 20169240Sjfv * The user files or directories must already exist. For a directory, it must 21169240Sjfv * not already have a CVS file in it. 22169240Sjfv * 23169240Sjfv * An "add" on a file that has been "remove"d but not committed will cause the 24169240Sjfv * file to be resurrected. 25169240Sjfv */ 26169240Sjfv 27169240Sjfv#include "cvs.h" 28169240Sjfv#include "savecwd.h" 29169240Sjfv#include "fileattr.h" 30169240Sjfv 31169240Sjfvstatic int add_directory PROTO ((struct file_info *finfo)); 32169240Sjfvstatic int build_entry PROTO((char *repository, char *user, char *options, 33169240Sjfv char *message, List * entries, char *tag)); 34169240Sjfv 35169240Sjfvstatic const char *const add_usage[] = 36169240Sjfv{ 37169240Sjfv "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", 38169240Sjfv "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n", 39169240Sjfv "\t-m\tUse \"message\" for the creation log.\n", 40169240Sjfv "(Specify the --help global option for a list of other help options)\n", 41169240Sjfv NULL 42169240Sjfv}; 43169240Sjfv 44169240Sjfvint 45169240Sjfvadd (argc, argv) 46169240Sjfv int argc; 47169240Sjfv char **argv; 48169240Sjfv{ 49169240Sjfv char *message = NULL; 50169240Sjfv int i; 51169240Sjfv char *repository; 52169240Sjfv int c; 53169240Sjfv int err = 0; 54169240Sjfv int added_files = 0; 55169240Sjfv char *options = NULL; 56169240Sjfv List *entries; 57169240Sjfv Vers_TS *vers; 58169240Sjfv struct saved_cwd cwd; 59169240Sjfv /* Nonzero if we found a slash, and are thus adding files in a 60169240Sjfv subdirectory. */ 61169240Sjfv int found_slash = 0; 62169240Sjfv 63169240Sjfv if (argc == 1 || argc == -1) 64169240Sjfv usage (add_usage); 65169240Sjfv 66169240Sjfv wrap_setup (); 67169240Sjfv 68169240Sjfv /* parse args */ 69169240Sjfv optind = 0; 70169240Sjfv while ((c = getopt (argc, argv, "+k:m:")) != -1) 71169240Sjfv { 72169240Sjfv switch (c) 73169240Sjfv { 74169240Sjfv case 'k': 75169240Sjfv if (options) 76169240Sjfv free (options); 77169240Sjfv options = RCS_check_kflag (optarg); 78169240Sjfv break; 79169240Sjfv 80169240Sjfv case 'm': 81169240Sjfv message = xstrdup (optarg); 82169240Sjfv break; 83169240Sjfv case '?': 84169240Sjfv default: 85169240Sjfv usage (add_usage); 86169240Sjfv break; 87169240Sjfv } 88169240Sjfv } 89169240Sjfv argc -= optind; 90169240Sjfv argv += optind; 91169240Sjfv 92169240Sjfv if (argc <= 0) 93169240Sjfv usage (add_usage); 94169240Sjfv 95169240Sjfv /* First some sanity checks. I know that the CVS case is (sort of) 96169240Sjfv also handled by add_directory, but we need to check here so the 97169240Sjfv client won't get all confused in send_file_names. */ 98169240Sjfv for (i = 0; i < argc; i++) 99169240Sjfv { 100169240Sjfv int skip_file = 0; 101169240Sjfv 102169240Sjfv /* If it were up to me I'd probably make this a fatal error. 103169240Sjfv But some people are really fond of their "cvs add *", and 104169240Sjfv don't seem to object to the warnings. 105169240Sjfv Whatever. */ 106169240Sjfv strip_trailing_slashes (argv[i]); 107169240Sjfv if (strcmp (argv[i], ".") == 0 108169240Sjfv || strcmp (argv[i], "..") == 0 109169240Sjfv || fncmp (argv[i], CVSADM) == 0) 110169240Sjfv { 111169240Sjfv error (0, 0, "cannot add special file `%s'; skipping", argv[i]); 112169240Sjfv skip_file = 1; 113169240Sjfv } 114169240Sjfv else 115169240Sjfv { 116169240Sjfv char *p; 117169240Sjfv p = argv[i]; 118169240Sjfv while (*p != '\0') 119169240Sjfv { 120169240Sjfv if (ISDIRSEP (*p)) 121169240Sjfv { 122169240Sjfv found_slash = 1; 123169240Sjfv break; 124169240Sjfv } 125169240Sjfv ++p; 126169240Sjfv } 127169240Sjfv } 128169240Sjfv 129169240Sjfv if (skip_file) 130169240Sjfv { 131169240Sjfv int j; 132169240Sjfv 133169240Sjfv /* FIXME: We don't do anything about free'ing argv[i]. But 134169240Sjfv the problem is that it is only sometimes allocated (see 135169240Sjfv cvsrc.c). */ 136169240Sjfv 137169240Sjfv for (j = i; j < argc - 1; ++j) 138169240Sjfv argv[j] = argv[j + 1]; 139169240Sjfv --argc; 140169240Sjfv /* Check the new argv[i] again. */ 141169240Sjfv --i; 142169240Sjfv ++err; 143169240Sjfv } 144169240Sjfv } 145169240Sjfv 146169240Sjfv#ifdef CLIENT_SUPPORT 147169240Sjfv if (client_active) 148169240Sjfv { 149169240Sjfv int i; 150169240Sjfv 151169240Sjfv if (argc == 0) 152169240Sjfv /* We snipped out all the arguments in the above sanity 153169240Sjfv check. We can just forget the whole thing (and we 154169240Sjfv better, because if we fired up the server and passed it 155169240Sjfv nothing, it would spit back a usage message). */ 156169240Sjfv return err; 157169240Sjfv 158169240Sjfv start_server (); 159169240Sjfv ign_setup (); 160169240Sjfv if (options) send_arg(options); 161169240Sjfv option_with_arg ("-m", message); 162169240Sjfv 163169240Sjfv /* If !found_slash, refrain from sending "Directory", for 164169240Sjfv CVS 1.9 compatibility. If we only tried to deal with servers 165169240Sjfv which are at least CVS 1.9.26 or so, we wouldn't have to 166169240Sjfv special-case this. */ 167169240Sjfv if (found_slash) 168169240Sjfv { 169169240Sjfv repository = Name_Repository (NULL, NULL); 170169240Sjfv send_a_repository ("", repository, ""); 171169240Sjfv free (repository); 172169240Sjfv } 173169240Sjfv 174169240Sjfv for (i = 0; i < argc; ++i) 175169240Sjfv { 176169240Sjfv /* FIXME: Does this erroneously call Create_Admin in error 177169240Sjfv conditions which are only detected once the server gets its 178169240Sjfv hands on things? */ 179169240Sjfv /* FIXME-also: if filenames are case-insensitive on the 180169240Sjfv client, and the directory in the repository already 181169240Sjfv exists and is named "foo", and the command is "cvs add 182169240Sjfv FOO", this call to Create_Admin puts the wrong thing in 183169240Sjfv CVS/Repository and so a subsequent "cvs update" will 184169240Sjfv give an error. The fix will be to have the server report 185169240Sjfv back what it actually did (e.g. use tagged text for the 186169240Sjfv "Directory %s added" message), and then Create_Admin, 187169240Sjfv which should also fix the error handling concerns. */ 188169240Sjfv 189169240Sjfv if (isdir (argv[i])) 190169240Sjfv { 191169240Sjfv char *tag; 192169240Sjfv char *date; 193169240Sjfv int nonbranch; 194169240Sjfv char *rcsdir; 195169240Sjfv char *p; 196169240Sjfv char *update_dir; 197169240Sjfv /* This is some mungeable storage into which we can point 198169240Sjfv with p and/or update_dir. */ 199169240Sjfv char *filedir; 200169240Sjfv 201169240Sjfv if (save_cwd (&cwd)) 202169240Sjfv error_exit (); 203169240Sjfv 204169240Sjfv filedir = xstrdup (argv[i]); 205169240Sjfv p = last_component (filedir); 206169240Sjfv if (p == filedir) 207169240Sjfv { 208169240Sjfv update_dir = ""; 209169240Sjfv } 210169240Sjfv else 211169240Sjfv { 212169240Sjfv p[-1] = '\0'; 213169240Sjfv update_dir = filedir; 214169240Sjfv if (CVS_CHDIR (update_dir) < 0) 215169240Sjfv error (1, errno, 216169240Sjfv "could not chdir to %s", update_dir); 217169240Sjfv } 218169240Sjfv 219169240Sjfv /* find the repository associated with our current dir */ 220169240Sjfv repository = Name_Repository (NULL, update_dir); 221169240Sjfv 222169240Sjfv /* before we do anything else, see if we have any 223169240Sjfv per-directory tags */ 224169240Sjfv ParseTag (&tag, &date, &nonbranch); 225169240Sjfv 226169240Sjfv rcsdir = xmalloc (strlen (repository) + strlen (p) + 5); 227169240Sjfv sprintf (rcsdir, "%s/%s", repository, p); 228169240Sjfv 229169240Sjfv Create_Admin (p, argv[i], rcsdir, tag, date, 230169240Sjfv nonbranch, 0); 231169240Sjfv 232169240Sjfv if (found_slash) 233169240Sjfv send_a_repository ("", repository, update_dir); 234169240Sjfv 235169240Sjfv if (restore_cwd (&cwd, NULL)) 236169240Sjfv error_exit (); 237169240Sjfv free_cwd (&cwd); 238169240Sjfv 239169240Sjfv if (tag) 240169240Sjfv free (tag); 241169240Sjfv if (date) 242169240Sjfv free (date); 243169240Sjfv free (rcsdir); 244169240Sjfv 245169240Sjfv if (p == filedir) 246169240Sjfv Subdir_Register ((List *) NULL, (char *) NULL, argv[i]); 247169240Sjfv else 248169240Sjfv { 249169240Sjfv Subdir_Register ((List *) NULL, update_dir, p); 250169240Sjfv } 251169240Sjfv free (repository); 252169240Sjfv free (filedir); 253169240Sjfv } 254169240Sjfv } 255169240Sjfv send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS); 256169240Sjfv send_file_names (argc, argv, SEND_EXPAND_WILD); 257169240Sjfv send_to_server ("add\012", 0); 258169240Sjfv if (message) 259169240Sjfv free (message); 260169240Sjfv return err + get_responses_and_close (); 261169240Sjfv } 262169240Sjfv#endif 263169240Sjfv 264169240Sjfv /* walk the arg list adding files/dirs */ 265169240Sjfv for (i = 0; i < argc; i++) 266169240Sjfv { 267169240Sjfv int begin_err = err; 268169240Sjfv#ifdef SERVER_SUPPORT 269169240Sjfv int begin_added_files = added_files; 270169240Sjfv#endif 271169240Sjfv struct file_info finfo; 272169240Sjfv char *p; 273169240Sjfv#if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE) 274169240Sjfv char *found_name; 275169240Sjfv#endif 276169240Sjfv 277169240Sjfv memset (&finfo, 0, sizeof finfo); 278169240Sjfv 279169240Sjfv if (save_cwd (&cwd)) 280169240Sjfv error_exit (); 281169240Sjfv 282169240Sjfv finfo.fullname = xstrdup (argv[i]); 283169240Sjfv p = last_component (argv[i]); 284169240Sjfv if (p == argv[i]) 285169240Sjfv { 286169240Sjfv finfo.update_dir = ""; 287169240Sjfv finfo.file = p; 288169240Sjfv } 289169240Sjfv else 290169240Sjfv { 291169240Sjfv p[-1] = '\0'; 292169240Sjfv finfo.update_dir = argv[i]; 293169240Sjfv finfo.file = p; 294169240Sjfv if (CVS_CHDIR (finfo.update_dir) < 0) 295169240Sjfv error (1, errno, "could not chdir to %s", finfo.update_dir); 296169240Sjfv } 297169240Sjfv 298169240Sjfv /* Add wrappers for this directory. They exist only until 299169240Sjfv the next call to wrap_add_file. */ 300169240Sjfv wrap_add_file (CVSDOTWRAPPER, 1); 301169240Sjfv 302169240Sjfv finfo.rcs = NULL; 303169240Sjfv 304169240Sjfv /* Find the repository associated with our current dir. */ 305169240Sjfv repository = Name_Repository (NULL, finfo.update_dir); 306169240Sjfv 307169240Sjfv entries = Entries_Open (0, NULL); 308169240Sjfv 309169240Sjfv finfo.repository = repository; 310169240Sjfv finfo.entries = entries; 311169240Sjfv 312169240Sjfv#if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE) 313169240Sjfv if (ign_case) 314169240Sjfv { 315169240Sjfv /* Need to check whether there is a directory with the 316169240Sjfv same name but different case. We'll check for files 317169240Sjfv with the same name later (when Version_TS calls 318169240Sjfv RCS_parse which calls fopen_case). If CVS some day 319169240Sjfv records directories in the RCS files, then we should be 320169240Sjfv able to skip the separate check here, which would be 321169240Sjfv cleaner. */ 322169240Sjfv DIR *dirp; 323169240Sjfv struct dirent *dp; 324169240Sjfv 325169240Sjfv dirp = CVS_OPENDIR (finfo.repository); 326169240Sjfv if (dirp == NULL) 327169240Sjfv error (1, errno, "cannot read directory %s", finfo.repository); 328169240Sjfv found_name = NULL; 329169240Sjfv errno = 0; 330169240Sjfv while ((dp = readdir (dirp)) != NULL) 331169240Sjfv { 332169240Sjfv if (cvs_casecmp (dp->d_name, finfo.file) == 0) 333169240Sjfv { 334169240Sjfv if (found_name != NULL) 335169240Sjfv error (1, 0, "%s is ambiguous; could mean %s or %s", 336169240Sjfv finfo.file, dp->d_name, found_name); 337169240Sjfv found_name = xstrdup (dp->d_name); 338169240Sjfv } 339169240Sjfv } 340169240Sjfv if (errno != 0) 341169240Sjfv error (1, errno, "cannot read directory %s", finfo.repository); 342169240Sjfv closedir (dirp); 343169240Sjfv 344169240Sjfv if (found_name != NULL) 345169240Sjfv { 346169240Sjfv /* OK, we are about to patch up the name, so patch up 347169240Sjfv the temporary directory too to match. The isdir 348169240Sjfv should "always" be true (since files have ,v), but 349169240Sjfv I guess we might as well make some attempt to not 350169240Sjfv get confused by stray files in the repository. */ 351169240Sjfv if (isdir (finfo.file)) 352169240Sjfv { 353169240Sjfv if (CVS_MKDIR (found_name, 0777) < 0 354169240Sjfv && errno != EEXIST) 355169240Sjfv error (0, errno, "cannot create %s", finfo.file); 356169240Sjfv } 357169240Sjfv 358169240Sjfv /* OK, we found a directory with the same name, maybe in 359169240Sjfv a different case. Treat it as if the name were the 360169240Sjfv same. */ 361169240Sjfv finfo.file = found_name; 362169240Sjfv } 363169240Sjfv } 364169240Sjfv#endif 365169240Sjfv 366169240Sjfv /* We pass force_tag_match as 1. If the directory has a 367169240Sjfv sticky branch tag, and there is already an RCS file which 368169240Sjfv does not have that tag, then the head revision is 369169240Sjfv meaningless to us. */ 370169240Sjfv vers = Version_TS (&finfo, options, NULL, NULL, 1, 0); 371169240Sjfv if (vers->vn_user == NULL) 372169240Sjfv { 373169240Sjfv /* No entry available, ts_rcs is invalid */ 374169240Sjfv if (vers->vn_rcs == NULL) 375169240Sjfv { 376169240Sjfv /* There is no RCS file either */ 377169240Sjfv if (vers->ts_user == NULL) 378169240Sjfv { 379169240Sjfv /* There is no user file either */ 380169240Sjfv error (0, 0, "nothing known about %s", finfo.fullname); 381169240Sjfv err++; 382169240Sjfv } 383169240Sjfv else if (!isdir (finfo.file) 384169240Sjfv || wrap_name_has (finfo.file, WRAP_TOCVS)) 385169240Sjfv { 386169240Sjfv /* 387169240Sjfv * See if a directory exists in the repository with 388169240Sjfv * the same name. If so, blow this request off. 389169240Sjfv */ 390169240Sjfv char *dname = xmalloc (strlen (repository) 391169240Sjfv + strlen (finfo.file) 392169240Sjfv + 10); 393169240Sjfv (void) sprintf (dname, "%s/%s", repository, finfo.file); 394169240Sjfv if (isdir (dname)) 395169240Sjfv { 396169240Sjfv error (0, 0, 397169240Sjfv "cannot add file `%s' since the directory", 398169240Sjfv finfo.fullname); 399169240Sjfv error (0, 0, "`%s' already exists in the repository", 400169240Sjfv dname); 401169240Sjfv error (1, 0, "illegal filename overlap"); 402169240Sjfv } 403169240Sjfv free (dname); 404169240Sjfv 405169240Sjfv if (vers->options == NULL || *vers->options == '\0') 406169240Sjfv { 407169240Sjfv /* No options specified on command line (or in 408169240Sjfv rcs file if it existed, e.g. the file exists 409169240Sjfv on another branch). Check for a value from 410169240Sjfv the wrapper stuff. */ 411169240Sjfv if (wrap_name_has (finfo.file, WRAP_RCSOPTION)) 412169240Sjfv { 413169240Sjfv if (vers->options) 414169240Sjfv free (vers->options); 415169240Sjfv vers->options = wrap_rcsoption (finfo.file, 1); 416169240Sjfv } 417169240Sjfv } 418169240Sjfv 419169240Sjfv if (vers->nonbranch) 420169240Sjfv { 421169240Sjfv error (0, 0, 422169240Sjfv "cannot add file on non-branch tag %s", 423169240Sjfv vers->tag); 424169240Sjfv ++err; 425169240Sjfv } 426169240Sjfv else 427169240Sjfv { 428169240Sjfv /* There is a user file, so build the entry for it */ 429169240Sjfv if (build_entry (repository, finfo.file, vers->options, 430169240Sjfv message, entries, vers->tag) != 0) 431169240Sjfv err++; 432169240Sjfv else 433169240Sjfv { 434169240Sjfv added_files++; 435169240Sjfv if (!quiet) 436169240Sjfv { 437169240Sjfv if (vers->tag) 438169240Sjfv error (0, 0, "\ 439169240Sjfvscheduling %s `%s' for addition on branch `%s'", 440169240Sjfv (wrap_name_has (finfo.file, 441169240Sjfv WRAP_TOCVS) 442169240Sjfv ? "wrapper" 443169240Sjfv : "file"), 444169240Sjfv finfo.fullname, vers->tag); 445169240Sjfv else 446169240Sjfv error (0, 0, 447169240Sjfv "scheduling %s `%s' for addition", 448169240Sjfv (wrap_name_has (finfo.file, 449169240Sjfv WRAP_TOCVS) 450169240Sjfv ? "wrapper" 451169240Sjfv : "file"), 452169240Sjfv finfo.fullname); 453169240Sjfv } 454169240Sjfv } 455169240Sjfv } 456169240Sjfv } 457169240Sjfv } 458169240Sjfv else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 459169240Sjfv { 460169240Sjfv if (isdir (finfo.file) 461169240Sjfv && !wrap_name_has (finfo.file, WRAP_TOCVS)) 462169240Sjfv { 463169240Sjfv error (0, 0, "\ 464169240Sjfvthe directory `%s' cannot be added because a file of the", finfo.fullname); 465169240Sjfv error (1, 0, "\ 466169240Sjfvsame name already exists in the repository."); 467169240Sjfv } 468169240Sjfv else 469169240Sjfv { 470169240Sjfv if (vers->nonbranch) 471169240Sjfv { 472169240Sjfv error (0, 0, 473169240Sjfv "cannot add file on non-branch tag %s", 474169240Sjfv vers->tag); 475169240Sjfv ++err; 476169240Sjfv } 477169240Sjfv else 478169240Sjfv { 479169240Sjfv if (vers->tag) 480169240Sjfv error (0, 0, "\ 481169240Sjfvfile `%s' will be added on branch `%s' from version %s", 482169240Sjfv finfo.fullname, vers->tag, vers->vn_rcs); 483169240Sjfv else 484169240Sjfv /* I'm not sure that mentioning 485169240Sjfv vers->vn_rcs makes any sense here; I 486169240Sjfv can't think of a way to word the 487169240Sjfv message which is not confusing. */ 488169240Sjfv error (0, 0, "\ 489169240Sjfvre-adding file %s (in place of dead revision %s)", 490169240Sjfv finfo.fullname, vers->vn_rcs); 491169240Sjfv Register (entries, finfo.file, "0", vers->ts_user, 492169240Sjfv vers->options, 493169240Sjfv vers->tag, NULL, NULL); 494169240Sjfv ++added_files; 495169240Sjfv } 496169240Sjfv } 497169240Sjfv } 498169240Sjfv else 499169240Sjfv { 500169240Sjfv /* 501169240Sjfv * There is an RCS file already, so somebody else must've 502169240Sjfv * added it 503169240Sjfv */ 504169240Sjfv error (0, 0, "%s added independently by second party", 505169240Sjfv finfo.fullname); 506169240Sjfv err++; 507169240Sjfv } 508169240Sjfv } 509169240Sjfv else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 510169240Sjfv { 511169240Sjfv 512169240Sjfv /* 513169240Sjfv * An entry for a new-born file, ts_rcs is dummy, but that is 514169240Sjfv * inappropriate here 515169240Sjfv */ 516169240Sjfv error (0, 0, "%s has already been entered", finfo.fullname); 517169240Sjfv err++; 518169240Sjfv } 519169240Sjfv else if (vers->vn_user[0] == '-') 520169240Sjfv { 521169240Sjfv /* An entry for a removed file, ts_rcs is invalid */ 522169240Sjfv if (vers->ts_user == NULL) 523169240Sjfv { 524169240Sjfv /* There is no user file (as it should be) */ 525169240Sjfv if (vers->vn_rcs == NULL) 526169240Sjfv { 527169240Sjfv 528169240Sjfv /* 529169240Sjfv * There is no RCS file, so somebody else must've removed 530169240Sjfv * it from under us 531169240Sjfv */ 532169240Sjfv error (0, 0, "\ 533169240Sjfvcannot resurrect %s; RCS file removed by second party", finfo.fullname); 534169240Sjfv err++; 535169240Sjfv } 536169240Sjfv else 537169240Sjfv { 538169240Sjfv 539169240Sjfv /* 540169240Sjfv * There is an RCS file, so remove the "-" from the 541169240Sjfv * version number and restore the file 542169240Sjfv */ 543169240Sjfv char *tmp = xmalloc (strlen (finfo.file) + 50); 544169240Sjfv 545169240Sjfv (void) strcpy (tmp, vers->vn_user + 1); 546169240Sjfv (void) strcpy (vers->vn_user, tmp); 547169240Sjfv (void) sprintf (tmp, "Resurrected %s", finfo.file); 548169240Sjfv Register (entries, finfo.file, vers->vn_user, tmp, 549169240Sjfv vers->options, 550169240Sjfv vers->tag, vers->date, vers->ts_conflict); 551169240Sjfv free (tmp); 552169240Sjfv 553169240Sjfv /* XXX - bugs here; this really resurrect the head */ 554169240Sjfv /* Note that this depends on the Register above actually 555169240Sjfv having written Entries, or else it won't really 556169240Sjfv check the file out. */ 557169240Sjfv if (update (2, argv + i - 1) == 0) 558169240Sjfv { 559169240Sjfv error (0, 0, "%s, version %s, resurrected", 560169240Sjfv finfo.fullname, 561169240Sjfv vers->vn_user); 562169240Sjfv } 563169240Sjfv else 564169240Sjfv { 565169240Sjfv error (0, 0, "could not resurrect %s", finfo.fullname); 566169240Sjfv err++; 567169240Sjfv } 568169240Sjfv } 569169240Sjfv } 570169240Sjfv else 571169240Sjfv { 572169240Sjfv /* The user file shouldn't be there */ 573169240Sjfv error (0, 0, "\ 574169240Sjfv%s should be removed and is still there (or is back again)", finfo.fullname); 575169240Sjfv err++; 576169240Sjfv } 577169240Sjfv } 578169240Sjfv else 579169240Sjfv { 580169240Sjfv /* A normal entry, ts_rcs is valid, so it must already be there */ 581169240Sjfv error (0, 0, "%s already exists, with version number %s", 582169240Sjfv finfo.fullname, 583169240Sjfv vers->vn_user); 584169240Sjfv err++; 585169240Sjfv } 586169240Sjfv freevers_ts (&vers); 587169240Sjfv 588169240Sjfv /* passed all the checks. Go ahead and add it if its a directory */ 589169240Sjfv if (begin_err == err 590169240Sjfv && isdir (finfo.file) 591169240Sjfv && !wrap_name_has (finfo.file, WRAP_TOCVS)) 592169240Sjfv { 593169240Sjfv err += add_directory (&finfo); 594169240Sjfv } 595169240Sjfv else 596169240Sjfv { 597169240Sjfv#ifdef SERVER_SUPPORT 598169240Sjfv if (server_active && begin_added_files != added_files) 599169240Sjfv server_checked_in (finfo.file, finfo.update_dir, repository); 600169240Sjfv#endif 601169240Sjfv } 602169240Sjfv free (repository); 603169240Sjfv Entries_Close (entries); 604169240Sjfv 605169240Sjfv if (restore_cwd (&cwd, NULL)) 606169240Sjfv error_exit (); 607169240Sjfv free_cwd (&cwd); 608169240Sjfv 609169240Sjfv free (finfo.fullname); 610169240Sjfv#if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE) 611169240Sjfv if (ign_case && found_name != NULL) 612169240Sjfv free (found_name); 613169240Sjfv#endif 614169240Sjfv } 615169240Sjfv if (added_files) 616169240Sjfv error (0, 0, "use '%s commit' to add %s permanently", 617169240Sjfv program_name, 618169240Sjfv (added_files == 1) ? "this file" : "these files"); 619169240Sjfv 620169240Sjfv if (message) 621169240Sjfv free (message); 622169240Sjfv 623169240Sjfv return (err); 624169240Sjfv} 625169240Sjfv 626169240Sjfv/* 627169240Sjfv * The specified user file is really a directory. So, let's make sure that 628169240Sjfv * it is created in the RCS source repository, and that the user's directory 629169240Sjfv * is updated to include a CVS directory. 630169240Sjfv * 631169240Sjfv * Returns 1 on failure, 0 on success. 632169240Sjfv */ 633169240Sjfvstatic int 634169240Sjfvadd_directory (finfo) 635169240Sjfv struct file_info *finfo; 636169240Sjfv{ 637169240Sjfv char *repository = finfo->repository; 638169240Sjfv List *entries = finfo->entries; 639169240Sjfv char *dir = finfo->file; 640169240Sjfv 641169240Sjfv char *rcsdir = NULL; 642169240Sjfv struct saved_cwd cwd; 643169240Sjfv char *message = NULL; 644169240Sjfv char *tag, *date; 645169240Sjfv int nonbranch; 646169240Sjfv char *attrs; 647169240Sjfv 648169240Sjfv if (strchr (dir, '/') != NULL) 649169240Sjfv { 650169240Sjfv /* "Can't happen". */ 651169240Sjfv error (0, 0, 652169240Sjfv "directory %s not added; must be a direct sub-directory", dir); 653169240Sjfv return (1); 654169240Sjfv } 655169240Sjfv if (fncmp (dir, CVSADM) == 0) 656169240Sjfv { 657169240Sjfv error (0, 0, "cannot add a `%s' directory", CVSADM); 658169240Sjfv return (1); 659169240Sjfv } 660169240Sjfv 661169240Sjfv /* before we do anything else, see if we have any per-directory tags */ 662169240Sjfv ParseTag (&tag, &date, &nonbranch); 663169240Sjfv 664169240Sjfv /* Remember the default attributes from this directory, so we can apply 665169240Sjfv them to the new directory. */ 666169240Sjfv fileattr_startdir (repository); 667169240Sjfv attrs = fileattr_getall (NULL); 668169240Sjfv fileattr_free (); 669169240Sjfv 670169240Sjfv /* now, remember where we were, so we can get back */ 671169240Sjfv if (save_cwd (&cwd)) 672169240Sjfv return (1); 673169240Sjfv if ( CVS_CHDIR (dir) < 0) 674169240Sjfv { 675169240Sjfv error (0, errno, "cannot chdir to %s", finfo->fullname); 676169240Sjfv return (1); 677169240Sjfv } 678169240Sjfv#ifdef SERVER_SUPPORT 679169240Sjfv if (!server_active && isfile (CVSADM)) 680169240Sjfv#else 681169240Sjfv if (isfile (CVSADM)) 682169240Sjfv#endif 683169240Sjfv { 684169240Sjfv error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM); 685169240Sjfv goto out; 686169240Sjfv } 687169240Sjfv 688169240Sjfv rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5); 689169240Sjfv sprintf (rcsdir, "%s/%s", repository, dir); 690169240Sjfv if (isfile (rcsdir) && !isdir (rcsdir)) 691169240Sjfv { 692169240Sjfv error (0, 0, "%s is not a directory; %s not added", rcsdir, 693169240Sjfv finfo->fullname); 694169240Sjfv goto out; 695169240Sjfv } 696169240Sjfv 697169240Sjfv /* setup the log message */ 698169240Sjfv message = xmalloc (strlen (rcsdir) 699169240Sjfv + 80 700169240Sjfv + (tag == NULL ? 0 : strlen (tag) + 80) 701169240Sjfv + (date == NULL ? 0 : strlen (date) + 80)); 702169240Sjfv (void) sprintf (message, "Directory %s added to the repository\n", rcsdir); 703169240Sjfv if (tag) 704169240Sjfv { 705169240Sjfv (void) strcat (message, "--> Using per-directory sticky tag `"); 706169240Sjfv (void) strcat (message, tag); 707169240Sjfv (void) strcat (message, "'\n"); 708169240Sjfv } 709169240Sjfv if (date) 710169240Sjfv { 711169240Sjfv (void) strcat (message, "--> Using per-directory sticky date `"); 712169240Sjfv (void) strcat (message, date); 713169240Sjfv (void) strcat (message, "'\n"); 714169240Sjfv } 715169240Sjfv 716169240Sjfv if (!isdir (rcsdir)) 717169240Sjfv { 718169240Sjfv mode_t omask; 719169240Sjfv Node *p; 720169240Sjfv List *ulist; 721169240Sjfv struct logfile_info *li; 722169240Sjfv 723169240Sjfv /* There used to be some code here which would prompt for 724169240Sjfv whether to add the directory. The details of that code had 725169240Sjfv bitrotted, but more to the point it can't work 726169240Sjfv client/server, doesn't ask in the right way for GUIs, etc. 727169240Sjfv A better way of making it harder to accidentally add 728169240Sjfv directories would be to have to add and commit directories 729169240Sjfv like for files. The code was #if 0'd at least since CVS 1.5. */ 730169240Sjfv 731169240Sjfv if (!noexec) 732169240Sjfv { 733169240Sjfv omask = umask (cvsumask); 734169240Sjfv if (CVS_MKDIR (rcsdir, 0777) < 0) 735169240Sjfv { 736169240Sjfv error (0, errno, "cannot mkdir %s", rcsdir); 737169240Sjfv (void) umask (omask); 738169240Sjfv goto out; 739169240Sjfv } 740169240Sjfv (void) umask (omask); 741169240Sjfv } 742169240Sjfv 743169240Sjfv /* Now set the default file attributes to the ones we inherited 744169240Sjfv from the parent directory. */ 745169240Sjfv fileattr_startdir (rcsdir); 746169240Sjfv fileattr_setall (NULL, attrs); 747169240Sjfv fileattr_write (); 748169240Sjfv fileattr_free (); 749169240Sjfv if (attrs != NULL) 750169240Sjfv free (attrs); 751169240Sjfv 752169240Sjfv /* 753169240Sjfv * Set up an update list with a single title node for Update_Logfile 754169240Sjfv */ 755169240Sjfv ulist = getlist (); 756169240Sjfv p = getnode (); 757169240Sjfv p->type = UPDATE; 758169240Sjfv p->delproc = update_delproc; 759169240Sjfv p->key = xstrdup ("- New directory"); 760169240Sjfv li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); 761169240Sjfv li->type = T_TITLE; 762169240Sjfv li->tag = xstrdup (tag); 763169240Sjfv li->rev_old = li->rev_new = NULL; 764169240Sjfv p->data = (char *) li; 765169240Sjfv (void) addnode (ulist, p); 766169240Sjfv Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); 767169240Sjfv dellist (&ulist); 768169240Sjfv } 769169240Sjfv 770169240Sjfv#ifdef SERVER_SUPPORT 771169240Sjfv if (!server_active) 772169240Sjfv Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0); 773169240Sjfv#else 774169240Sjfv Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0); 775169240Sjfv#endif 776169240Sjfv if (tag) 777169240Sjfv free (tag); 778169240Sjfv if (date) 779169240Sjfv free (date); 780169240Sjfv 781169240Sjfv if (restore_cwd (&cwd, NULL)) 782169240Sjfv error_exit (); 783169240Sjfv free_cwd (&cwd); 784169240Sjfv 785169240Sjfv Subdir_Register (entries, (char *) NULL, dir); 786169240Sjfv 787169240Sjfv cvs_output (message, 0); 788169240Sjfv 789169240Sjfv free (rcsdir); 790169240Sjfv free (message); 791169240Sjfv 792169240Sjfv return (0); 793169240Sjfv 794169240Sjfvout: 795169240Sjfv if (restore_cwd (&cwd, NULL)) 796169240Sjfv error_exit (); 797169240Sjfv free_cwd (&cwd); 798169240Sjfv if (rcsdir != NULL) 799169240Sjfv free (rcsdir); 800169240Sjfv return (0); 801169240Sjfv} 802169240Sjfv 803169240Sjfv/* 804169240Sjfv * Builds an entry for a new file and sets up "CVS/file",[pt] by 805169240Sjfv * interrogating the user. Returns non-zero on error. 806169240Sjfv */ 807169240Sjfvstatic int 808169240Sjfvbuild_entry (repository, user, options, message, entries, tag) 809169240Sjfv char *repository; 810169240Sjfv char *user; 811169240Sjfv char *options; 812169240Sjfv char *message; 813169240Sjfv List *entries; 814169240Sjfv char *tag; 815169240Sjfv{ 816169240Sjfv char *fname; 817169240Sjfv char *line; 818169240Sjfv FILE *fp; 819169240Sjfv 820169240Sjfv if (noexec) 821169240Sjfv return (0); 822169240Sjfv 823169240Sjfv /* 824169240Sjfv * The requested log is read directly from the user and stored in the 825169240Sjfv * file user,t. If the "message" argument is set, use it as the 826169240Sjfv * initial creation log (which typically describes the file). 827169240Sjfv */ 828169240Sjfv fname = xmalloc (strlen (user) + 80); 829169240Sjfv (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG); 830169240Sjfv fp = open_file (fname, "w+"); 831169240Sjfv if (message && fputs (message, fp) == EOF) 832169240Sjfv error (1, errno, "cannot write to %s", fname); 833169240Sjfv if (fclose(fp) == EOF) 834169240Sjfv error(1, errno, "cannot close %s", fname); 835169240Sjfv free (fname); 836169240Sjfv 837169240Sjfv /* 838169240Sjfv * Create the entry now, since this allows the user to interrupt us above 839169240Sjfv * without needing to clean anything up (well, we could clean up the 840169240Sjfv * ,t file, but who cares). 841169240Sjfv */ 842169240Sjfv line = xmalloc (strlen (user) + 20); 843169240Sjfv (void) sprintf (line, "Initial %s", user); 844169240Sjfv Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0); 845169240Sjfv free (line); 846169240Sjfv return (0); 847169240Sjfv} 848169240Sjfv