add.c revision 267654
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
37static int add_directory PROTO ((struct file_info *finfo));
38static int build_entry PROTO((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 (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, "\
426scheduling %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, "\
451the directory `%s' cannot be added because a file of the", finfo.fullname);
452		    error (1, 0, "\
453same 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, "\
608cannot 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
699skip_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 */
735static int
736add_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
895out:
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 */
911static int
912build_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