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