add.c revision 25839
1356290Sjkim/*
2318899Sjkim * Copyright (c) 1992, Brian Berliner and Jeff Polk
3318899Sjkim * Copyright (c) 1989-1992, Brian Berliner
4318899Sjkim *
5318899Sjkim * You may distribute under the terms of the GNU General Public License as
6318899Sjkim * specified in the README file that comes with the CVS 1.4 kit.
7318899Sjkim *
8318899Sjkim * Add
9318899Sjkim *
10318899Sjkim * Adds a file or directory to the RCS source repository.  For a file,
11318899Sjkim * the entry is marked as "needing to be added" in the user's own CVS
12318899Sjkim * directory, and really added to the repository when it is committed.
13318899Sjkim * For a directory, it is added at the appropriate place in the source
14318899Sjkim * repository and a CVS directory is generated within the directory.
15318899Sjkim *
16318899Sjkim * The -m option is currently the only supported option.  Some may wish to
17318899Sjkim * supply standard "rcs" options here, but I've found that this causes more
18318899Sjkim * trouble than anything else.
19318899Sjkim *
20318899Sjkim * The user files or directories must already exist.  For a directory, it must
21318899Sjkim * not already have a CVS file in it.
22318899Sjkim *
23318899Sjkim * An "add" on a file that has been "remove"d but not committed will cause the
24318899Sjkim * file to be resurrected.
25318899Sjkim */
26318899Sjkim
27318899Sjkim#include "cvs.h"
28318899Sjkim#include "savecwd.h"
29318899Sjkim
30318899Sjkimstatic int add_directory PROTO((char *repository, List *, char *dir));
31318899Sjkimstatic int build_entry PROTO((char *repository, char *user, char *options,
32318899Sjkim		        char *message, List * entries, char *tag));
33318899Sjkim
34318899Sjkimstatic const char *const add_usage[] =
35318899Sjkim{
36318899Sjkim    "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
37318899Sjkim    "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
38318899Sjkim    "\t-m\tUse \"message\" for the creation log.\n",
39318899Sjkim    NULL
40318899Sjkim};
41318899Sjkim
42318899Sjkimstatic char *combine_dir PROTO ((char *, char *));
43318899Sjkim
44318899Sjkim/* Given a directory DIR and a subdirectory within it, SUBDIR, combine
45318899Sjkim   the two into a new directory name.  Returns a newly malloc'd string.
46318899Sjkim   For now this is a fairly simple affair, but perhaps it will want
47318899Sjkim   to have grander ambitions in the context of VMS or others (or perhaps
48318899Sjkim   not, perhaps that should all be hidden inside CVS_FOPEN and libc and so
49318899Sjkim   on, and CVS should just see foo/bar/baz style pathnames).  */
50318899Sjkimstatic char *
51318899Sjkimcombine_dir (dir, subdir)
52318899Sjkim    char *dir;
53318899Sjkim    char *subdir;
54318899Sjkim{
55318899Sjkim    char *retval;
56318899Sjkim    size_t dir_len;
57344604Sjkim
58344604Sjkim    dir_len = strlen (dir);
59344604Sjkim    retval = xmalloc (dir_len + strlen (subdir) + 10);
60344604Sjkim    if (dir_len >= 2
61344604Sjkim	&& dir[dir_len - 1] == '.'
62344604Sjkim	&& ISDIRSEP (dir[dir_len - 2]))
63318899Sjkim    {
64344604Sjkim	/* The dir name has an extraneous "." at the end.
65344604Sjkim	   I'm not completely sure that this is the best place
66344604Sjkim	   to strip it off--it is possible that Name_Repository
67344604Sjkim	   should do so, or it shouldn't be in the CVS/Repository
68318899Sjkim	   file in the first place.  Fixing it here seems like
69318899Sjkim	   a safe, small change, but I'm not sure it catches
70344604Sjkim	   all the cases.  */
71318899Sjkim	strncpy (retval, dir, dir_len - 2);
72318899Sjkim	retval[dir_len - 2] = '\0';
73318899Sjkim    }
74318899Sjkim    else
75318899Sjkim    {
76318899Sjkim	strcpy (retval, dir);
77318899Sjkim    }
78318899Sjkim    strcat (retval, "/");
79318899Sjkim    strcat (retval, subdir);
80318899Sjkim    return retval;
81318899Sjkim}
82318899Sjkim
83318899Sjkimint
84318899Sjkimadd (argc, argv)
85318899Sjkim    int argc;
86318899Sjkim    char **argv;
87318899Sjkim{
88318899Sjkim    char *message = NULL;
89318899Sjkim    char *user;
90318899Sjkim    int i;
91318899Sjkim    char *repository;
92318899Sjkim    int c;
93318899Sjkim    int err = 0;
94318899Sjkim    int added_files = 0;
95318899Sjkim    char *options = NULL;
96318899Sjkim    List *entries;
97318899Sjkim    Vers_TS *vers;
98318899Sjkim
99318899Sjkim    if (argc == 1 || argc == -1)
100318899Sjkim	usage (add_usage);
101318899Sjkim
102318899Sjkim    wrap_setup ();
103318899Sjkim
104318899Sjkim    /* parse args */
105318899Sjkim    optind = 1;
106318899Sjkim    while ((c = getopt (argc, argv, "+k:m:")) != -1)
107318899Sjkim    {
108318899Sjkim	switch (c)
109318899Sjkim	{
110318899Sjkim	    case 'k':
111318899Sjkim		if (options)
112318899Sjkim		    free (options);
113318899Sjkim		options = RCS_check_kflag (optarg);
114318899Sjkim		break;
115318899Sjkim
116318899Sjkim	    case 'm':
117318899Sjkim		message = xstrdup (optarg);
118318899Sjkim		break;
119318899Sjkim	    case '?':
120318899Sjkim	    default:
121318899Sjkim		usage (add_usage);
122318899Sjkim		break;
123318899Sjkim	}
124318899Sjkim    }
125318899Sjkim    argc -= optind;
126318899Sjkim    argv += optind;
127318899Sjkim
128318899Sjkim    if (argc <= 0)
129318899Sjkim	usage (add_usage);
130318899Sjkim
131318899Sjkim    /* find the repository associated with our current dir */
132318899Sjkim    repository = Name_Repository ((char *) NULL, (char *) NULL);
133318899Sjkim
134318899Sjkim#ifdef CLIENT_SUPPORT
135318899Sjkim    if (client_active)
136356290Sjkim    {
137318899Sjkim	int i;
138318899Sjkim	start_server ();
139318899Sjkim	ign_setup ();
140318899Sjkim	if (options) send_arg(options);
141318899Sjkim	option_with_arg ("-m", message);
142318899Sjkim	for (i = 0; i < argc; ++i)
143318899Sjkim	    /* FIXME: Does this erroneously call Create_Admin in error
144318899Sjkim	       conditions which are only detected once the server gets its
145318899Sjkim	       hands on things?  */
146318899Sjkim	    if (isdir (argv[i]))
147318899Sjkim	    {
148318899Sjkim		char *tag;
149318899Sjkim		char *date;
150318899Sjkim		int nonbranch;
151318899Sjkim		char *rcsdir;
152318899Sjkim
153318899Sjkim		/* before we do anything else, see if we have any
154318899Sjkim		   per-directory tags */
155318899Sjkim		ParseTag (&tag, &date, &nonbranch);
156318899Sjkim
157318899Sjkim		rcsdir = combine_dir (repository, argv[i]);
158318899Sjkim
159344604Sjkim		strip_trailing_slashes (argv[i]);
160318899Sjkim
161318899Sjkim		Create_Admin (argv[i], argv[i], rcsdir, tag, date, nonbranch);
162318899Sjkim
163318899Sjkim		if (tag)
164318899Sjkim		    free (tag);
165344604Sjkim		if (date)
166318899Sjkim		    free (date);
167318899Sjkim		free (rcsdir);
168344604Sjkim
169318899Sjkim		if (strchr (argv[i], '/') == NULL)
170318899Sjkim		    Subdir_Register ((List *) NULL, (char *) NULL, argv[i]);
171344604Sjkim		else
172318899Sjkim		{
173318899Sjkim		    char *cp, *b;
174318899Sjkim
175318899Sjkim		    cp = xstrdup (argv[i]);
176318899Sjkim		    b = strrchr (cp, '/');
177318899Sjkim		    *b++ = '\0';
178318899Sjkim		    Subdir_Register ((List *) NULL, cp, b);
179318899Sjkim		    free (cp);
180344604Sjkim		}
181344604Sjkim	    }
182318899Sjkim	send_file_names (argc, argv, SEND_EXPAND_WILD);
183318899Sjkim	/* FIXME: should be able to pass SEND_NO_CONTENTS, I think.  */
184344604Sjkim	send_files (argc, argv, 0, 0, SEND_BUILD_DIRS);
185344604Sjkim	send_to_server ("add\012", 0);
186318899Sjkim	if (message)
187318899Sjkim	    free (message);
188318899Sjkim	free (repository);
189318899Sjkim	return get_responses_and_close ();
190318899Sjkim    }
191318899Sjkim#endif
192318899Sjkim
193318899Sjkim    entries = Entries_Open (0);
194
195    /* walk the arg list adding files/dirs */
196    for (i = 0; i < argc; i++)
197    {
198	int begin_err = err;
199#ifdef SERVER_SUPPORT
200	int begin_added_files = added_files;
201#endif
202	struct file_info finfo;
203
204	user = argv[i];
205	strip_trailing_slashes (user);
206	if (strchr (user, '/') != NULL)
207	{
208	    error (0, 0,
209	     "cannot add files with '/' in their name; %s not added", user);
210	    err++;
211	    continue;
212	}
213
214	memset (&finfo, 0, sizeof finfo);
215	finfo.file = user;
216	finfo.update_dir = "";
217	finfo.fullname = user;
218	finfo.repository = repository;
219	finfo.entries = entries;
220	finfo.rcs = NULL;
221
222	/* We pass force_tag_match as 1.  If the directory has a
223           sticky branch tag, and there is already an RCS file which
224           does not have that tag, then the head revision is
225           meaningless to us.  */
226	vers = Version_TS (&finfo, options, NULL, NULL, 1, 0);
227	if (vers->vn_user == NULL)
228	{
229	    /* No entry available, ts_rcs is invalid */
230	    if (vers->vn_rcs == NULL)
231	    {
232		/* There is no RCS file either */
233		if (vers->ts_user == NULL)
234		{
235		    /* There is no user file either */
236		    error (0, 0, "nothing known about %s", user);
237		    err++;
238		}
239		else if (!isdir (user) || wrap_name_has (user, WRAP_TOCVS))
240		{
241		    /*
242		     * See if a directory exists in the repository with
243		     * the same name.  If so, blow this request off.
244		     */
245		    char *dname = xmalloc (strlen (repository) + strlen (user)
246					   + 10);
247		    (void) sprintf (dname, "%s/%s", repository, user);
248		    if (isdir (dname))
249		    {
250			error (0, 0,
251			       "cannot add file `%s' since the directory",
252			       user);
253			error (0, 0, "`%s' already exists in the repository",
254			       dname);
255			error (1, 0, "illegal filename overlap");
256		    }
257		    free (dname);
258
259		    if (vers->options == NULL || *vers->options == '\0')
260		    {
261			/* No options specified on command line (or in
262			   rcs file if it existed, e.g. the file exists
263			   on another branch).  Check for a value from
264			   the wrapper stuff.  */
265			if (wrap_name_has (user, WRAP_RCSOPTION))
266			{
267			    if (vers->options)
268				free (vers->options);
269			    vers->options = wrap_rcsoption (user, 1);
270			}
271		    }
272
273		    if (vers->nonbranch)
274		    {
275			error (0, 0,
276			       "cannot add file on non-branch tag %s",
277			       vers->tag);
278			++err;
279		    }
280		    else
281		    {
282			/* There is a user file, so build the entry for it */
283			if (build_entry (repository, user, vers->options,
284					 message, entries, vers->tag) != 0)
285			    err++;
286			else
287			{
288			    added_files++;
289			    if (!quiet)
290			    {
291				if (vers->tag)
292				    error (0, 0, "\
293scheduling %s `%s' for addition on branch `%s'",
294					   (wrap_name_has (user, WRAP_TOCVS)
295					    ? "wrapper"
296					    : "file"),
297					   user, vers->tag);
298				else
299				    error (0, 0,
300					   "scheduling %s `%s' for addition",
301					   (wrap_name_has (user, WRAP_TOCVS)
302					    ? "wrapper"
303					    : "file"),
304					   user);
305			    }
306			}
307		    }
308		}
309	    }
310	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
311	    {
312		if (isdir (user) && !wrap_name_has (user, WRAP_TOCVS))
313		{
314		    error (0, 0, "\
315the directory `%s' cannot be added because a file of the", user);
316		    error (1, 0, "\
317same name already exists in the repository.");
318		}
319		else
320		{
321		    if (vers->nonbranch)
322		    {
323			error (0, 0,
324			       "cannot add file on non-branch tag %s",
325			       vers->tag);
326			++err;
327		    }
328		    else
329		    {
330			if (vers->tag)
331			    error (0, 0, "\
332file `%s' will be added on branch `%s' from version %s",
333				   user, vers->tag, vers->vn_rcs);
334			else
335			    /* I'm not sure that mentioning
336			       vers->vn_rcs makes any sense here; I
337			       can't think of a way to word the
338			       message which is not confusing.  */
339			    error (0, 0, "\
340re-adding file %s (in place of dead revision %s)",
341				   user, vers->vn_rcs);
342			Register (entries, user, "0", vers->ts_user, NULL,
343				  vers->tag, NULL, NULL);
344			++added_files;
345		    }
346		}
347	    }
348	    else
349	    {
350		/*
351		 * There is an RCS file already, so somebody else must've
352		 * added it
353		 */
354		error (0, 0, "%s added independently by second party", user);
355		err++;
356	    }
357	}
358	else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
359	{
360
361	    /*
362	     * An entry for a new-born file, ts_rcs is dummy, but that is
363	     * inappropriate here
364	     */
365	    error (0, 0, "%s has already been entered", user);
366	    err++;
367	}
368	else if (vers->vn_user[0] == '-')
369	{
370	    /* An entry for a removed file, ts_rcs is invalid */
371	    if (vers->ts_user == NULL)
372	    {
373		/* There is no user file (as it should be) */
374		if (vers->vn_rcs == NULL)
375		{
376
377		    /*
378		     * There is no RCS file, so somebody else must've removed
379		     * it from under us
380		     */
381		    error (0, 0, "\
382cannot resurrect %s; RCS file removed by second party", user);
383		    err++;
384		}
385		else
386		{
387
388		    /*
389		     * There is an RCS file, so remove the "-" from the
390		     * version number and restore the file
391		     */
392		    char *tmp = xmalloc (strlen (user) + 50);
393
394		    (void) strcpy (tmp, vers->vn_user + 1);
395		    (void) strcpy (vers->vn_user, tmp);
396		    (void) sprintf (tmp, "Resurrected %s", user);
397		    Register (entries, user, vers->vn_user, tmp, vers->options,
398			      vers->tag, vers->date, vers->ts_conflict);
399		    free (tmp);
400
401		    /* XXX - bugs here; this really resurrect the head */
402		    /* Note that this depends on the Register above actually
403		       having written Entries, or else it won't really
404		       check the file out.  */
405		    if (update (2, argv + i - 1) == 0)
406		    {
407			error (0, 0, "%s, version %s, resurrected", user,
408			       vers->vn_user);
409		    }
410		    else
411		    {
412			error (0, 0, "could not resurrect %s", user);
413			err++;
414		    }
415		}
416	    }
417	    else
418	    {
419		/* The user file shouldn't be there */
420		error (0, 0, "\
421%s should be removed and is still there (or is back again)", user);
422		err++;
423	    }
424	}
425	else
426	{
427	    /* A normal entry, ts_rcs is valid, so it must already be there */
428	    error (0, 0, "%s already exists, with version number %s", user,
429		   vers->vn_user);
430	    err++;
431	}
432	freevers_ts (&vers);
433
434	/* passed all the checks.  Go ahead and add it if its a directory */
435	if (begin_err == err
436	    && isdir (user)
437	    && !wrap_name_has (user, WRAP_TOCVS))
438	{
439	    err += add_directory (repository, entries, user);
440	    continue;
441	}
442#ifdef SERVER_SUPPORT
443	if (server_active && begin_added_files != added_files)
444	    server_checked_in (user, ".", repository);
445#endif
446    }
447    if (added_files)
448	error (0, 0, "use 'cvs commit' to add %s permanently",
449	       (added_files == 1) ? "this file" : "these files");
450
451    Entries_Close (entries);
452
453    if (message)
454	free (message);
455    free (repository);
456
457    return (err);
458}
459
460/*
461 * The specified user file is really a directory.  So, let's make sure that
462 * it is created in the RCS source repository, and that the user's directory
463 * is updated to include a CVS directory.
464 *
465 * Returns 1 on failure, 0 on success.
466 */
467static int
468add_directory (repository, entries, dir)
469    char *repository;
470    List *entries;
471    char *dir;
472{
473    char *rcsdir = NULL;
474    struct saved_cwd cwd;
475    char *message = NULL;
476    char *tag, *date;
477    int nonbranch;
478
479    if (strchr (dir, '/') != NULL)
480    {
481	error (0, 0,
482	       "directory %s not added; must be a direct sub-directory", dir);
483	return (1);
484    }
485    if (strcmp (dir, CVSADM) == 0)
486    {
487	error (0, 0, "cannot add a `%s' directory", CVSADM);
488	return (1);
489    }
490
491    /* before we do anything else, see if we have any per-directory tags */
492    ParseTag (&tag, &date, &nonbranch);
493
494    /* now, remember where we were, so we can get back */
495    if (save_cwd (&cwd))
496	return (1);
497    if ( CVS_CHDIR (dir) < 0)
498    {
499	error (0, errno, "cannot chdir to %s", dir);
500	return (1);
501    }
502#ifdef SERVER_SUPPORT
503    if (!server_active && isfile (CVSADM))
504#else
505    if (isfile (CVSADM))
506#endif
507    {
508	error (0, 0, "%s/%s already exists", dir, CVSADM);
509	goto out;
510    }
511
512    rcsdir = combine_dir (repository, dir);
513    if (isfile (rcsdir) && !isdir (rcsdir))
514    {
515	error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
516	goto out;
517    }
518
519    /* setup the log message */
520    message = xmalloc (strlen (rcsdir) + 80);
521    (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
522    if (tag)
523    {
524	(void) strcat (message, "--> Using per-directory sticky tag `");
525	(void) strcat (message, tag);
526	(void) strcat (message, "'\n");
527    }
528    if (date)
529    {
530	(void) strcat (message, "--> Using per-directory sticky date `");
531	(void) strcat (message, date);
532	(void) strcat (message, "'\n");
533    }
534
535    if (!isdir (rcsdir))
536    {
537	mode_t omask;
538	Node *p;
539	List *ulist;
540	struct logfile_info *li;
541
542	/* There used to be some code here which would prompt for
543	   whether to add the directory.  The details of that code had
544	   bitrotted, but more to the point it can't work
545	   client/server, doesn't ask in the right way for GUIs, etc.
546	   A better way of making it harder to accidentally add
547	   directories would be to have to add and commit directories
548	   like for files.  The code was #if 0'd at least since CVS 1.5.  */
549
550	if (!noexec)
551	{
552	    omask = umask (cvsumask);
553	    if (CVS_MKDIR (rcsdir, 0777) < 0)
554	    {
555		error (0, errno, "cannot mkdir %s", rcsdir);
556		(void) umask (omask);
557		goto out;
558	    }
559	    (void) umask (omask);
560	}
561
562	/*
563	 * Set up an update list with a single title node for Update_Logfile
564	 */
565	ulist = getlist ();
566	p = getnode ();
567	p->type = UPDATE;
568	p->delproc = update_delproc;
569	p->key = xstrdup ("- New directory");
570	li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
571	li->type = T_TITLE;
572	li->tag = xstrdup (tag);
573	li->rev_old = li->rev_new = NULL;
574	p->data = (char *) li;
575	(void) addnode (ulist, p);
576	Update_Logfile (rcsdir, message, (FILE *) NULL, ulist);
577	dellist (&ulist);
578    }
579
580#ifdef SERVER_SUPPORT
581    if (!server_active)
582	Create_Admin (".", dir, rcsdir, tag, date, nonbranch);
583#else
584    Create_Admin (".", dir, rcsdir, tag, date, nonbranch);
585#endif
586    if (tag)
587	free (tag);
588    if (date)
589	free (date);
590
591    if (restore_cwd (&cwd, NULL))
592	error_exit ();
593    free_cwd (&cwd);
594
595    Subdir_Register (entries, (char *) NULL, dir);
596
597    (void) printf ("%s", message);
598    free (rcsdir);
599    free (message);
600
601    return (0);
602
603out:
604    if (restore_cwd (&cwd, NULL))
605	error_exit ();
606    free_cwd (&cwd);
607    if (rcsdir != NULL)
608	free (rcsdir);
609    return (0);
610}
611
612/*
613 * Builds an entry for a new file and sets up "CVS/file",[pt] by
614 * interrogating the user.  Returns non-zero on error.
615 */
616static int
617build_entry (repository, user, options, message, entries, tag)
618    char *repository;
619    char *user;
620    char *options;
621    char *message;
622    List *entries;
623    char *tag;
624{
625    char *fname;
626    char *line;
627    FILE *fp;
628
629    if (noexec)
630	return (0);
631
632    /*
633     * The requested log is read directly from the user and stored in the
634     * file user,t.  If the "message" argument is set, use it as the
635     * initial creation log (which typically describes the file).
636     */
637    fname = xmalloc (strlen (user) + 80);
638    (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
639    fp = open_file (fname, "w+");
640    if (message && fputs (message, fp) == EOF)
641	    error (1, errno, "cannot write to %s", fname);
642    if (fclose(fp) == EOF)
643        error(1, errno, "cannot close %s", fname);
644    free (fname);
645
646    /*
647     * Create the entry now, since this allows the user to interrupt us above
648     * without needing to clean anything up (well, we could clean up the
649     * ,t file, but who cares).
650     */
651    line = xmalloc (strlen (user) + 20);
652    (void) sprintf (line, "Initial %s", user);
653    Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
654    free (line);
655    return (0);
656}
657