add.c revision 34461
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 source distribution.
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#include "savecwd.h"
29#include "fileattr.h"
30
31static int add_directory PROTO ((struct file_info *finfo));
32static int build_entry PROTO((char *repository, char *user, char *options,
33		        char *message, List * entries, char *tag));
34
35static const char *const add_usage[] =
36{
37    "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
38    "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
39    "\t-m\tUse \"message\" for the creation log.\n",
40    "(Specify the --help global option for a list of other help options)\n",
41    NULL
42};
43
44int
45add (argc, argv)
46    int argc;
47    char **argv;
48{
49    char *message = NULL;
50    int i;
51    char *repository;
52    int c;
53    int err = 0;
54    int added_files = 0;
55    char *options = NULL;
56    List *entries;
57    Vers_TS *vers;
58    struct saved_cwd cwd;
59
60    if (argc == 1 || argc == -1)
61	usage (add_usage);
62
63    wrap_setup ();
64
65    /* parse args */
66    optind = 0;
67    while ((c = getopt (argc, argv, "+k:m:")) != -1)
68    {
69	switch (c)
70	{
71	    case 'k':
72		if (options)
73		    free (options);
74		options = RCS_check_kflag (optarg);
75		break;
76
77	    case 'm':
78		message = xstrdup (optarg);
79		break;
80	    case '?':
81	    default:
82		usage (add_usage);
83		break;
84	}
85    }
86    argc -= optind;
87    argv += optind;
88
89    if (argc <= 0)
90	usage (add_usage);
91
92    /* First some sanity checks.  I know that the CVS case is (sort of)
93       also handled by add_directory, but we need to check here so the
94       client won't get all confused in send_file_names.  */
95    for (i = 0; i < argc; i++)
96    {
97	int skip_file = 0;
98
99	/* If it were up to me I'd probably make this a fatal error.
100	   But some people are really fond of their "cvs add *", and
101	   don't seem to object to the warnings.
102	   Whatever.  */
103	strip_trailing_slashes (argv[i]);
104	if (strcmp (argv[i], ".") == 0
105	    || strcmp (argv[i], "..") == 0
106	    || fncmp (argv[i], CVSADM) == 0)
107	{
108	    error (0, 0, "cannot add special file `%s'; skipping", argv[i]);
109	    skip_file = 1;
110	}
111
112	if (skip_file)
113	{
114	    int j;
115
116	    /* FIXME: We don't do anything about free'ing argv[i].  But
117	       the problem is that it is only sometimes allocated (see
118	       cvsrc.c).  */
119
120	    for (j = i; j < argc - 1; ++j)
121		argv[j] = argv[j + 1];
122	    --argc;
123	    /* Check the new argv[i] again.  */
124	    --i;
125	    ++err;
126	}
127    }
128
129#ifdef CLIENT_SUPPORT
130    if (client_active)
131    {
132	int i;
133
134	if (argc == 0)
135	    /* We snipped out all the arguments in the above sanity
136	       check.  We can just forget the whole thing (and we
137	       better, because if we fired up the server and passed it
138	       nothing, it would spit back a usage message).  */
139	    return err;
140
141	start_server ();
142	ign_setup ();
143	if (options) send_arg(options);
144	option_with_arg ("-m", message);
145
146	repository = Name_Repository (NULL, NULL);
147	send_a_repository ("", repository, "");
148	free (repository);
149
150	for (i = 0; i < argc; ++i)
151	    /* FIXME: Does this erroneously call Create_Admin in error
152	       conditions which are only detected once the server gets its
153	       hands on things?  */
154	    if (isdir (argv[i]))
155	    {
156		char *tag;
157		char *date;
158		int nonbranch;
159		char *rcsdir;
160		char *p;
161		char *update_dir;
162		/* This is some mungeable storage into which we can point
163		   with p and/or update_dir.  */
164		char *filedir;
165
166		if (save_cwd (&cwd))
167		    error_exit ();
168
169		filedir = xstrdup (argv[i]);
170		p = last_component (filedir);
171		if (p == filedir)
172		{
173		    update_dir = "";
174		}
175		else
176		{
177		    p[-1] = '\0';
178		    update_dir = filedir;
179		    if (CVS_CHDIR (update_dir) < 0)
180			error (1, errno,
181			       "could not chdir to %s", update_dir);
182		}
183
184		/* find the repository associated with our current dir */
185		repository = Name_Repository (NULL, update_dir);
186
187		/* before we do anything else, see if we have any
188		   per-directory tags */
189		ParseTag (&tag, &date, &nonbranch);
190
191		rcsdir = xmalloc (strlen (repository) + strlen (p) + 5);
192		sprintf (rcsdir, "%s/%s", repository, p);
193
194		Create_Admin (p, argv[i], rcsdir, tag, date,
195			      nonbranch, 0);
196
197		send_a_repository ("", repository, update_dir);
198
199		if (restore_cwd (&cwd, NULL))
200		    error_exit ();
201		free_cwd (&cwd);
202
203		if (tag)
204		    free (tag);
205		if (date)
206		    free (date);
207		free (rcsdir);
208
209		if (p == filedir)
210		    Subdir_Register ((List *) NULL, (char *) NULL, argv[i]);
211		else
212		{
213		    Subdir_Register ((List *) NULL, update_dir, p);
214		}
215		free (repository);
216		free (filedir);
217	    }
218	send_file_names (argc, argv, SEND_EXPAND_WILD);
219	send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS);
220	send_to_server ("add\012", 0);
221	if (message)
222	    free (message);
223	return err + get_responses_and_close ();
224    }
225#endif
226
227    /* walk the arg list adding files/dirs */
228    for (i = 0; i < argc; i++)
229    {
230	int begin_err = err;
231#ifdef SERVER_SUPPORT
232	int begin_added_files = added_files;
233#endif
234	struct file_info finfo;
235	char *p;
236
237	memset (&finfo, 0, sizeof finfo);
238
239	if (save_cwd (&cwd))
240	    error_exit ();
241
242	finfo.fullname = xstrdup (argv[i]);
243	p = last_component (argv[i]);
244	if (p == argv[i])
245	{
246	    finfo.update_dir = "";
247	    finfo.file = p;
248	}
249	else
250	{
251	    p[-1] = '\0';
252	    finfo.update_dir = argv[i];
253	    finfo.file = p;
254	    if (CVS_CHDIR (finfo.update_dir) < 0)
255		error (1, errno, "could not chdir to %s", finfo.update_dir);
256	}
257
258	/* Add wrappers for this directory.  They exist only until
259	   the next call to wrap_add_file.  */
260	wrap_add_file (CVSDOTWRAPPER, 1);
261
262	finfo.rcs = NULL;
263
264	/* Find the repository associated with our current dir.  */
265	repository = Name_Repository (NULL, finfo.update_dir);
266
267	entries = Entries_Open (0, NULL);
268
269	finfo.repository = repository;
270	finfo.entries = entries;
271
272	/* We pass force_tag_match as 1.  If the directory has a
273           sticky branch tag, and there is already an RCS file which
274           does not have that tag, then the head revision is
275           meaningless to us.  */
276	vers = Version_TS (&finfo, options, NULL, NULL, 1, 0);
277	if (vers->vn_user == NULL)
278	{
279	    /* No entry available, ts_rcs is invalid */
280	    if (vers->vn_rcs == NULL)
281	    {
282		/* There is no RCS file either */
283		if (vers->ts_user == NULL)
284		{
285		    /* There is no user file either */
286		    error (0, 0, "nothing known about %s", finfo.fullname);
287		    err++;
288		}
289		else if (!isdir (finfo.file)
290			 || wrap_name_has (finfo.file, WRAP_TOCVS))
291		{
292		    /*
293		     * See if a directory exists in the repository with
294		     * the same name.  If so, blow this request off.
295		     */
296		    char *dname = xmalloc (strlen (repository)
297					   + strlen (finfo.file)
298					   + 10);
299		    (void) sprintf (dname, "%s/%s", repository, finfo.file);
300		    if (isdir (dname))
301		    {
302			error (0, 0,
303			       "cannot add file `%s' since the directory",
304			       finfo.fullname);
305			error (0, 0, "`%s' already exists in the repository",
306			       dname);
307			error (1, 0, "illegal filename overlap");
308		    }
309		    free (dname);
310
311		    if (vers->options == NULL || *vers->options == '\0')
312		    {
313			/* No options specified on command line (or in
314			   rcs file if it existed, e.g. the file exists
315			   on another branch).  Check for a value from
316			   the wrapper stuff.  */
317			if (wrap_name_has (finfo.file, WRAP_RCSOPTION))
318			{
319			    if (vers->options)
320				free (vers->options);
321			    vers->options = wrap_rcsoption (finfo.file, 1);
322			}
323		    }
324
325		    if (vers->nonbranch)
326		    {
327			error (0, 0,
328			       "cannot add file on non-branch tag %s",
329			       vers->tag);
330			++err;
331		    }
332		    else
333		    {
334			/* There is a user file, so build the entry for it */
335			if (build_entry (repository, finfo.file, vers->options,
336					 message, entries, vers->tag) != 0)
337			    err++;
338			else
339			{
340			    added_files++;
341			    if (!quiet)
342			    {
343				if (vers->tag)
344				    error (0, 0, "\
345scheduling %s `%s' for addition on branch `%s'",
346					   (wrap_name_has (finfo.file,
347							   WRAP_TOCVS)
348					    ? "wrapper"
349					    : "file"),
350					   finfo.fullname, vers->tag);
351				else
352				    error (0, 0,
353					   "scheduling %s `%s' for addition",
354					   (wrap_name_has (finfo.file,
355							   WRAP_TOCVS)
356					    ? "wrapper"
357					    : "file"),
358					   finfo.fullname);
359			    }
360			}
361		    }
362		}
363	    }
364	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
365	    {
366		if (isdir (finfo.file)
367		    && !wrap_name_has (finfo.file, WRAP_TOCVS))
368		{
369		    error (0, 0, "\
370the directory `%s' cannot be added because a file of the", finfo.fullname);
371		    error (1, 0, "\
372same name already exists in the repository.");
373		}
374		else
375		{
376		    if (vers->nonbranch)
377		    {
378			error (0, 0,
379			       "cannot add file on non-branch tag %s",
380			       vers->tag);
381			++err;
382		    }
383		    else
384		    {
385			if (vers->tag)
386			    error (0, 0, "\
387file `%s' will be added on branch `%s' from version %s",
388				   finfo.fullname, vers->tag, vers->vn_rcs);
389			else
390			    /* I'm not sure that mentioning
391			       vers->vn_rcs makes any sense here; I
392			       can't think of a way to word the
393			       message which is not confusing.  */
394			    error (0, 0, "\
395re-adding file %s (in place of dead revision %s)",
396				   finfo.fullname, vers->vn_rcs);
397			Register (entries, finfo.file, "0", vers->ts_user,
398				  NULL,
399				  vers->tag, NULL, NULL);
400			++added_files;
401		    }
402		}
403	    }
404	    else
405	    {
406		/*
407		 * There is an RCS file already, so somebody else must've
408		 * added it
409		 */
410		error (0, 0, "%s added independently by second party",
411		       finfo.fullname);
412		err++;
413	    }
414	}
415	else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
416	{
417
418	    /*
419	     * An entry for a new-born file, ts_rcs is dummy, but that is
420	     * inappropriate here
421	     */
422	    error (0, 0, "%s has already been entered", finfo.fullname);
423	    err++;
424	}
425	else if (vers->vn_user[0] == '-')
426	{
427	    /* An entry for a removed file, ts_rcs is invalid */
428	    if (vers->ts_user == NULL)
429	    {
430		/* There is no user file (as it should be) */
431		if (vers->vn_rcs == NULL)
432		{
433
434		    /*
435		     * There is no RCS file, so somebody else must've removed
436		     * it from under us
437		     */
438		    error (0, 0, "\
439cannot resurrect %s; RCS file removed by second party", finfo.fullname);
440		    err++;
441		}
442		else
443		{
444
445		    /*
446		     * There is an RCS file, so remove the "-" from the
447		     * version number and restore the file
448		     */
449		    char *tmp = xmalloc (strlen (finfo.file) + 50);
450
451		    (void) strcpy (tmp, vers->vn_user + 1);
452		    (void) strcpy (vers->vn_user, tmp);
453		    (void) sprintf (tmp, "Resurrected %s", finfo.file);
454		    Register (entries, finfo.file, vers->vn_user, tmp,
455			      vers->options,
456			      vers->tag, vers->date, vers->ts_conflict);
457		    free (tmp);
458
459		    /* XXX - bugs here; this really resurrect the head */
460		    /* Note that this depends on the Register above actually
461		       having written Entries, or else it won't really
462		       check the file out.  */
463		    if (update (2, argv + i - 1) == 0)
464		    {
465			error (0, 0, "%s, version %s, resurrected",
466			       finfo.fullname,
467			       vers->vn_user);
468		    }
469		    else
470		    {
471			error (0, 0, "could not resurrect %s", finfo.fullname);
472			err++;
473		    }
474		}
475	    }
476	    else
477	    {
478		/* The user file shouldn't be there */
479		error (0, 0, "\
480%s should be removed and is still there (or is back again)", finfo.fullname);
481		err++;
482	    }
483	}
484	else
485	{
486	    /* A normal entry, ts_rcs is valid, so it must already be there */
487	    error (0, 0, "%s already exists, with version number %s",
488		   finfo.fullname,
489		   vers->vn_user);
490	    err++;
491	}
492	freevers_ts (&vers);
493
494	/* passed all the checks.  Go ahead and add it if its a directory */
495	if (begin_err == err
496	    && isdir (finfo.file)
497	    && !wrap_name_has (finfo.file, WRAP_TOCVS))
498	{
499	    err += add_directory (&finfo);
500	}
501	else
502	{
503#ifdef SERVER_SUPPORT
504	    if (server_active && begin_added_files != added_files)
505		server_checked_in (finfo.file, finfo.update_dir, repository);
506#endif
507	}
508	free (repository);
509	Entries_Close (entries);
510
511	if (restore_cwd (&cwd, NULL))
512	    error_exit ();
513	free_cwd (&cwd);
514
515	free (finfo.fullname);
516    }
517    if (added_files)
518	error (0, 0, "use '%s commit' to add %s permanently",
519	       program_name,
520	       (added_files == 1) ? "this file" : "these files");
521
522    if (message)
523	free (message);
524
525    return (err);
526}
527
528/*
529 * The specified user file is really a directory.  So, let's make sure that
530 * it is created in the RCS source repository, and that the user's directory
531 * is updated to include a CVS directory.
532 *
533 * Returns 1 on failure, 0 on success.
534 */
535static int
536add_directory (finfo)
537    struct file_info *finfo;
538{
539    char *repository = finfo->repository;
540    List *entries = finfo->entries;
541    char *dir = finfo->file;
542
543    char *rcsdir = NULL;
544    struct saved_cwd cwd;
545    char *message = NULL;
546    char *tag, *date;
547    int nonbranch;
548    char *attrs;
549
550    if (strchr (dir, '/') != NULL)
551    {
552	/* "Can't happen".  */
553	error (0, 0,
554	       "directory %s not added; must be a direct sub-directory", dir);
555	return (1);
556    }
557    if (fncmp (dir, CVSADM) == 0)
558    {
559	error (0, 0, "cannot add a `%s' directory", CVSADM);
560	return (1);
561    }
562
563    /* before we do anything else, see if we have any per-directory tags */
564    ParseTag (&tag, &date, &nonbranch);
565
566    /* Remember the default attributes from this directory, so we can apply
567       them to the new directory.  */
568    fileattr_startdir (repository);
569    attrs = fileattr_getall (NULL);
570    fileattr_free ();
571
572    /* now, remember where we were, so we can get back */
573    if (save_cwd (&cwd))
574	return (1);
575    if ( CVS_CHDIR (dir) < 0)
576    {
577	error (0, errno, "cannot chdir to %s", finfo->fullname);
578	return (1);
579    }
580#ifdef SERVER_SUPPORT
581    if (!server_active && isfile (CVSADM))
582#else
583    if (isfile (CVSADM))
584#endif
585    {
586	error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM);
587	goto out;
588    }
589
590    rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5);
591    sprintf (rcsdir, "%s/%s", repository, dir);
592    if (isfile (rcsdir) && !isdir (rcsdir))
593    {
594	error (0, 0, "%s is not a directory; %s not added", rcsdir,
595	       finfo->fullname);
596	goto out;
597    }
598
599    /* setup the log message */
600    message = xmalloc (strlen (rcsdir)
601		       + 80
602		       + (tag == NULL ? 0 : strlen (tag) + 80)
603		       + (date == NULL ? 0 : strlen (date) + 80));
604    (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
605    if (tag)
606    {
607	(void) strcat (message, "--> Using per-directory sticky tag `");
608	(void) strcat (message, tag);
609	(void) strcat (message, "'\n");
610    }
611    if (date)
612    {
613	(void) strcat (message, "--> Using per-directory sticky date `");
614	(void) strcat (message, date);
615	(void) strcat (message, "'\n");
616    }
617
618    if (!isdir (rcsdir))
619    {
620	mode_t omask;
621	Node *p;
622	List *ulist;
623	struct logfile_info *li;
624
625	/* There used to be some code here which would prompt for
626	   whether to add the directory.  The details of that code had
627	   bitrotted, but more to the point it can't work
628	   client/server, doesn't ask in the right way for GUIs, etc.
629	   A better way of making it harder to accidentally add
630	   directories would be to have to add and commit directories
631	   like for files.  The code was #if 0'd at least since CVS 1.5.  */
632
633	if (!noexec)
634	{
635	    omask = umask (cvsumask);
636	    if (CVS_MKDIR (rcsdir, 0777) < 0)
637	    {
638		error (0, errno, "cannot mkdir %s", rcsdir);
639		(void) umask (omask);
640		goto out;
641	    }
642	    (void) umask (omask);
643	}
644
645	/* Now set the default file attributes to the ones we inherited
646	   from the parent directory.  */
647	fileattr_startdir (rcsdir);
648	fileattr_setall (NULL, attrs);
649	fileattr_write ();
650	fileattr_free ();
651	if (attrs != NULL)
652	    free (attrs);
653
654	/*
655	 * Set up an update list with a single title node for Update_Logfile
656	 */
657	ulist = getlist ();
658	p = getnode ();
659	p->type = UPDATE;
660	p->delproc = update_delproc;
661	p->key = xstrdup ("- New directory");
662	li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
663	li->type = T_TITLE;
664	li->tag = xstrdup (tag);
665	li->rev_old = li->rev_new = NULL;
666	p->data = (char *) li;
667	(void) addnode (ulist, p);
668	Update_Logfile (rcsdir, message, (FILE *) NULL, ulist);
669	dellist (&ulist);
670    }
671
672#ifdef SERVER_SUPPORT
673    if (!server_active)
674	Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0);
675#else
676    Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0);
677#endif
678    if (tag)
679	free (tag);
680    if (date)
681	free (date);
682
683    if (restore_cwd (&cwd, NULL))
684	error_exit ();
685    free_cwd (&cwd);
686
687    Subdir_Register (entries, (char *) NULL, dir);
688
689    cvs_output (message, 0);
690
691    free (rcsdir);
692    free (message);
693
694    return (0);
695
696out:
697    if (restore_cwd (&cwd, NULL))
698	error_exit ();
699    free_cwd (&cwd);
700    if (rcsdir != NULL)
701	free (rcsdir);
702    return (0);
703}
704
705/*
706 * Builds an entry for a new file and sets up "CVS/file",[pt] by
707 * interrogating the user.  Returns non-zero on error.
708 */
709static int
710build_entry (repository, user, options, message, entries, tag)
711    char *repository;
712    char *user;
713    char *options;
714    char *message;
715    List *entries;
716    char *tag;
717{
718    char *fname;
719    char *line;
720    FILE *fp;
721
722    if (noexec)
723	return (0);
724
725    /*
726     * The requested log is read directly from the user and stored in the
727     * file user,t.  If the "message" argument is set, use it as the
728     * initial creation log (which typically describes the file).
729     */
730    fname = xmalloc (strlen (user) + 80);
731    (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
732    fp = open_file (fname, "w+");
733    if (message && fputs (message, fp) == EOF)
734	    error (1, errno, "cannot write to %s", fname);
735    if (fclose(fp) == EOF)
736        error(1, errno, "cannot close %s", fname);
737    free (fname);
738
739    /*
740     * Create the entry now, since this allows the user to interrupt us above
741     * without needing to clean anything up (well, we could clean up the
742     * ,t file, but who cares).
743     */
744    line = xmalloc (strlen (user) + 20);
745    (void) sprintf (line, "Initial %s", user);
746    Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
747    free (line);
748    return (0);
749}
750