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