add.c revision 54427
1169240Sjfv/*
2169240Sjfv * Copyright (c) 1992, Brian Berliner and Jeff Polk
3169240Sjfv * Copyright (c) 1989-1992, Brian Berliner
4169240Sjfv *
5169240Sjfv * You may distribute under the terms of the GNU General Public License as
6169240Sjfv * specified in the README file that comes with the CVS source distribution.
7169240Sjfv *
8169240Sjfv * Add
9169240Sjfv *
10169240Sjfv * Adds a file or directory to the RCS source repository.  For a file,
11169240Sjfv * the entry is marked as "needing to be added" in the user's own CVS
12169240Sjfv * directory, and really added to the repository when it is committed.
13169240Sjfv * For a directory, it is added at the appropriate place in the source
14169240Sjfv * repository and a CVS directory is generated within the directory.
15169240Sjfv *
16169240Sjfv * The -m option is currently the only supported option.  Some may wish to
17169240Sjfv * supply standard "rcs" options here, but I've found that this causes more
18169240Sjfv * trouble than anything else.
19169240Sjfv *
20169240Sjfv * The user files or directories must already exist.  For a directory, it must
21169240Sjfv * not already have a CVS file in it.
22169240Sjfv *
23169240Sjfv * An "add" on a file that has been "remove"d but not committed will cause the
24169240Sjfv * file to be resurrected.
25169240Sjfv */
26169240Sjfv
27169240Sjfv#include "cvs.h"
28169240Sjfv#include "savecwd.h"
29169240Sjfv#include "fileattr.h"
30169240Sjfv
31169240Sjfvstatic int add_directory PROTO ((struct file_info *finfo));
32169240Sjfvstatic int build_entry PROTO((char *repository, char *user, char *options,
33169240Sjfv		        char *message, List * entries, char *tag));
34169240Sjfv
35169240Sjfvstatic const char *const add_usage[] =
36169240Sjfv{
37169240Sjfv    "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
38169240Sjfv    "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
39169240Sjfv    "\t-m\tUse \"message\" for the creation log.\n",
40169240Sjfv    "(Specify the --help global option for a list of other help options)\n",
41169240Sjfv    NULL
42169240Sjfv};
43169240Sjfv
44169240Sjfvint
45169240Sjfvadd (argc, argv)
46169240Sjfv    int argc;
47169240Sjfv    char **argv;
48169240Sjfv{
49169240Sjfv    char *message = NULL;
50169240Sjfv    int i;
51169240Sjfv    char *repository;
52169240Sjfv    int c;
53169240Sjfv    int err = 0;
54169240Sjfv    int added_files = 0;
55169240Sjfv    char *options = NULL;
56169240Sjfv    List *entries;
57169240Sjfv    Vers_TS *vers;
58169240Sjfv    struct saved_cwd cwd;
59169240Sjfv    /* Nonzero if we found a slash, and are thus adding files in a
60169240Sjfv       subdirectory.  */
61169240Sjfv    int found_slash = 0;
62169240Sjfv
63169240Sjfv    if (argc == 1 || argc == -1)
64169240Sjfv	usage (add_usage);
65169240Sjfv
66169240Sjfv    wrap_setup ();
67169240Sjfv
68169240Sjfv    /* parse args */
69169240Sjfv    optind = 0;
70169240Sjfv    while ((c = getopt (argc, argv, "+k:m:")) != -1)
71169240Sjfv    {
72169240Sjfv	switch (c)
73169240Sjfv	{
74169240Sjfv	    case 'k':
75169240Sjfv		if (options)
76169240Sjfv		    free (options);
77169240Sjfv		options = RCS_check_kflag (optarg);
78169240Sjfv		break;
79169240Sjfv
80169240Sjfv	    case 'm':
81169240Sjfv		message = xstrdup (optarg);
82169240Sjfv		break;
83169240Sjfv	    case '?':
84169240Sjfv	    default:
85169240Sjfv		usage (add_usage);
86169240Sjfv		break;
87169240Sjfv	}
88169240Sjfv    }
89169240Sjfv    argc -= optind;
90169240Sjfv    argv += optind;
91169240Sjfv
92169240Sjfv    if (argc <= 0)
93169240Sjfv	usage (add_usage);
94169240Sjfv
95169240Sjfv    /* First some sanity checks.  I know that the CVS case is (sort of)
96169240Sjfv       also handled by add_directory, but we need to check here so the
97169240Sjfv       client won't get all confused in send_file_names.  */
98169240Sjfv    for (i = 0; i < argc; i++)
99169240Sjfv    {
100169240Sjfv	int skip_file = 0;
101169240Sjfv
102169240Sjfv	/* If it were up to me I'd probably make this a fatal error.
103169240Sjfv	   But some people are really fond of their "cvs add *", and
104169240Sjfv	   don't seem to object to the warnings.
105169240Sjfv	   Whatever.  */
106169240Sjfv	strip_trailing_slashes (argv[i]);
107169240Sjfv	if (strcmp (argv[i], ".") == 0
108169240Sjfv	    || strcmp (argv[i], "..") == 0
109169240Sjfv	    || fncmp (argv[i], CVSADM) == 0)
110169240Sjfv	{
111169240Sjfv	    error (0, 0, "cannot add special file `%s'; skipping", argv[i]);
112169240Sjfv	    skip_file = 1;
113169240Sjfv	}
114169240Sjfv	else
115169240Sjfv	{
116169240Sjfv	    char *p;
117169240Sjfv	    p = argv[i];
118169240Sjfv	    while (*p != '\0')
119169240Sjfv	    {
120169240Sjfv		if (ISDIRSEP (*p))
121169240Sjfv		{
122169240Sjfv		    found_slash = 1;
123169240Sjfv		    break;
124169240Sjfv		}
125169240Sjfv		++p;
126169240Sjfv	    }
127169240Sjfv	}
128169240Sjfv
129169240Sjfv	if (skip_file)
130169240Sjfv	{
131169240Sjfv	    int j;
132169240Sjfv
133169240Sjfv	    /* FIXME: We don't do anything about free'ing argv[i].  But
134169240Sjfv	       the problem is that it is only sometimes allocated (see
135169240Sjfv	       cvsrc.c).  */
136169240Sjfv
137169240Sjfv	    for (j = i; j < argc - 1; ++j)
138169240Sjfv		argv[j] = argv[j + 1];
139169240Sjfv	    --argc;
140169240Sjfv	    /* Check the new argv[i] again.  */
141169240Sjfv	    --i;
142169240Sjfv	    ++err;
143169240Sjfv	}
144169240Sjfv    }
145169240Sjfv
146169240Sjfv#ifdef CLIENT_SUPPORT
147169240Sjfv    if (client_active)
148169240Sjfv    {
149169240Sjfv	int i;
150169240Sjfv
151169240Sjfv	if (argc == 0)
152169240Sjfv	    /* We snipped out all the arguments in the above sanity
153169240Sjfv	       check.  We can just forget the whole thing (and we
154169240Sjfv	       better, because if we fired up the server and passed it
155169240Sjfv	       nothing, it would spit back a usage message).  */
156169240Sjfv	    return err;
157169240Sjfv
158169240Sjfv	start_server ();
159169240Sjfv	ign_setup ();
160169240Sjfv	if (options) send_arg(options);
161169240Sjfv	option_with_arg ("-m", message);
162169240Sjfv
163169240Sjfv	/* If !found_slash, refrain from sending "Directory", for
164169240Sjfv	   CVS 1.9 compatibility.  If we only tried to deal with servers
165169240Sjfv	   which are at least CVS 1.9.26 or so, we wouldn't have to
166169240Sjfv	   special-case this.  */
167169240Sjfv	if (found_slash)
168169240Sjfv	{
169169240Sjfv	    repository = Name_Repository (NULL, NULL);
170169240Sjfv	    send_a_repository ("", repository, "");
171169240Sjfv	    free (repository);
172169240Sjfv	}
173169240Sjfv
174169240Sjfv	for (i = 0; i < argc; ++i)
175169240Sjfv	{
176169240Sjfv	    /* FIXME: Does this erroneously call Create_Admin in error
177169240Sjfv	       conditions which are only detected once the server gets its
178169240Sjfv	       hands on things?  */
179169240Sjfv	    /* FIXME-also: if filenames are case-insensitive on the
180169240Sjfv	       client, and the directory in the repository already
181169240Sjfv	       exists and is named "foo", and the command is "cvs add
182169240Sjfv	       FOO", this call to Create_Admin puts the wrong thing in
183169240Sjfv	       CVS/Repository and so a subsequent "cvs update" will
184169240Sjfv	       give an error.  The fix will be to have the server report
185169240Sjfv	       back what it actually did (e.g. use tagged text for the
186169240Sjfv	       "Directory %s added" message), and then Create_Admin,
187169240Sjfv	       which should also fix the error handling concerns.  */
188169240Sjfv
189169240Sjfv	    if (isdir (argv[i]))
190169240Sjfv	    {
191169240Sjfv		char *tag;
192169240Sjfv		char *date;
193169240Sjfv		int nonbranch;
194169240Sjfv		char *rcsdir;
195169240Sjfv		char *p;
196169240Sjfv		char *update_dir;
197169240Sjfv		/* This is some mungeable storage into which we can point
198169240Sjfv		   with p and/or update_dir.  */
199169240Sjfv		char *filedir;
200169240Sjfv
201169240Sjfv		if (save_cwd (&cwd))
202169240Sjfv		    error_exit ();
203169240Sjfv
204169240Sjfv		filedir = xstrdup (argv[i]);
205169240Sjfv		p = last_component (filedir);
206169240Sjfv		if (p == filedir)
207169240Sjfv		{
208169240Sjfv		    update_dir = "";
209169240Sjfv		}
210169240Sjfv		else
211169240Sjfv		{
212169240Sjfv		    p[-1] = '\0';
213169240Sjfv		    update_dir = filedir;
214169240Sjfv		    if (CVS_CHDIR (update_dir) < 0)
215169240Sjfv			error (1, errno,
216169240Sjfv			       "could not chdir to %s", update_dir);
217169240Sjfv		}
218169240Sjfv
219169240Sjfv		/* find the repository associated with our current dir */
220169240Sjfv		repository = Name_Repository (NULL, update_dir);
221169240Sjfv
222169240Sjfv		/* before we do anything else, see if we have any
223169240Sjfv		   per-directory tags */
224169240Sjfv		ParseTag (&tag, &date, &nonbranch);
225169240Sjfv
226169240Sjfv		rcsdir = xmalloc (strlen (repository) + strlen (p) + 5);
227169240Sjfv		sprintf (rcsdir, "%s/%s", repository, p);
228169240Sjfv
229169240Sjfv		Create_Admin (p, argv[i], rcsdir, tag, date,
230169240Sjfv			      nonbranch, 0);
231169240Sjfv
232169240Sjfv		if (found_slash)
233169240Sjfv		    send_a_repository ("", repository, update_dir);
234169240Sjfv
235169240Sjfv		if (restore_cwd (&cwd, NULL))
236169240Sjfv		    error_exit ();
237169240Sjfv		free_cwd (&cwd);
238169240Sjfv
239169240Sjfv		if (tag)
240169240Sjfv		    free (tag);
241169240Sjfv		if (date)
242169240Sjfv		    free (date);
243169240Sjfv		free (rcsdir);
244169240Sjfv
245169240Sjfv		if (p == filedir)
246169240Sjfv		    Subdir_Register ((List *) NULL, (char *) NULL, argv[i]);
247169240Sjfv		else
248169240Sjfv		{
249169240Sjfv		    Subdir_Register ((List *) NULL, update_dir, p);
250169240Sjfv		}
251169240Sjfv		free (repository);
252169240Sjfv		free (filedir);
253169240Sjfv	    }
254169240Sjfv	}
255169240Sjfv	send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS);
256169240Sjfv	send_file_names (argc, argv, SEND_EXPAND_WILD);
257169240Sjfv	send_to_server ("add\012", 0);
258169240Sjfv	if (message)
259169240Sjfv	    free (message);
260169240Sjfv	return err + get_responses_and_close ();
261169240Sjfv    }
262169240Sjfv#endif
263169240Sjfv
264169240Sjfv    /* walk the arg list adding files/dirs */
265169240Sjfv    for (i = 0; i < argc; i++)
266169240Sjfv    {
267169240Sjfv	int begin_err = err;
268169240Sjfv#ifdef SERVER_SUPPORT
269169240Sjfv	int begin_added_files = added_files;
270169240Sjfv#endif
271169240Sjfv	struct file_info finfo;
272169240Sjfv	char *p;
273169240Sjfv#if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
274169240Sjfv	char *found_name;
275169240Sjfv#endif
276169240Sjfv
277169240Sjfv	memset (&finfo, 0, sizeof finfo);
278169240Sjfv
279169240Sjfv	if (save_cwd (&cwd))
280169240Sjfv	    error_exit ();
281169240Sjfv
282169240Sjfv	finfo.fullname = xstrdup (argv[i]);
283169240Sjfv	p = last_component (argv[i]);
284169240Sjfv	if (p == argv[i])
285169240Sjfv	{
286169240Sjfv	    finfo.update_dir = "";
287169240Sjfv	    finfo.file = p;
288169240Sjfv	}
289169240Sjfv	else
290169240Sjfv	{
291169240Sjfv	    p[-1] = '\0';
292169240Sjfv	    finfo.update_dir = argv[i];
293169240Sjfv	    finfo.file = p;
294169240Sjfv	    if (CVS_CHDIR (finfo.update_dir) < 0)
295169240Sjfv		error (1, errno, "could not chdir to %s", finfo.update_dir);
296169240Sjfv	}
297169240Sjfv
298169240Sjfv	/* Add wrappers for this directory.  They exist only until
299169240Sjfv	   the next call to wrap_add_file.  */
300169240Sjfv	wrap_add_file (CVSDOTWRAPPER, 1);
301169240Sjfv
302169240Sjfv	finfo.rcs = NULL;
303169240Sjfv
304169240Sjfv	/* Find the repository associated with our current dir.  */
305169240Sjfv	repository = Name_Repository (NULL, finfo.update_dir);
306169240Sjfv
307169240Sjfv	entries = Entries_Open (0, NULL);
308169240Sjfv
309169240Sjfv	finfo.repository = repository;
310169240Sjfv	finfo.entries = entries;
311169240Sjfv
312169240Sjfv#if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
313169240Sjfv	if (ign_case)
314169240Sjfv	{
315169240Sjfv	    /* Need to check whether there is a directory with the
316169240Sjfv	       same name but different case.  We'll check for files
317169240Sjfv	       with the same name later (when Version_TS calls
318169240Sjfv	       RCS_parse which calls fopen_case).  If CVS some day
319169240Sjfv	       records directories in the RCS files, then we should be
320169240Sjfv	       able to skip the separate check here, which would be
321169240Sjfv	       cleaner.  */
322169240Sjfv	    DIR *dirp;
323169240Sjfv	    struct dirent *dp;
324169240Sjfv
325169240Sjfv	    dirp = CVS_OPENDIR (finfo.repository);
326169240Sjfv	    if (dirp == NULL)
327169240Sjfv		error (1, errno, "cannot read directory %s", finfo.repository);
328169240Sjfv	    found_name = NULL;
329169240Sjfv	    errno = 0;
330169240Sjfv	    while ((dp = readdir (dirp)) != NULL)
331169240Sjfv	    {
332169240Sjfv		if (cvs_casecmp (dp->d_name, finfo.file) == 0)
333169240Sjfv		{
334169240Sjfv		    if (found_name != NULL)
335169240Sjfv			error (1, 0, "%s is ambiguous; could mean %s or %s",
336169240Sjfv			       finfo.file, dp->d_name, found_name);
337169240Sjfv		    found_name = xstrdup (dp->d_name);
338169240Sjfv		}
339169240Sjfv	    }
340169240Sjfv	    if (errno != 0)
341169240Sjfv		error (1, errno, "cannot read directory %s", finfo.repository);
342169240Sjfv	    closedir (dirp);
343169240Sjfv
344169240Sjfv	    if (found_name != NULL)
345169240Sjfv	    {
346169240Sjfv		/* OK, we are about to patch up the name, so patch up
347169240Sjfv		   the temporary directory too to match.  The isdir
348169240Sjfv		   should "always" be true (since files have ,v), but
349169240Sjfv		   I guess we might as well make some attempt to not
350169240Sjfv		   get confused by stray files in the repository.  */
351169240Sjfv		if (isdir (finfo.file))
352169240Sjfv		{
353169240Sjfv		    if (CVS_MKDIR (found_name, 0777) < 0
354169240Sjfv			&& errno != EEXIST)
355169240Sjfv			error (0, errno, "cannot create %s", finfo.file);
356169240Sjfv		}
357169240Sjfv
358169240Sjfv		/* OK, we found a directory with the same name, maybe in
359169240Sjfv		   a different case.  Treat it as if the name were the
360169240Sjfv		   same.  */
361169240Sjfv		finfo.file = found_name;
362169240Sjfv	    }
363169240Sjfv	}
364169240Sjfv#endif
365169240Sjfv
366169240Sjfv	/* We pass force_tag_match as 1.  If the directory has a
367169240Sjfv           sticky branch tag, and there is already an RCS file which
368169240Sjfv           does not have that tag, then the head revision is
369169240Sjfv           meaningless to us.  */
370169240Sjfv	vers = Version_TS (&finfo, options, NULL, NULL, 1, 0);
371169240Sjfv	if (vers->vn_user == NULL)
372169240Sjfv	{
373169240Sjfv	    /* No entry available, ts_rcs is invalid */
374169240Sjfv	    if (vers->vn_rcs == NULL)
375169240Sjfv	    {
376169240Sjfv		/* There is no RCS file either */
377169240Sjfv		if (vers->ts_user == NULL)
378169240Sjfv		{
379169240Sjfv		    /* There is no user file either */
380169240Sjfv		    error (0, 0, "nothing known about %s", finfo.fullname);
381169240Sjfv		    err++;
382169240Sjfv		}
383169240Sjfv		else if (!isdir (finfo.file)
384169240Sjfv			 || wrap_name_has (finfo.file, WRAP_TOCVS))
385169240Sjfv		{
386169240Sjfv		    /*
387169240Sjfv		     * See if a directory exists in the repository with
388169240Sjfv		     * the same name.  If so, blow this request off.
389169240Sjfv		     */
390169240Sjfv		    char *dname = xmalloc (strlen (repository)
391169240Sjfv					   + strlen (finfo.file)
392169240Sjfv					   + 10);
393169240Sjfv		    (void) sprintf (dname, "%s/%s", repository, finfo.file);
394169240Sjfv		    if (isdir (dname))
395169240Sjfv		    {
396169240Sjfv			error (0, 0,
397169240Sjfv			       "cannot add file `%s' since the directory",
398169240Sjfv			       finfo.fullname);
399169240Sjfv			error (0, 0, "`%s' already exists in the repository",
400169240Sjfv			       dname);
401169240Sjfv			error (1, 0, "illegal filename overlap");
402169240Sjfv		    }
403169240Sjfv		    free (dname);
404169240Sjfv
405169240Sjfv		    if (vers->options == NULL || *vers->options == '\0')
406169240Sjfv		    {
407169240Sjfv			/* No options specified on command line (or in
408169240Sjfv			   rcs file if it existed, e.g. the file exists
409169240Sjfv			   on another branch).  Check for a value from
410169240Sjfv			   the wrapper stuff.  */
411169240Sjfv			if (wrap_name_has (finfo.file, WRAP_RCSOPTION))
412169240Sjfv			{
413169240Sjfv			    if (vers->options)
414169240Sjfv				free (vers->options);
415169240Sjfv			    vers->options = wrap_rcsoption (finfo.file, 1);
416169240Sjfv			}
417169240Sjfv		    }
418169240Sjfv
419169240Sjfv		    if (vers->nonbranch)
420169240Sjfv		    {
421169240Sjfv			error (0, 0,
422169240Sjfv			       "cannot add file on non-branch tag %s",
423169240Sjfv			       vers->tag);
424169240Sjfv			++err;
425169240Sjfv		    }
426169240Sjfv		    else
427169240Sjfv		    {
428169240Sjfv			/* There is a user file, so build the entry for it */
429169240Sjfv			if (build_entry (repository, finfo.file, vers->options,
430169240Sjfv					 message, entries, vers->tag) != 0)
431169240Sjfv			    err++;
432169240Sjfv			else
433169240Sjfv			{
434169240Sjfv			    added_files++;
435169240Sjfv			    if (!quiet)
436169240Sjfv			    {
437169240Sjfv				if (vers->tag)
438169240Sjfv				    error (0, 0, "\
439169240Sjfvscheduling %s `%s' for addition on branch `%s'",
440169240Sjfv					   (wrap_name_has (finfo.file,
441169240Sjfv							   WRAP_TOCVS)
442169240Sjfv					    ? "wrapper"
443169240Sjfv					    : "file"),
444169240Sjfv					   finfo.fullname, vers->tag);
445169240Sjfv				else
446169240Sjfv				    error (0, 0,
447169240Sjfv					   "scheduling %s `%s' for addition",
448169240Sjfv					   (wrap_name_has (finfo.file,
449169240Sjfv							   WRAP_TOCVS)
450169240Sjfv					    ? "wrapper"
451169240Sjfv					    : "file"),
452169240Sjfv					   finfo.fullname);
453169240Sjfv			    }
454169240Sjfv			}
455169240Sjfv		    }
456169240Sjfv		}
457169240Sjfv	    }
458169240Sjfv	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
459169240Sjfv	    {
460169240Sjfv		if (isdir (finfo.file)
461169240Sjfv		    && !wrap_name_has (finfo.file, WRAP_TOCVS))
462169240Sjfv		{
463169240Sjfv		    error (0, 0, "\
464169240Sjfvthe directory `%s' cannot be added because a file of the", finfo.fullname);
465169240Sjfv		    error (1, 0, "\
466169240Sjfvsame name already exists in the repository.");
467169240Sjfv		}
468169240Sjfv		else
469169240Sjfv		{
470169240Sjfv		    if (vers->nonbranch)
471169240Sjfv		    {
472169240Sjfv			error (0, 0,
473169240Sjfv			       "cannot add file on non-branch tag %s",
474169240Sjfv			       vers->tag);
475169240Sjfv			++err;
476169240Sjfv		    }
477169240Sjfv		    else
478169240Sjfv		    {
479169240Sjfv			if (vers->tag)
480169240Sjfv			    error (0, 0, "\
481169240Sjfvfile `%s' will be added on branch `%s' from version %s",
482169240Sjfv				   finfo.fullname, vers->tag, vers->vn_rcs);
483169240Sjfv			else
484169240Sjfv			    /* I'm not sure that mentioning
485169240Sjfv			       vers->vn_rcs makes any sense here; I
486169240Sjfv			       can't think of a way to word the
487169240Sjfv			       message which is not confusing.  */
488169240Sjfv			    error (0, 0, "\
489169240Sjfvre-adding file %s (in place of dead revision %s)",
490169240Sjfv				   finfo.fullname, vers->vn_rcs);
491169240Sjfv			Register (entries, finfo.file, "0", vers->ts_user,
492169240Sjfv				  vers->options,
493169240Sjfv				  vers->tag, NULL, NULL);
494169240Sjfv			++added_files;
495169240Sjfv		    }
496169240Sjfv		}
497169240Sjfv	    }
498169240Sjfv	    else
499169240Sjfv	    {
500169240Sjfv		/*
501169240Sjfv		 * There is an RCS file already, so somebody else must've
502169240Sjfv		 * added it
503169240Sjfv		 */
504169240Sjfv		error (0, 0, "%s added independently by second party",
505169240Sjfv		       finfo.fullname);
506169240Sjfv		err++;
507169240Sjfv	    }
508169240Sjfv	}
509169240Sjfv	else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
510169240Sjfv	{
511169240Sjfv
512169240Sjfv	    /*
513169240Sjfv	     * An entry for a new-born file, ts_rcs is dummy, but that is
514169240Sjfv	     * inappropriate here
515169240Sjfv	     */
516169240Sjfv	    error (0, 0, "%s has already been entered", finfo.fullname);
517169240Sjfv	    err++;
518169240Sjfv	}
519169240Sjfv	else if (vers->vn_user[0] == '-')
520169240Sjfv	{
521169240Sjfv	    /* An entry for a removed file, ts_rcs is invalid */
522169240Sjfv	    if (vers->ts_user == NULL)
523169240Sjfv	    {
524169240Sjfv		/* There is no user file (as it should be) */
525169240Sjfv		if (vers->vn_rcs == NULL)
526169240Sjfv		{
527169240Sjfv
528169240Sjfv		    /*
529169240Sjfv		     * There is no RCS file, so somebody else must've removed
530169240Sjfv		     * it from under us
531169240Sjfv		     */
532169240Sjfv		    error (0, 0, "\
533169240Sjfvcannot resurrect %s; RCS file removed by second party", finfo.fullname);
534169240Sjfv		    err++;
535169240Sjfv		}
536169240Sjfv		else
537169240Sjfv		{
538169240Sjfv
539169240Sjfv		    /*
540169240Sjfv		     * There is an RCS file, so remove the "-" from the
541169240Sjfv		     * version number and restore the file
542169240Sjfv		     */
543169240Sjfv		    char *tmp = xmalloc (strlen (finfo.file) + 50);
544169240Sjfv
545169240Sjfv		    (void) strcpy (tmp, vers->vn_user + 1);
546169240Sjfv		    (void) strcpy (vers->vn_user, tmp);
547169240Sjfv		    (void) sprintf (tmp, "Resurrected %s", finfo.file);
548169240Sjfv		    Register (entries, finfo.file, vers->vn_user, tmp,
549169240Sjfv			      vers->options,
550169240Sjfv			      vers->tag, vers->date, vers->ts_conflict);
551169240Sjfv		    free (tmp);
552169240Sjfv
553169240Sjfv		    /* XXX - bugs here; this really resurrect the head */
554169240Sjfv		    /* Note that this depends on the Register above actually
555169240Sjfv		       having written Entries, or else it won't really
556169240Sjfv		       check the file out.  */
557169240Sjfv		    if (update (2, argv + i - 1) == 0)
558169240Sjfv		    {
559169240Sjfv			error (0, 0, "%s, version %s, resurrected",
560169240Sjfv			       finfo.fullname,
561169240Sjfv			       vers->vn_user);
562169240Sjfv		    }
563169240Sjfv		    else
564169240Sjfv		    {
565169240Sjfv			error (0, 0, "could not resurrect %s", finfo.fullname);
566169240Sjfv			err++;
567169240Sjfv		    }
568169240Sjfv		}
569169240Sjfv	    }
570169240Sjfv	    else
571169240Sjfv	    {
572169240Sjfv		/* The user file shouldn't be there */
573169240Sjfv		error (0, 0, "\
574169240Sjfv%s should be removed and is still there (or is back again)", finfo.fullname);
575169240Sjfv		err++;
576169240Sjfv	    }
577169240Sjfv	}
578169240Sjfv	else
579169240Sjfv	{
580169240Sjfv	    /* A normal entry, ts_rcs is valid, so it must already be there */
581169240Sjfv	    error (0, 0, "%s already exists, with version number %s",
582169240Sjfv		   finfo.fullname,
583169240Sjfv		   vers->vn_user);
584169240Sjfv	    err++;
585169240Sjfv	}
586169240Sjfv	freevers_ts (&vers);
587169240Sjfv
588169240Sjfv	/* passed all the checks.  Go ahead and add it if its a directory */
589169240Sjfv	if (begin_err == err
590169240Sjfv	    && isdir (finfo.file)
591169240Sjfv	    && !wrap_name_has (finfo.file, WRAP_TOCVS))
592169240Sjfv	{
593169240Sjfv	    err += add_directory (&finfo);
594169240Sjfv	}
595169240Sjfv	else
596169240Sjfv	{
597169240Sjfv#ifdef SERVER_SUPPORT
598169240Sjfv	    if (server_active && begin_added_files != added_files)
599169240Sjfv		server_checked_in (finfo.file, finfo.update_dir, repository);
600169240Sjfv#endif
601169240Sjfv	}
602169240Sjfv	free (repository);
603169240Sjfv	Entries_Close (entries);
604169240Sjfv
605169240Sjfv	if (restore_cwd (&cwd, NULL))
606169240Sjfv	    error_exit ();
607169240Sjfv	free_cwd (&cwd);
608169240Sjfv
609169240Sjfv	free (finfo.fullname);
610169240Sjfv#if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
611169240Sjfv	if (ign_case && found_name != NULL)
612169240Sjfv	    free (found_name);
613169240Sjfv#endif
614169240Sjfv    }
615169240Sjfv    if (added_files)
616169240Sjfv	error (0, 0, "use '%s commit' to add %s permanently",
617169240Sjfv	       program_name,
618169240Sjfv	       (added_files == 1) ? "this file" : "these files");
619169240Sjfv
620169240Sjfv    if (message)
621169240Sjfv	free (message);
622169240Sjfv
623169240Sjfv    return (err);
624169240Sjfv}
625169240Sjfv
626169240Sjfv/*
627169240Sjfv * The specified user file is really a directory.  So, let's make sure that
628169240Sjfv * it is created in the RCS source repository, and that the user's directory
629169240Sjfv * is updated to include a CVS directory.
630169240Sjfv *
631169240Sjfv * Returns 1 on failure, 0 on success.
632169240Sjfv */
633169240Sjfvstatic int
634169240Sjfvadd_directory (finfo)
635169240Sjfv    struct file_info *finfo;
636169240Sjfv{
637169240Sjfv    char *repository = finfo->repository;
638169240Sjfv    List *entries = finfo->entries;
639169240Sjfv    char *dir = finfo->file;
640169240Sjfv
641169240Sjfv    char *rcsdir = NULL;
642169240Sjfv    struct saved_cwd cwd;
643169240Sjfv    char *message = NULL;
644169240Sjfv    char *tag, *date;
645169240Sjfv    int nonbranch;
646169240Sjfv    char *attrs;
647169240Sjfv
648169240Sjfv    if (strchr (dir, '/') != NULL)
649169240Sjfv    {
650169240Sjfv	/* "Can't happen".  */
651169240Sjfv	error (0, 0,
652169240Sjfv	       "directory %s not added; must be a direct sub-directory", dir);
653169240Sjfv	return (1);
654169240Sjfv    }
655169240Sjfv    if (fncmp (dir, CVSADM) == 0)
656169240Sjfv    {
657169240Sjfv	error (0, 0, "cannot add a `%s' directory", CVSADM);
658169240Sjfv	return (1);
659169240Sjfv    }
660169240Sjfv
661169240Sjfv    /* before we do anything else, see if we have any per-directory tags */
662169240Sjfv    ParseTag (&tag, &date, &nonbranch);
663169240Sjfv
664169240Sjfv    /* Remember the default attributes from this directory, so we can apply
665169240Sjfv       them to the new directory.  */
666169240Sjfv    fileattr_startdir (repository);
667169240Sjfv    attrs = fileattr_getall (NULL);
668169240Sjfv    fileattr_free ();
669169240Sjfv
670169240Sjfv    /* now, remember where we were, so we can get back */
671169240Sjfv    if (save_cwd (&cwd))
672169240Sjfv	return (1);
673169240Sjfv    if ( CVS_CHDIR (dir) < 0)
674169240Sjfv    {
675169240Sjfv	error (0, errno, "cannot chdir to %s", finfo->fullname);
676169240Sjfv	return (1);
677169240Sjfv    }
678169240Sjfv#ifdef SERVER_SUPPORT
679169240Sjfv    if (!server_active && isfile (CVSADM))
680169240Sjfv#else
681169240Sjfv    if (isfile (CVSADM))
682169240Sjfv#endif
683169240Sjfv    {
684169240Sjfv	error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM);
685169240Sjfv	goto out;
686169240Sjfv    }
687169240Sjfv
688169240Sjfv    rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5);
689169240Sjfv    sprintf (rcsdir, "%s/%s", repository, dir);
690169240Sjfv    if (isfile (rcsdir) && !isdir (rcsdir))
691169240Sjfv    {
692169240Sjfv	error (0, 0, "%s is not a directory; %s not added", rcsdir,
693169240Sjfv	       finfo->fullname);
694169240Sjfv	goto out;
695169240Sjfv    }
696169240Sjfv
697169240Sjfv    /* setup the log message */
698169240Sjfv    message = xmalloc (strlen (rcsdir)
699169240Sjfv		       + 80
700169240Sjfv		       + (tag == NULL ? 0 : strlen (tag) + 80)
701169240Sjfv		       + (date == NULL ? 0 : strlen (date) + 80));
702169240Sjfv    (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
703169240Sjfv    if (tag)
704169240Sjfv    {
705169240Sjfv	(void) strcat (message, "--> Using per-directory sticky tag `");
706169240Sjfv	(void) strcat (message, tag);
707169240Sjfv	(void) strcat (message, "'\n");
708169240Sjfv    }
709169240Sjfv    if (date)
710169240Sjfv    {
711169240Sjfv	(void) strcat (message, "--> Using per-directory sticky date `");
712169240Sjfv	(void) strcat (message, date);
713169240Sjfv	(void) strcat (message, "'\n");
714169240Sjfv    }
715169240Sjfv
716169240Sjfv    if (!isdir (rcsdir))
717169240Sjfv    {
718169240Sjfv	mode_t omask;
719169240Sjfv	Node *p;
720169240Sjfv	List *ulist;
721169240Sjfv	struct logfile_info *li;
722169240Sjfv
723169240Sjfv	/* There used to be some code here which would prompt for
724169240Sjfv	   whether to add the directory.  The details of that code had
725169240Sjfv	   bitrotted, but more to the point it can't work
726169240Sjfv	   client/server, doesn't ask in the right way for GUIs, etc.
727169240Sjfv	   A better way of making it harder to accidentally add
728169240Sjfv	   directories would be to have to add and commit directories
729169240Sjfv	   like for files.  The code was #if 0'd at least since CVS 1.5.  */
730169240Sjfv
731169240Sjfv	if (!noexec)
732169240Sjfv	{
733169240Sjfv	    omask = umask (cvsumask);
734169240Sjfv	    if (CVS_MKDIR (rcsdir, 0777) < 0)
735169240Sjfv	    {
736169240Sjfv		error (0, errno, "cannot mkdir %s", rcsdir);
737169240Sjfv		(void) umask (omask);
738169240Sjfv		goto out;
739169240Sjfv	    }
740169240Sjfv	    (void) umask (omask);
741169240Sjfv	}
742169240Sjfv
743169240Sjfv	/* Now set the default file attributes to the ones we inherited
744169240Sjfv	   from the parent directory.  */
745169240Sjfv	fileattr_startdir (rcsdir);
746169240Sjfv	fileattr_setall (NULL, attrs);
747169240Sjfv	fileattr_write ();
748169240Sjfv	fileattr_free ();
749169240Sjfv	if (attrs != NULL)
750169240Sjfv	    free (attrs);
751169240Sjfv
752169240Sjfv	/*
753169240Sjfv	 * Set up an update list with a single title node for Update_Logfile
754169240Sjfv	 */
755169240Sjfv	ulist = getlist ();
756169240Sjfv	p = getnode ();
757169240Sjfv	p->type = UPDATE;
758169240Sjfv	p->delproc = update_delproc;
759169240Sjfv	p->key = xstrdup ("- New directory");
760169240Sjfv	li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
761169240Sjfv	li->type = T_TITLE;
762169240Sjfv	li->tag = xstrdup (tag);
763169240Sjfv	li->rev_old = li->rev_new = NULL;
764169240Sjfv	p->data = (char *) li;
765169240Sjfv	(void) addnode (ulist, p);
766169240Sjfv	Update_Logfile (rcsdir, message, (FILE *) NULL, ulist);
767169240Sjfv	dellist (&ulist);
768169240Sjfv    }
769169240Sjfv
770169240Sjfv#ifdef SERVER_SUPPORT
771169240Sjfv    if (!server_active)
772169240Sjfv	Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0);
773169240Sjfv#else
774169240Sjfv    Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0);
775169240Sjfv#endif
776169240Sjfv    if (tag)
777169240Sjfv	free (tag);
778169240Sjfv    if (date)
779169240Sjfv	free (date);
780169240Sjfv
781169240Sjfv    if (restore_cwd (&cwd, NULL))
782169240Sjfv	error_exit ();
783169240Sjfv    free_cwd (&cwd);
784169240Sjfv
785169240Sjfv    Subdir_Register (entries, (char *) NULL, dir);
786169240Sjfv
787169240Sjfv    cvs_output (message, 0);
788169240Sjfv
789169240Sjfv    free (rcsdir);
790169240Sjfv    free (message);
791169240Sjfv
792169240Sjfv    return (0);
793169240Sjfv
794169240Sjfvout:
795169240Sjfv    if (restore_cwd (&cwd, NULL))
796169240Sjfv	error_exit ();
797169240Sjfv    free_cwd (&cwd);
798169240Sjfv    if (rcsdir != NULL)
799169240Sjfv	free (rcsdir);
800169240Sjfv    return (0);
801169240Sjfv}
802169240Sjfv
803169240Sjfv/*
804169240Sjfv * Builds an entry for a new file and sets up "CVS/file",[pt] by
805169240Sjfv * interrogating the user.  Returns non-zero on error.
806169240Sjfv */
807169240Sjfvstatic int
808169240Sjfvbuild_entry (repository, user, options, message, entries, tag)
809169240Sjfv    char *repository;
810169240Sjfv    char *user;
811169240Sjfv    char *options;
812169240Sjfv    char *message;
813169240Sjfv    List *entries;
814169240Sjfv    char *tag;
815169240Sjfv{
816169240Sjfv    char *fname;
817169240Sjfv    char *line;
818169240Sjfv    FILE *fp;
819169240Sjfv
820169240Sjfv    if (noexec)
821169240Sjfv	return (0);
822169240Sjfv
823169240Sjfv    /*
824169240Sjfv     * The requested log is read directly from the user and stored in the
825169240Sjfv     * file user,t.  If the "message" argument is set, use it as the
826169240Sjfv     * initial creation log (which typically describes the file).
827169240Sjfv     */
828169240Sjfv    fname = xmalloc (strlen (user) + 80);
829169240Sjfv    (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
830169240Sjfv    fp = open_file (fname, "w+");
831169240Sjfv    if (message && fputs (message, fp) == EOF)
832169240Sjfv	    error (1, errno, "cannot write to %s", fname);
833169240Sjfv    if (fclose(fp) == EOF)
834169240Sjfv        error(1, errno, "cannot close %s", fname);
835169240Sjfv    free (fname);
836169240Sjfv
837169240Sjfv    /*
838169240Sjfv     * Create the entry now, since this allows the user to interrupt us above
839169240Sjfv     * without needing to clean anything up (well, we could clean up the
840169240Sjfv     * ,t file, but who cares).
841169240Sjfv     */
842169240Sjfv    line = xmalloc (strlen (user) + 20);
843169240Sjfv    (void) sprintf (line, "Initial %s", user);
844169240Sjfv    Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
845169240Sjfv    free (line);
846169240Sjfv    return (0);
847169240Sjfv}
848169240Sjfv