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