386BSD 0.1 development
[unix-history] / usr / othersrc / public / cvs-1.3 / src / add.c
CommitLineData
13edba98
WJ
1/*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 *
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS 1.3 kit.
7 *
8 * Add
9 *
10 * Adds a file or directory to the RCS source repository. For a file,
11 * the entry is marked as "needing to be added" in the user's own CVS
12 * directory, and really added to the repository when it is committed.
13 * For a directory, it is added at the appropriate place in the source
14 * repository and a CVS directory is generated within the directory.
15 *
16 * The -m option is currently the only supported option. Some may wish to
17 * supply standard "rcs" options here, but I've found that this causes more
18 * trouble than anything else.
19 *
20 * The user files or directories must already exist. For a directory, it must
21 * not already have a CVS file in it.
22 *
23 * An "add" on a file that has been "remove"d but not committed will cause the
24 * file to be resurrected.
25 */
26
27#include "cvs.h"
28
29#ifndef lint
30static char rcsid[] = "@(#)add.c 1.46 92/04/03";
31#endif
32
33#if __STDC__
34static int add_directory (char *repository, char *dir);
35static int build_entry (char *repository, char *user, char *options,
36 char *message, List * entries);
37#else
38static int add_directory ();
39static int build_entry ();
40#endif /* __STDC__ */
41
42static char *add_usage[] =
43{
44 "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
45 "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
46 "\t-m\tUse \"message\" for the creation log.\n",
47 NULL
48};
49
50int
51add (argc, argv)
52 int argc;
53 char *argv[];
54{
55 char message[MAXMESGLEN];
56 char *user;
57 int i;
58 char *repository;
59 int c;
60 int err = 0;
61 int added_files = 0;
62 char *options = NULL;
63 List *entries;
64 Vers_TS *vers;
65
66 if (argc == 1 || argc == -1)
67 usage (add_usage);
68
69 /* parse args */
70 message[0] = '\0';
71 optind = 1;
72 while ((c = gnu_getopt (argc, argv, "k:m:")) != -1)
73 {
74 switch (c)
75 {
76 case 'k':
77 if (options)
78 free (options);
79 options = RCS_check_kflag (optarg);
80 break;
81
82 case 'm':
83 if (strlen (optarg) >= sizeof (message))
84 {
85 error (0, 0, "warning: message too long; truncated!");
86 (void) strncpy (message, optarg, sizeof (message));
87 message[sizeof (message) - 1] = '\0';
88 }
89 else
90 (void) strcpy (message, optarg);
91 break;
92 case '?':
93 default:
94 usage (add_usage);
95 break;
96 }
97 }
98 argc -= optind;
99 argv += optind;
100
101 if (argc <= 0)
102 usage (add_usage);
103
104 /* find the repository associated with our current dir */
105 repository = Name_Repository ((char *) NULL, (char *) NULL);
106 entries = ParseEntries (0);
107
108 /* walk the arg list adding files/dirs */
109 for (i = 0; i < argc; i++)
110 {
111 int begin_err = err;
112
113 user = argv[i];
114 if (index (user, '/') != NULL)
115 {
116 error (0, 0,
117 "cannot add files with '/' in their name; %s not added", user);
118 err++;
119 continue;
120 }
121
122 vers = Version_TS (repository, options, (char *) NULL, (char *) NULL,
123 user, 0, 0, entries, (List *) NULL);
124 if (vers->vn_user == NULL)
125 {
126 /* No entry available, ts_rcs is invalid */
127 if (vers->vn_rcs == NULL)
128 {
129 /* There is no RCS file either */
130 if (vers->ts_user == NULL)
131 {
132 /* There is no user file either */
133 error (0, 0, "nothing known about %s", user);
134 err++;
135 }
136 else if (!isdir (user))
137 {
138 /*
139 * See if a directory exists in the repository with
140 * the same name. If so, blow this request off.
141 */
142 char dname[PATH_MAX];
143 (void) sprintf (dname, "%s/%s", repository, user);
144 if (isdir (dname))
145 {
146 error (0, 0,
147 "cannot add file `%s' since the directory",
148 user);
149 error (0, 0, "`%s' already exists in the repository",
150 dname);
151 error (1, 0, "illegal filename overlap");
152 }
153
154 /* There is a user file, so build the entry for it */
155 if (build_entry (repository, user, vers->options,
156 message, entries) != 0)
157 err++;
158 else if (!quiet)
159 {
160 added_files++;
161 error (0, 0, "scheduling file `%s' for addition",
162 user);
163 }
164 }
165 }
166 else
167 {
168
169 /*
170 * There is an RCS file already, so somebody else must've
171 * added it
172 */
173 error (0, 0, "%s added independently by second party", user);
174 err++;
175 }
176 }
177 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
178 {
179
180 /*
181 * An entry for a new-born file, ts_rcs is dummy, but that is
182 * inappropriate here
183 */
184 error (0, 0, "%s has already been entered", user);
185 err++;
186 }
187 else if (vers->vn_user[0] == '-')
188 {
189 /* An entry for a removed file, ts_rcs is invalid */
190 if (vers->ts_user == NULL)
191 {
192 /* There is no user file (as it should be) */
193 if (vers->vn_rcs == NULL)
194 {
195
196 /*
197 * There is no RCS file, so somebody else must've removed
198 * it from under us
199 */
200 error (0, 0,
201 "cannot resurrect %s; RCS file removed by second party", user);
202 err++;
203 }
204 else
205 {
206
207 /*
208 * There is an RCS file, so remove the "-" from the
209 * version number and restore the file
210 */
211 char *tmp = xmalloc (strlen (user) + 50);
212
213 (void) strcpy (tmp, vers->vn_user + 1);
214 (void) strcpy (vers->vn_user, tmp);
215 (void) sprintf (tmp, "Resurrected %s", user);
216 Register (entries, user, vers->vn_user, tmp, vers->options,
217 vers->tag, vers->date);
218 free (tmp);
219
220 /* XXX - bugs here; this really resurrect the head */
221 if (update (2, argv + i - 1) == 0)
222 {
223 error (0, 0, "%s, version %s, resurrected", user,
224 vers->vn_user);
225 }
226 else
227 {
228 error (0, 0, "could not resurrect %s", user);
229 err++;
230 }
231 }
232 }
233 else
234 {
235 /* The user file shouldn't be there */
236 error (0, 0, "%s should be removed and is still there (or is back again)", user);
237 err++;
238 }
239 }
240 else
241 {
242 /* A normal entry, ts_rcs is valid, so it must already be there */
243 error (0, 0, "%s already exists, with version number %s", user,
244 vers->vn_user);
245 err++;
246 }
247 freevers_ts (&vers);
248
249 /* passed all the checks. Go ahead and add it if its a directory */
250 if (begin_err == err && isdir (user))
251 {
252 err += add_directory (repository, user);
253 continue;
254 }
255 }
256 if (added_files)
257 error (0, 0, "use 'cvs commit' to add %s permanently",
258 (added_files == 1) ? "this file" : "these files");
259 dellist (&entries);
260 return (err);
261}
262
263/*
264 * The specified user file is really a directory. So, let's make sure that
265 * it is created in the RCS source repository, and that the user's directory
266 * is updated to include a CVS directory.
267 *
268 * Returns 1 on failure, 0 on success.
269 */
270static int
271add_directory (repository, dir)
272 char *repository;
273 char *dir;
274{
275 char cwd[PATH_MAX], rcsdir[PATH_MAX];
276 char message[PATH_MAX + 100];
277 char *tag, *date;
278
279 if (index (dir, '/') != NULL)
280 {
281 error (0, 0,
282 "directory %s not added; must be a direct sub-directory", dir);
283 return (1);
284 }
285 if (strcmp (dir, CVSADM) == 0 || strcmp (dir, OCVSADM) == 0)
286 {
287 error (0, 0, "cannot add a `%s' or a `%s' directory", CVSADM, OCVSADM);
288 return (1);
289 }
290
291 /* before we do anything else, see if we have any per-directory tags */
292 ParseTag (&tag, &date);
293
294 /* now, remember where we were, so we can get back */
295 if (getwd (cwd) == NULL)
296 {
297 error (0, 0, "cannot get working directory: %s", cwd);
298 return (1);
299 }
300 if (chdir (dir) < 0)
301 {
302 error (0, errno, "cannot chdir to %s", dir);
303 return (1);
304 }
305 if (isfile (CVSADM) || isfile (OCVSADM))
306 {
307 error (0, 0,
308 "%s/%s (or %s/%s) already exists", dir, CVSADM, dir, OCVSADM);
309 goto out;
310 }
311
312 (void) sprintf (rcsdir, "%s/%s", repository, dir);
313 if (isfile (rcsdir) && !isdir (rcsdir))
314 {
315 error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
316 goto out;
317 }
318
319 /* setup the log message */
320 (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
321 if (tag)
322 {
323 (void) strcat (message, "--> Using per-directory sticky tag `");
324 (void) strcat (message, tag);
325 (void) strcat (message, "'\n");
326 }
327 if (date)
328 {
329 (void) strcat (message, "--> Using per-directory sticky date `");
330 (void) strcat (message, date);
331 (void) strcat (message, "'\n");
332 }
333
334 if (!isdir (rcsdir))
335 {
336 mode_t omask;
337 char line[MAXLINELEN];
338 Node *p;
339 List *ulist;
340
341 (void) printf ("Add directory %s to the repository (y/n) [n] ? ",
342 rcsdir);
343 (void) fflush (stdout);
344 clearerr (stdin);
345 if (fgets (line, sizeof (line), stdin) == NULL ||
346 (line[0] != 'y' && line[0] != 'Y'))
347 {
348 error (0, 0, "directory %s not added", rcsdir);
349 goto out;
350 }
351 omask = umask (2);
352 if (mkdir (rcsdir, 0777) < 0)
353 {
354 error (0, errno, "cannot mkdir %s", rcsdir);
355 (void) umask ((int) omask);
356 goto out;
357 }
358 (void) umask ((int) omask);
359
360 /*
361 * Set up an update list with a single title node for Update_Logfile
362 */
363 ulist = getlist ();
364 p = getnode ();
365 p->type = UPDATE;
366 p->delproc = update_delproc;
367 p->key = xstrdup ("- New directory");
368 p->data = (char *) T_TITLE;
369 (void) addnode (ulist, p);
370 Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist);
371 dellist (&ulist);
372 }
373
374 Create_Admin (".", rcsdir, tag, date);
375 if (tag)
376 free (tag);
377 if (date)
378 free (date);
379
380 (void) printf ("%s", message);
381out:
382 if (chdir (cwd) < 0)
383 error (1, errno, "cannot chdir to %s", cwd);
384 return (0);
385}
386
387/*
388 * Builds an entry for a new file and sets up "CVS/file",[pt] by
389 * interrogating the user. Returns non-zero on error.
390 */
391static int
392build_entry (repository, user, options, message, entries)
393 char *repository;
394 char *user;
395 char *options;
396 char *message;
397 List *entries;
398{
399 char fname[PATH_MAX];
400 char line[MAXLINELEN];
401 FILE *fp;
402
403 /*
404 * There may be an old file with the same name in the Attic! This is,
405 * perhaps, an awkward place to check for this, but other places are
406 * equally awkward.
407 */
408 (void) sprintf (fname, "%s/%s/%s%s", repository, CVSATTIC, user, RCSEXT);
409 if (isreadable (fname))
410 {
411 error (0, 0, "there is an old file %s already in %s/%s", user,
412 repository, CVSATTIC);
413 return (1);
414 }
415
416 if (noexec)
417 return (0);
418
419 /*
420 * The options for the "add" command are store in the file CVS/user,p
421 */
422 (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_OPT);
423 fp = open_file (fname, "w+");
424 if (fclose (fp) == EOF)
425 error(1, errno, "cannot close %s", fname);
426
427 /*
428 * And the requested log is read directly from the user and stored in the
429 * file user,t. If the "message" argument is set, use it as the
430 * initial creation log (which typically describes the file).
431 */
432 (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
433 fp = open_file (fname, "w+");
434 if (*message && fputs (message, fp) == EOF)
435 error (1, errno, "cannot write to %s", fname);
436 if (fclose(fp) == EOF)
437 error(1, errno, "cannot close %s", fname);
438
439 /*
440 * Create the entry now, since this allows the user to interrupt us above
441 * without needing to clean anything up (well, we could clean up the ,p
442 * and ,t files, but who cares).
443 */
444 (void) sprintf (line, "Initial %s", user);
445 Register (entries, user, "0", line, options, (char *) 0, (char *) 0);
446 return (0);
447}