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