add.c revision 17721
1/* 2 * Copyright (c) 1992, Brian Berliner and Jeff Polk 3 * Copyright (c) 1989-1992, Brian Berliner 4 * 5 * You may distribute under the terms of the GNU General Public License as 6 * specified in the README file that comes with the CVS 1.4 kit. 7 * 8 * Add 9 * 10 * Adds a file or directory to the RCS source repository. For a file, 11 * the entry is marked as "needing to be added" in the user's own CVS 12 * directory, and really added to the repository when it is committed. 13 * For a directory, it is added at the appropriate place in the source 14 * repository and a CVS directory is generated within the directory. 15 * 16 * The -m option is currently the only supported option. Some may wish to 17 * supply standard "rcs" options here, but I've found that this causes more 18 * trouble than anything else. 19 * 20 * The user files or directories must already exist. For a directory, it must 21 * not already have a CVS file in it. 22 * 23 * An "add" on a file that has been "remove"d but not committed will cause the 24 * file to be resurrected. 25 */ 26 27#include "cvs.h" 28#include "savecwd.h" 29 30static int add_directory PROTO((char *repository, char *dir)); 31static int build_entry PROTO((char *repository, char *user, char *options, 32 char *message, List * entries, char *tag)); 33 34static const char *const add_usage[] = 35{ 36 "Usage: %s %s [-k rcs-kflag] [-m message] files...\n", 37 "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n", 38 "\t-m\tUse \"message\" for the creation log.\n", 39 NULL 40}; 41 42int 43add (argc, argv) 44 int argc; 45 char **argv; 46{ 47 char *message = NULL; 48 char *user; 49 int i; 50 char *repository; 51 int c; 52 int err = 0; 53 int added_files = 0; 54 char *options = NULL; 55 List *entries; 56 Vers_TS *vers; 57 58 if (argc == 1 || argc == -1) 59 usage (add_usage); 60 61 wrap_setup (); 62 63 /* parse args */ 64 optind = 1; 65 while ((c = getopt (argc, argv, "k:m:")) != -1) 66 { 67 switch (c) 68 { 69 case 'k': 70 if (options) 71 free (options); 72 options = RCS_check_kflag (optarg); 73 break; 74 75 case 'm': 76 message = xstrdup (optarg); 77 break; 78 case '?': 79 default: 80 usage (add_usage); 81 break; 82 } 83 } 84 argc -= optind; 85 argv += optind; 86 87 if (argc <= 0) 88 usage (add_usage); 89 90 /* find the repository associated with our current dir */ 91 repository = Name_Repository ((char *) NULL, (char *) NULL); 92 93#ifdef CLIENT_SUPPORT 94 if (client_active) 95 { 96 int i; 97 start_server (); 98 ign_setup (); 99 if (options) send_arg(options); 100 option_with_arg ("-m", message); 101 for (i = 0; i < argc; ++i) 102 /* FIXME: Does this erroneously call Create_Admin in error 103 conditions which are only detected once the server gets its 104 hands on things? */ 105 if (isdir (argv[i])) 106 { 107 char *tag; 108 char *date; 109 char *rcsdir = xmalloc (strlen (repository) 110 + strlen (argv[i]) + 10); 111 112 /* before we do anything else, see if we have any 113 per-directory tags */ 114 ParseTag (&tag, &date); 115 116 sprintf (rcsdir, "%s/%s", repository, argv[i]); 117 118 Create_Admin (argv[i], argv[i], rcsdir, tag, date); 119 120 if (tag) 121 free (tag); 122 if (date) 123 free (date); 124 free (rcsdir); 125 } 126 send_file_names (argc, argv, SEND_EXPAND_WILD); 127 send_files (argc, argv, 0, 0); 128 send_to_server ("add\012", 0); 129 return get_responses_and_close (); 130 } 131#endif 132 133 entries = Entries_Open (0); 134 135 /* walk the arg list adding files/dirs */ 136 for (i = 0; i < argc; i++) 137 { 138 int begin_err = err; 139 int begin_added_files = added_files; 140 141 user = argv[i]; 142 strip_trailing_slashes (user); 143 if (strchr (user, '/') != NULL) 144 { 145 error (0, 0, 146 "cannot add files with '/' in their name; %s not added", user); 147 err++; 148 continue; 149 } 150 151 vers = Version_TS (repository, options, (char *) NULL, (char *) NULL, 152 user, 0, 0, entries, (RCSNode *) NULL); 153 if (vers->vn_user == NULL) 154 { 155 /* No entry available, ts_rcs is invalid */ 156 if (vers->vn_rcs == NULL) 157 { 158 /* There is no RCS file either */ 159 if (vers->ts_user == NULL) 160 { 161 /* There is no user file either */ 162 error (0, 0, "nothing known about %s", user); 163 err++; 164 } 165 else if (!isdir (user) || wrap_name_has (user, WRAP_TOCVS)) 166 { 167 /* 168 * See if a directory exists in the repository with 169 * the same name. If so, blow this request off. 170 */ 171 char dname[PATH_MAX]; 172 (void) sprintf (dname, "%s/%s", repository, user); 173 if (isdir (dname)) 174 { 175 error (0, 0, 176 "cannot add file `%s' since the directory", 177 user); 178 error (0, 0, "`%s' already exists in the repository", 179 dname); 180 error (1, 0, "illegal filename overlap"); 181 } 182 183 /* There is a user file, so build the entry for it */ 184 if (build_entry (repository, user, vers->options, 185 message, entries, vers->tag) != 0) 186 err++; 187 else 188 { 189 added_files++; 190 if (!quiet) 191 { 192 if (vers->tag) 193 error (0, 0, "\ 194scheduling %s `%s' for addition on branch `%s'", 195 (wrap_name_has (user, WRAP_TOCVS) 196 ? "wrapper" 197 : "file"), 198 user, vers->tag); 199 else 200 error (0, 0, "scheduling %s `%s' for addition", 201 (wrap_name_has (user, WRAP_TOCVS) 202 ? "wrapper" 203 : "file"), 204 user); 205 } 206 } 207 } 208 } 209 else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 210 { 211 if (isdir (user) && !wrap_name_has (user, WRAP_TOCVS)) 212 { 213 error (0, 0, "the directory `%s' cannot be added because a file of the", user); 214 error (1, 0, "same name already exists in the repository."); 215 } 216 else 217 { 218 if (vers->tag) 219 error (0, 0, "file `%s' will be added on branch `%s' from version %s", 220 user, vers->tag, vers->vn_rcs); 221 else 222 error (0, 0, "version %s of `%s' will be resurrected", 223 vers->vn_rcs, user); 224 Register (entries, user, "0", vers->ts_user, NULL, 225 vers->tag, NULL, NULL); 226 ++added_files; 227 } 228 } 229 else 230 { 231 /* 232 * There is an RCS file already, so somebody else must've 233 * added it 234 */ 235 error (0, 0, "%s added independently by second party", user); 236 err++; 237 } 238 } 239 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0') 240 { 241 242 /* 243 * An entry for a new-born file, ts_rcs is dummy, but that is 244 * inappropriate here 245 */ 246 error (0, 0, "%s has already been entered", user); 247 err++; 248 } 249 else if (vers->vn_user[0] == '-') 250 { 251 /* An entry for a removed file, ts_rcs is invalid */ 252 if (vers->ts_user == NULL) 253 { 254 /* There is no user file (as it should be) */ 255 if (vers->vn_rcs == NULL) 256 { 257 258 /* 259 * There is no RCS file, so somebody else must've removed 260 * it from under us 261 */ 262 error (0, 0, 263 "cannot resurrect %s; RCS file removed by second party", user); 264 err++; 265 } 266 else 267 { 268 269 /* 270 * There is an RCS file, so remove the "-" from the 271 * version number and restore the file 272 */ 273 char *tmp = xmalloc (strlen (user) + 50); 274 275 (void) strcpy (tmp, vers->vn_user + 1); 276 (void) strcpy (vers->vn_user, tmp); 277 (void) sprintf (tmp, "Resurrected %s", user); 278 Register (entries, user, vers->vn_user, tmp, vers->options, 279 vers->tag, vers->date, vers->ts_conflict); 280 free (tmp); 281 282 /* XXX - bugs here; this really resurrect the head */ 283 /* Note that this depends on the Register above actually 284 having written Entries, or else it won't really 285 check the file out. */ 286 if (update (2, argv + i - 1) == 0) 287 { 288 error (0, 0, "%s, version %s, resurrected", user, 289 vers->vn_user); 290 } 291 else 292 { 293 error (0, 0, "could not resurrect %s", user); 294 err++; 295 } 296 } 297 } 298 else 299 { 300 /* The user file shouldn't be there */ 301 error (0, 0, "%s should be removed and is still there (or is back again)", user); 302 err++; 303 } 304 } 305 else 306 { 307 /* A normal entry, ts_rcs is valid, so it must already be there */ 308 error (0, 0, "%s already exists, with version number %s", user, 309 vers->vn_user); 310 err++; 311 } 312 freevers_ts (&vers); 313 314 /* passed all the checks. Go ahead and add it if its a directory */ 315 if (begin_err == err 316 && isdir (user) 317 && !wrap_name_has (user, WRAP_TOCVS)) 318 { 319 err += add_directory (repository, user); 320 continue; 321 } 322#ifdef SERVER_SUPPORT 323 if (server_active && begin_added_files != added_files) 324 server_checked_in (user, ".", repository); 325#endif 326 } 327 if (added_files) 328 error (0, 0, "use 'cvs commit' to add %s permanently", 329 (added_files == 1) ? "this file" : "these files"); 330 331 Entries_Close (entries); 332 333 if (message) 334 free (message); 335 336 return (err); 337} 338 339/* 340 * The specified user file is really a directory. So, let's make sure that 341 * it is created in the RCS source repository, and that the user's directory 342 * is updated to include a CVS directory. 343 * 344 * Returns 1 on failure, 0 on success. 345 */ 346static int 347add_directory (repository, dir) 348 char *repository; 349 char *dir; 350{ 351 char rcsdir[PATH_MAX]; 352 struct saved_cwd cwd; 353 char message[PATH_MAX + 100]; 354 char *tag, *date; 355 356 if (strchr (dir, '/') != NULL) 357 { 358 error (0, 0, 359 "directory %s not added; must be a direct sub-directory", dir); 360 return (1); 361 } 362 if (strcmp (dir, CVSADM) == 0) 363 { 364 error (0, 0, "cannot add a `%s' directory", CVSADM); 365 return (1); 366 } 367 368 /* before we do anything else, see if we have any per-directory tags */ 369 ParseTag (&tag, &date); 370 371 /* now, remember where we were, so we can get back */ 372 if (save_cwd (&cwd)) 373 return (1); 374 if (chdir (dir) < 0) 375 { 376 error (0, errno, "cannot chdir to %s", dir); 377 return (1); 378 } 379#ifdef SERVER_SUPPORT 380 if (!server_active && isfile (CVSADM)) 381#else 382 if (isfile (CVSADM)) 383#endif 384 { 385 error (0, 0, "%s/%s already exists", dir, CVSADM); 386 goto out; 387 } 388 389 (void) sprintf (rcsdir, "%s/%s", repository, dir); 390 if (isfile (rcsdir) && !isdir (rcsdir)) 391 { 392 error (0, 0, "%s is not a directory; %s not added", rcsdir, dir); 393 goto out; 394 } 395 396 /* setup the log message */ 397 (void) sprintf (message, "Directory %s added to the repository\n", rcsdir); 398 if (tag) 399 { 400 (void) strcat (message, "--> Using per-directory sticky tag `"); 401 (void) strcat (message, tag); 402 (void) strcat (message, "'\n"); 403 } 404 if (date) 405 { 406 (void) strcat (message, "--> Using per-directory sticky date `"); 407 (void) strcat (message, date); 408 (void) strcat (message, "'\n"); 409 } 410 411 if (!isdir (rcsdir)) 412 { 413 mode_t omask; 414 Node *p; 415 List *ulist; 416 417#if 0 418 char line[MAXLINELEN]; 419 420 (void) printf ("Add directory %s to the repository (y/n) [n] ? ", 421 rcsdir); 422 (void) fflush (stdout); 423 clearerr (stdin); 424 if (fgets (line, sizeof (line), stdin) == NULL || 425 (line[0] != 'y' && line[0] != 'Y')) 426 { 427 error (0, 0, "directory %s not added", rcsdir); 428 goto out; 429 } 430#endif 431 432 omask = umask (cvsumask); 433 if (CVS_MKDIR (rcsdir, 0777) < 0) 434 { 435 error (0, errno, "cannot mkdir %s", rcsdir); 436 (void) umask (omask); 437 goto out; 438 } 439 (void) umask (omask); 440 441 /* 442 * Set up an update list with a single title node for Update_Logfile 443 */ 444 ulist = getlist (); 445 p = getnode (); 446 p->type = UPDATE; 447 p->delproc = update_delproc; 448 p->key = xstrdup ("- New directory"); 449 p->data = (char *) T_TITLE; 450 (void) addnode (ulist, p); 451 Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist); 452 dellist (&ulist); 453 } 454 455#ifdef SERVER_SUPPORT 456 if (!server_active) 457 Create_Admin (".", dir, rcsdir, tag, date); 458#else 459 Create_Admin (".", dir, rcsdir, tag, date); 460#endif 461 if (tag) 462 free (tag); 463 if (date) 464 free (date); 465 466 (void) printf ("%s", message); 467out: 468 if (restore_cwd (&cwd, NULL)) 469 exit (EXIT_FAILURE); 470 free_cwd (&cwd); 471 return (0); 472} 473 474/* 475 * Builds an entry for a new file and sets up "CVS/file",[pt] by 476 * interrogating the user. Returns non-zero on error. 477 */ 478static int 479build_entry (repository, user, options, message, entries, tag) 480 char *repository; 481 char *user; 482 char *options; 483 char *message; 484 List *entries; 485 char *tag; 486{ 487 char fname[PATH_MAX]; 488 char line[MAXLINELEN]; 489 FILE *fp; 490 491 if (noexec) 492 return (0); 493 494 /* 495 * The requested log is read directly from the user and stored in the 496 * file user,t. If the "message" argument is set, use it as the 497 * initial creation log (which typically describes the file). 498 */ 499 (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG); 500 fp = open_file (fname, "w+"); 501 if (message && fputs (message, fp) == EOF) 502 error (1, errno, "cannot write to %s", fname); 503 if (fclose(fp) == EOF) 504 error(1, errno, "cannot close %s", fname); 505 506 /* 507 * Create the entry now, since this allows the user to interrupt us above 508 * without needing to clean anything up (well, we could clean up the 509 * ,t file, but who cares). 510 */ 511 (void) sprintf (line, "Initial %s", user); 512 Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0); 513 return (0); 514} 515