xref: /dragonfly/contrib/cvs-1.12/src/add.c (revision 0ac6bf9d)
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