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
37static int add_directory (struct file_info *finfo);
38static int build_entry (const char *repository, const char *user,
39                        const char *options, const char *message,
40                        List * entries, const char *tag);
41
42static 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
52int
53add (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    getoptreset ();
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
688skip_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 */
725static int
726add_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
873out:
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 */
890static int
891build_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