add.c revision 128266
162053Smarkm/*
262765Smarkm * Copyright (c) 1992, Brian Berliner and Jeff Polk
362053Smarkm * Copyright (c) 1989-1992, Brian Berliner
462053Smarkm *
562053Smarkm * You may distribute under the terms of the GNU General Public License as
662053Smarkm * specified in the README file that comes with the CVS source distribution.
762053Smarkm *
862053Smarkm * Add
962053Smarkm *
1062053Smarkm * Adds a file or directory to the RCS source repository.  For a file,
1162053Smarkm * the entry is marked as "needing to be added" in the user's own CVS
1262053Smarkm * directory, and really added to the repository when it is committed.
1362053Smarkm * For a directory, it is added at the appropriate place in the source
1462053Smarkm * repository and a CVS directory is generated within the directory.
1562053Smarkm *
1662053Smarkm * The -m option is currently the only supported option.  Some may wish to
1762053Smarkm * supply standard "rcs" options here, but I've found that this causes more
1862053Smarkm * trouble than anything else.
1962053Smarkm *
2062053Smarkm * The user files or directories must already exist.  For a directory, it must
2162053Smarkm * not already have a CVS file in it.
2262053Smarkm *
2362053Smarkm * An "add" on a file that has been "remove"d but not committed will cause the
2462053Smarkm * file to be resurrected.
2562053Smarkm */
2662053Smarkm
2762053Smarkm#include <assert.h>
2862053Smarkm#include "cvs.h"
2962053Smarkm#include "savecwd.h"
3062765Smarkm#include "fileattr.h"
3162053Smarkm
3262053Smarkmstatic int add_directory PROTO ((struct file_info *finfo));
3362053Smarkmstatic int build_entry PROTO((const char *repository, const char *user,
3462053Smarkm                              const char *options, const char *message,
3562053Smarkm                              List * entries, const char *tag));
3662053Smarkm
3762053Smarkmstatic const char *const add_usage[] =
3862053Smarkm{
3967112Smarkm    "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
4066044Srwatson    "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
4167112Smarkm    "\t-m\tUse \"message\" for the creation log.\n",
4262053Smarkm    "(Specify the --help global option for a list of other help options)\n",
4367112Smarkm    NULL
4462053Smarkm};
4562053Smarkm
4662053Smarkmint
4762053Smarkmadd (argc, argv)
4862053Smarkm    int argc;
4967112Smarkm    char **argv;
5067112Smarkm{
5162053Smarkm    char *message = NULL;
5263855Smarkm    int i;
5362765Smarkm    char *repository;
5462765Smarkm    int c;
5565686Smarkm    int err = 0;
5667112Smarkm    int added_files = 0;
5762053Smarkm    char *options = NULL;
5862053Smarkm    List *entries;
5962053Smarkm    Vers_TS *vers;
6062149Smarkm    struct saved_cwd cwd;
6162053Smarkm    /* Nonzero if we found a slash, and are thus adding files in a
6262053Smarkm       subdirectory.  */
6363855Smarkm    int found_slash = 0;
6462053Smarkm    size_t cvsroot_len;
6562765Smarkm
6662765Smarkm    if (argc == 1 || argc == -1)
6765686Smarkm	usage (add_usage);
6867112Smarkm
6962053Smarkm    wrap_setup ();
7062053Smarkm
7162053Smarkm    /* parse args */
7262053Smarkm    optind = 0;
7362053Smarkm    while ((c = getopt (argc, argv, "+k:m:")) != -1)
7462053Smarkm    {
7562053Smarkm	switch (c)
7662053Smarkm	{
7762053Smarkm	    case 'k':
7862053Smarkm		if (options)
7962053Smarkm		    free (options);
8062765Smarkm		options = RCS_check_kflag (optarg);
8165686Smarkm		break;
8262053Smarkm
8362053Smarkm	    case 'm':
8462053Smarkm		message = xstrdup (optarg);
8562840Smarkm		break;
8662840Smarkm	    case '?':
8762840Smarkm	    default:
8862840Smarkm		usage (add_usage);
8962840Smarkm		break;
9062840Smarkm	}
9162840Smarkm    }
9263855Smarkm    argc -= optind;
9362840Smarkm    argv += optind;
9462840Smarkm
9562053Smarkm    if (argc <= 0)
9662053Smarkm	usage (add_usage);
9763855Smarkm
9863855Smarkm    cvsroot_len = strlen (current_parsed_root->directory);
9963855Smarkm
10063855Smarkm    /* First some sanity checks.  I know that the CVS case is (sort of)
10163855Smarkm       also handled by add_directory, but we need to check here so the
10263855Smarkm       client won't get all confused in send_file_names.  */
10363855Smarkm    for (i = 0; i < argc; i++)
10463855Smarkm    {
10563855Smarkm	int skip_file = 0;
10662765Smarkm
10762053Smarkm	/* If it were up to me I'd probably make this a fatal error.
10862053Smarkm	   But some people are really fond of their "cvs add *", and
10962053Smarkm	   don't seem to object to the warnings.
11062840Smarkm	   Whatever.  */
11162053Smarkm	strip_trailing_slashes (argv[i]);
11267112Smarkm	if (strcmp (argv[i], ".") == 0
11367112Smarkm	    || strcmp (argv[i], "..") == 0
11462053Smarkm	    || fncmp (argv[i], CVSADM) == 0)
11567112Smarkm	{
11667112Smarkm	    if (!quiet)
11767112Smarkm		error (0, 0, "cannot add special file `%s'; skipping", argv[i]);
11867112Smarkm	    skip_file = 1;
11967112Smarkm	}
12067112Smarkm	else
12167112Smarkm	{
12267112Smarkm	    char *p;
12367112Smarkm	    p = argv[i];
12467112Smarkm	    while (*p != '\0')
12567112Smarkm	    {
12667112Smarkm		if (ISDIRSEP (*p))
12767112Smarkm		{
12862053Smarkm		    found_slash = 1;
12962053Smarkm		    break;
13062053Smarkm		}
13162053Smarkm		++p;
13262765Smarkm	    }
13362053Smarkm	}
13462053Smarkm
13562053Smarkm	if (skip_file)
13662840Smarkm	{
13762053Smarkm	    int j;
13862765Smarkm
13962053Smarkm	    /* FIXME: We don't do anything about free'ing argv[i].  But
14062053Smarkm	       the problem is that it is only sometimes allocated (see
14162765Smarkm	       cvsrc.c).  */
14262053Smarkm
14362053Smarkm	    for (j = i; j < argc - 1; ++j)
14463306Smarkm		argv[j] = argv[j + 1];
14562053Smarkm	    --argc;
14662765Smarkm	    /* Check the new argv[i] again.  */
14762053Smarkm	    --i;
14862053Smarkm	    ++err;
14962053Smarkm	}
15062053Smarkm    }
15165686Smarkm
15265686Smarkm#ifdef CLIENT_SUPPORT
15365686Smarkm    if (current_parsed_root->isremote)
15465686Smarkm    {
15565686Smarkm	int j;
15665686Smarkm
15767112Smarkm	if (argc == 0)
15867112Smarkm	    /* We snipped out all the arguments in the above sanity
15967112Smarkm	       check.  We can just forget the whole thing (and we
16067112Smarkm	       better, because if we fired up the server and passed it
16167112Smarkm	       nothing, it would spit back a usage message).  */
16267112Smarkm	    return err;
16367112Smarkm
16467112Smarkm	start_server ();
16567112Smarkm	ign_setup ();
16667112Smarkm	if (options)
16767112Smarkm	{
16867112Smarkm	    send_arg (options);
16967112Smarkm	    free (options);
17067112Smarkm	}
17167112Smarkm	option_with_arg ("-m", message);
17262053Smarkm	send_arg ("--");
17362053Smarkm
17465686Smarkm	/* If !found_slash, refrain from sending "Directory", for
17565686Smarkm	   CVS 1.9 compatibility.  If we only tried to deal with servers
17662053Smarkm	   which are at least CVS 1.9.26 or so, we wouldn't have to
17762053Smarkm	   special-case this.  */
17865686Smarkm	if (found_slash)
17965686Smarkm	{
18065686Smarkm	    repository = Name_Repository (NULL, NULL);
18162053Smarkm	    send_a_repository ("", repository, "");
18262053Smarkm	    free (repository);
18362765Smarkm	}
18462149Smarkm
18562765Smarkm	for (j = 0; j < argc; ++j)
18665686Smarkm	{
18762053Smarkm	    /* FIXME: Does this erroneously call Create_Admin in error
18862053Smarkm	       conditions which are only detected once the server gets its
18962053Smarkm	       hands on things?  */
19062765Smarkm	    /* FIXME-also: if filenames are case-insensitive on the
19162765Smarkm	       client, and the directory in the repository already
19265686Smarkm	       exists and is named "foo", and the command is "cvs add
19362053Smarkm	       FOO", this call to Create_Admin puts the wrong thing in
19462053Smarkm	       CVS/Repository and so a subsequent "cvs update" will
19562053Smarkm	       give an error.  The fix will be to have the server report
19662053Smarkm	       back what it actually did (e.g. use tagged text for the
19762053Smarkm	       "Directory %s added" message), and then Create_Admin,
19862053Smarkm	       which should also fix the error handling concerns.  */
19962053Smarkm
20062053Smarkm	    if (isdir (argv[j]))
20162053Smarkm	    {
20262053Smarkm		char *tag;
20362053Smarkm		char *date;
204		int nonbranch;
205		char *rcsdir;
206		char *p;
207		char *update_dir;
208		/* This is some mungeable storage into which we can point
209		   with p and/or update_dir.  */
210		char *filedir;
211
212		if (save_cwd (&cwd))
213		    error_exit ();
214
215		filedir = xstrdup (argv[j]);
216                /* Deliberately discard the const below since we know we just
217                 * allocated filedir and can do what we like with it.
218                 */
219		p = (char *)last_component (filedir);
220		if (p == filedir)
221		{
222		    update_dir = "";
223		}
224		else
225		{
226		    p[-1] = '\0';
227		    update_dir = filedir;
228		    if (CVS_CHDIR (update_dir) < 0)
229			error (1, errno,
230			       "could not chdir to %s", update_dir);
231		}
232
233		/* find the repository associated with our current dir */
234		repository = Name_Repository (NULL, update_dir);
235
236		/* don't add stuff to Emptydir */
237		if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0
238		    && ISDIRSEP (repository[cvsroot_len])
239		    && strncmp (repository + cvsroot_len + 1,
240				CVSROOTADM,
241				sizeof CVSROOTADM - 1) == 0
242		    && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM])
243		    && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1,
244			       CVSNULLREPOS) == 0)
245		    error (1, 0, "cannot add to %s", repository);
246
247		/* before we do anything else, see if we have any
248		   per-directory tags */
249		ParseTag (&tag, &date, &nonbranch);
250
251		rcsdir = xmalloc (strlen (repository) + strlen (p) + 5);
252		sprintf (rcsdir, "%s/%s", repository, p);
253
254		Create_Admin (p, argv[j], rcsdir, tag, date,
255			      nonbranch, 0, 1);
256
257		if (found_slash)
258		    send_a_repository ("", repository, update_dir);
259
260		if (restore_cwd (&cwd, NULL))
261		    error_exit ();
262		free_cwd (&cwd);
263
264		if (tag)
265		    free (tag);
266		if (date)
267		    free (date);
268		free (rcsdir);
269
270		if (p == filedir)
271		    Subdir_Register ((List *) NULL, (char *) NULL, argv[j]);
272		else
273		{
274		    Subdir_Register ((List *) NULL, update_dir, p);
275		}
276		free (repository);
277		free (filedir);
278	    }
279	}
280	send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS);
281	send_file_names (argc, argv, SEND_EXPAND_WILD);
282	send_to_server ("add\012", 0);
283	if (message)
284	    free (message);
285	return err + get_responses_and_close ();
286    }
287#endif
288
289    /* walk the arg list adding files/dirs */
290    for (i = 0; i < argc; i++)
291    {
292	int begin_err = err;
293#ifdef SERVER_SUPPORT
294	int begin_added_files = added_files;
295#endif
296	struct file_info finfo;
297	char *filename, *p;
298
299	memset (&finfo, 0, sizeof finfo);
300
301	if (save_cwd (&cwd))
302	    error_exit ();
303
304	finfo.fullname = xstrdup (argv[i]);
305	filename = xstrdup (argv[i]);
306	/* We know we can discard the const below since we just allocated
307	 * filename and can do as we like with it.
308         */
309	p = (char *)last_component (filename);
310	if (p == filename)
311	{
312	    finfo.update_dir = "";
313	    finfo.file = p;
314	}
315	else
316	{
317	    p[-1] = '\0';
318	    finfo.update_dir = filename;
319	    finfo.file = p;
320	    if (CVS_CHDIR (finfo.update_dir) < 0)
321		error (1, errno, "could not chdir to %s", finfo.update_dir);
322	}
323
324	/* Add wrappers for this directory.  They exist only until
325	   the next call to wrap_add_file.  */
326	wrap_add_file (CVSDOTWRAPPER, 1);
327
328	finfo.rcs = NULL;
329
330	/* Find the repository associated with our current dir.  */
331	repository = Name_Repository (NULL, finfo.update_dir);
332
333	/* don't add stuff to Emptydir */
334	if (strncmp (repository, current_parsed_root->directory,
335                     cvsroot_len) == 0
336	    && ISDIRSEP (repository[cvsroot_len])
337	    && strncmp (repository + cvsroot_len + 1,
338			CVSROOTADM,
339			sizeof CVSROOTADM - 1) == 0
340	    && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM])
341	    && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1,
342		       CVSNULLREPOS) == 0)
343	    error (1, 0, "cannot add to %s", repository);
344
345	entries = Entries_Open (0, NULL);
346
347	finfo.repository = repository;
348	finfo.entries = entries;
349
350	/* We pass force_tag_match as 1.  If the directory has a
351           sticky branch tag, and there is already an RCS file which
352           does not have that tag, then the head revision is
353           meaningless to us.  */
354	vers = Version_TS (&finfo, options, NULL, NULL, 1, 0);
355
356	if (vers->vn_user == NULL)
357	{
358	    /* No entry available, ts_rcs is invalid */
359	    if (vers->vn_rcs == NULL)
360	    {
361		/* There is no RCS file either */
362		if (vers->ts_user == NULL)
363		{
364		    /* There is no user file either */
365		    error (0, 0, "nothing known about %s", finfo.fullname);
366		    err++;
367		}
368		else if (!isdir (finfo.file)
369			 || wrap_name_has (finfo.file, WRAP_TOCVS))
370		{
371		    /*
372		     * See if a directory exists in the repository with
373		     * the same name.  If so, blow this request off.
374		     */
375		    char *dname = xmalloc (strlen (repository)
376					   + strlen (finfo.file)
377					   + 10);
378		    (void) sprintf (dname, "%s/%s", repository, finfo.file);
379		    if (isdir (dname))
380		    {
381			error (0, 0,
382			       "cannot add file `%s' since the directory",
383			       finfo.fullname);
384			error (0, 0, "`%s' already exists in the repository",
385			       dname);
386			error (1, 0, "illegal filename overlap");
387		    }
388		    free (dname);
389
390		    if (vers->options == NULL || *vers->options == '\0')
391		    {
392			/* No options specified on command line (or in
393			   rcs file if it existed, e.g. the file exists
394			   on another branch).  Check for a value from
395			   the wrapper stuff.  */
396			if (wrap_name_has (finfo.file, WRAP_RCSOPTION))
397			{
398			    if (vers->options)
399				free (vers->options);
400			    vers->options = wrap_rcsoption (finfo.file, 1);
401			}
402		    }
403
404		    if (vers->nonbranch)
405		    {
406			error (0, 0,
407				"cannot add file on non-branch tag %s",
408				vers->tag);
409			++err;
410		    }
411		    else
412		    {
413			/* There is a user file, so build the entry for it */
414			if (build_entry (repository, finfo.file, vers->options,
415					 message, entries, vers->tag) != 0)
416			    err++;
417			else
418			{
419			    added_files++;
420			    if (!quiet)
421			    {
422				if (vers->tag)
423				    error (0, 0, "\
424scheduling %s `%s' for addition on branch `%s'",
425					   (wrap_name_has (finfo.file,
426							   WRAP_TOCVS)
427					    ? "wrapper"
428					    : "file"),
429					   finfo.fullname, vers->tag);
430				else
431				    error (0, 0,
432					   "scheduling %s `%s' for addition",
433					   (wrap_name_has (finfo.file,
434							   WRAP_TOCVS)
435					    ? "wrapper"
436					    : "file"),
437					   finfo.fullname);
438			    }
439			}
440		    }
441		}
442	    }
443	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
444	    {
445		if (isdir (finfo.file)
446		    && !wrap_name_has (finfo.file, WRAP_TOCVS))
447		{
448		    error (0, 0, "\
449the directory `%s' cannot be added because a file of the", finfo.fullname);
450		    error (1, 0, "\
451same name already exists in the repository.");
452		}
453		else
454		{
455		    if (vers->nonbranch)
456		    {
457			error (0, 0,
458			       "cannot add file on non-branch tag %s",
459			       vers->tag);
460			++err;
461		    }
462		    else
463		    {
464			char *timestamp = NULL;
465			if (vers->ts_user == NULL)
466			{
467			    /* If this file does not exist locally, assume that
468			     * the last version on the branch is being
469			     * resurrected.
470			     *
471			     * Compute previous revision.  We assume that it
472			     * exists and that it is not a revision on the
473			     * trunk of the form X.1 (1.1, 2.1, 3.1, ...).  We
474			     * also assume that it is not dead, which seems
475			     * fair since we know vers->vn_rcs is dead
476			     * and we shouldn't see two dead revisions in a
477			     * row.
478			     */
479			    char *prev = previous_rev (vers->srcfile,
480			                               vers->vn_rcs);
481			    int status;
482			    assert (prev != NULL);
483			    if (!quiet)
484				error (0, 0,
485"Resurrecting file `%s' from revision %s.",
486			               finfo.fullname, prev);
487			    status = RCS_checkout (vers->srcfile, finfo.file,
488						   prev, vers->tag,
489						   vers->options, RUN_TTY,
490			                           NULL, NULL);
491			    xchmod (finfo.file, 1);
492			    if (status != 0)
493			    {
494				error (0, 0, "Failed to resurrect revision %s",
495				       prev);
496				err++;
497			    }
498			    else
499			    {
500				/* I don't actually set vers->ts_user here
501				 * because it would confuse server_update().
502				 */
503				timestamp = time_stamp (finfo.file);
504				if (!really_quiet)
505				    write_letter (&finfo, 'U');
506			    }
507			    free (prev);
508			}
509			if (!quiet)
510			{
511			    if (vers->tag)
512				error (0, 0,
513"file `%s' will be added on branch `%s' from version %s",
514				       finfo.fullname, vers->tag,
515				       vers->vn_rcs);
516			    else
517				/* I'm not sure that mentioning
518				   vers->vn_rcs makes any sense here; I
519				   can't think of a way to word the
520				   message which is not confusing.  */
521				error (0, 0,
522"Re-adding file `%s' (in place of dead revision %s).",
523				       finfo.fullname, vers->vn_rcs);
524			}
525			Register (entries, finfo.file, "0",
526				  timestamp ? timestamp : vers->ts_user,
527				  vers->options, vers->tag, vers->date, NULL);
528			if (timestamp) free (timestamp);
529#ifdef SERVER_SUPPORT
530			if (server_active && vers->ts_user == NULL)
531			{
532			    /* If we resurrected the file from the archive, we
533			     * need to tell the client about it.
534			     */
535			    server_updated (&finfo, vers,
536					    SERVER_UPDATED,
537					    (mode_t) -1, NULL, NULL);
538			    /* This is kinda hacky or, at least, it renders the
539			     * name "begin_added_files" obsolete, but we want
540			     * the added_files to be counted without triggering
541			     * the check that causes server_checked_in() to be
542			     * called below since we have already called
543			     * server_updated() to complete the resurrection.
544			     */
545			    ++begin_added_files;
546			}
547#endif
548			++added_files;
549		    }
550		}
551	    }
552	    else
553	    {
554		/*
555		 * There is an RCS file already, so somebody else must've
556		 * added it
557		 */
558		error (0, 0, "%s added independently by second party",
559		       finfo.fullname);
560		err++;
561	    }
562	}
563	else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
564	{
565
566	    /*
567	     * An entry for a new-born file, ts_rcs is dummy, but that is
568	     * inappropriate here
569	     */
570	    if (!quiet)
571		error (0, 0, "%s has already been entered", finfo.fullname);
572	    err++;
573	}
574	else if (vers->vn_user[0] == '-')
575	{
576	    /* An entry for a removed file, ts_rcs is invalid */
577	    if (vers->ts_user == NULL)
578	    {
579		/* There is no user file (as it should be) */
580		if (vers->vn_rcs == NULL)
581		{
582
583		    /*
584		     * There is no RCS file, so somebody else must've removed
585		     * it from under us
586		     */
587		    error (0, 0, "\
588cannot resurrect %s; RCS file removed by second party", finfo.fullname);
589		    err++;
590		}
591		else
592		{
593		    int status;
594		    /*
595		     * There is an RCS file, so remove the "-" from the
596		     * version number and restore the file
597		     */
598		    char *tmp = xmalloc (strlen (vers->vn_user));
599		    (void) strcpy (tmp, vers->vn_user + 1);
600		    (void) strcpy (vers->vn_user, tmp);
601		    free(tmp);
602		    status = RCS_checkout (vers->srcfile, finfo.file,
603					   vers->vn_user, vers->tag,
604					   vers->options, RUN_TTY,
605					   NULL, NULL);
606		    xchmod (finfo.file, 1);
607		    if (status != 0)
608		    {
609			error (0, 0, "Failed to resurrect revision %s",
610			       vers->vn_user);
611			err++;
612			tmp = NULL;
613		    }
614		    else
615		    {
616			/* I don't actually set vers->ts_user here because it
617			 * would confuse server_update().
618			 */
619			tmp = time_stamp (finfo.file);
620			write_letter (&finfo, 'U');
621			if (!quiet)
622			     error (0, 0, "%s, version %s, resurrected",
623			            finfo.fullname, vers->vn_user);
624		    }
625		    Register (entries, finfo.file, vers->vn_user,
626                              tmp, vers->options,
627			      vers->tag, vers->date, NULL);
628		    if (tmp) free (tmp);
629#ifdef SERVER_SUPPORT
630		    if (server_active)
631		    {
632			/* If we resurrected the file from the archive, we
633			 * need to tell the client about it.
634			 */
635			server_updated (&finfo, vers,
636					SERVER_UPDATED,
637					(mode_t) -1, NULL, NULL);
638		    }
639		   /* We don't increment added_files here because this isn't
640		    * a change that needs to be committed.
641		    */
642#endif
643		}
644	    }
645	    else
646	    {
647		/* The user file shouldn't be there */
648		error (0, 0, "\
649%s should be removed and is still there (or is back again)", finfo.fullname);
650		err++;
651	    }
652	}
653	else
654	{
655	    /* A normal entry, ts_rcs is valid, so it must already be there */
656	    if (!quiet)
657		error (0, 0, "%s already exists, with version number %s",
658			finfo.fullname,
659			vers->vn_user);
660	    err++;
661	}
662	freevers_ts (&vers);
663
664	/* passed all the checks.  Go ahead and add it if its a directory */
665	if (begin_err == err
666	    && isdir (finfo.file)
667	    && !wrap_name_has (finfo.file, WRAP_TOCVS))
668	{
669	    err += add_directory (&finfo);
670	}
671	else
672	{
673#ifdef SERVER_SUPPORT
674	    if (server_active && begin_added_files != added_files)
675		server_checked_in (finfo.file, finfo.update_dir, repository);
676#endif
677	}
678	free (repository);
679	Entries_Close (entries);
680
681	if (restore_cwd (&cwd, NULL))
682	    error_exit ();
683	free_cwd (&cwd);
684
685	/* It's okay to discard the const to free this - we allocated this
686	 * above.  The const is for everybody else.
687	 */
688	free ((char *) finfo.fullname);
689	free ((char *) filename);
690    }
691    if (added_files && !really_quiet)
692	error (0, 0, "use '%s commit' to add %s permanently",
693	       program_name,
694	       (added_files == 1) ? "this file" : "these files");
695
696    if (message)
697	free (message);
698    if (options)
699	free (options);
700
701    return (err);
702}
703
704/*
705 * The specified user file is really a directory.  So, let's make sure that
706 * it is created in the RCS source repository, and that the user's directory
707 * is updated to include a CVS directory.
708 *
709 * Returns 1 on failure, 0 on success.
710 */
711static int
712add_directory (finfo)
713    struct file_info *finfo;
714{
715    const char *repository = finfo->repository;
716    List *entries = finfo->entries;
717    const char *dir = finfo->file;
718
719    char *rcsdir = NULL;
720    struct saved_cwd cwd;
721    char *message = NULL;
722    char *tag, *date;
723    int nonbranch;
724    char *attrs;
725
726    if (strchr (dir, '/') != NULL)
727    {
728	/* "Can't happen".  */
729	error (0, 0,
730	       "directory %s not added; must be a direct sub-directory", dir);
731	return (1);
732    }
733    if (fncmp (dir, CVSADM) == 0)
734    {
735	error (0, 0, "cannot add a `%s' directory", CVSADM);
736	return (1);
737    }
738
739    /* before we do anything else, see if we have any per-directory tags */
740    ParseTag (&tag, &date, &nonbranch);
741
742    /* Remember the default attributes from this directory, so we can apply
743       them to the new directory.  */
744    fileattr_startdir (repository);
745    attrs = fileattr_getall (NULL);
746    fileattr_free ();
747
748    /* now, remember where we were, so we can get back */
749    if (save_cwd (&cwd))
750	return 1;
751    if (CVS_CHDIR (dir) < 0)
752    {
753	error (0, errno, "cannot chdir to %s", finfo->fullname);
754	return 1;
755    }
756#ifdef SERVER_SUPPORT
757    if (!server_active && isfile (CVSADM))
758#else
759    if (isfile (CVSADM))
760#endif
761    {
762	error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM);
763	goto out;
764    }
765
766    rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5);
767    sprintf (rcsdir, "%s/%s", repository, dir);
768    if (isfile (rcsdir) && !isdir (rcsdir))
769    {
770	error (0, 0, "%s is not a directory; %s not added", rcsdir,
771	       finfo->fullname);
772	goto out;
773    }
774
775    /* setup the log message */
776    message = xmalloc (strlen (rcsdir)
777		       + 80
778		       + (tag == NULL ? 0 : strlen (tag) + 80)
779		       + (date == NULL ? 0 : strlen (date) + 80));
780    (void) sprintf (message, "Directory %s added to the repository\n",
781		    rcsdir);
782    if (tag)
783    {
784	(void) strcat (message, "--> Using per-directory sticky tag `");
785	(void) strcat (message, tag);
786	(void) strcat (message, "'\n");
787    }
788    if (date)
789    {
790	(void) strcat (message, "--> Using per-directory sticky date `");
791	(void) strcat (message, date);
792	(void) strcat (message, "'\n");
793    }
794
795    if (!isdir (rcsdir))
796    {
797	mode_t omask;
798	Node *p;
799	List *ulist;
800	struct logfile_info *li;
801
802	/* There used to be some code here which would prompt for
803	   whether to add the directory.  The details of that code had
804	   bitrotted, but more to the point it can't work
805	   client/server, doesn't ask in the right way for GUIs, etc.
806	   A better way of making it harder to accidentally add
807	   directories would be to have to add and commit directories
808	   like for files.  The code was #if 0'd at least since CVS 1.5.  */
809
810	if (!noexec)
811	{
812	    omask = umask (cvsumask);
813	    if (CVS_MKDIR (rcsdir, 0777) < 0)
814	    {
815		error (0, errno, "cannot mkdir %s", rcsdir);
816		(void) umask (omask);
817		goto out;
818	    }
819	    (void) umask (omask);
820	}
821
822	/* Now set the default file attributes to the ones we inherited
823	   from the parent directory.  */
824	fileattr_startdir (rcsdir);
825	fileattr_setall (NULL, attrs);
826	fileattr_write ();
827	fileattr_free ();
828	if (attrs != NULL)
829	    free (attrs);
830
831	/*
832	 * Set up an update list with a single title node for Update_Logfile
833	 */
834	ulist = getlist ();
835	p = getnode ();
836	p->type = UPDATE;
837	p->delproc = update_delproc;
838	p->key = xstrdup ("- New directory");
839	li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
840	li->type = T_TITLE;
841	li->tag = xstrdup (tag);
842	li->rev_old = li->rev_new = NULL;
843	p->data = li;
844	(void) addnode (ulist, p);
845	Update_Logfile (rcsdir, message, (FILE *) NULL, ulist);
846	dellist (&ulist);
847    }
848
849#ifdef SERVER_SUPPORT
850    if (!server_active)
851#endif
852        Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1);
853    if (tag)
854	free (tag);
855    if (date)
856	free (date);
857
858    if (restore_cwd (&cwd, NULL))
859	error_exit ();
860    free_cwd (&cwd);
861
862    Subdir_Register (entries, (char *) NULL, dir);
863
864    if (!really_quiet)
865	cvs_output (message, 0);
866
867    free (rcsdir);
868    free (message);
869
870    return 0;
871
872out:
873    if (restore_cwd (&cwd, NULL))
874	error_exit ();
875    free_cwd (&cwd);
876    if (rcsdir != NULL)
877	free (rcsdir);
878    return (0);
879}
880
881
882
883/*
884 * Builds an entry for a new file and sets up "CVS/file",[pt] by
885 * interrogating the user.  Returns non-zero on error.
886 */
887static int
888build_entry (repository, user, options, message, entries, tag)
889    const char *repository;
890    const char *user;
891    const char *options;
892    const char *message;
893    List *entries;
894    const char *tag;
895{
896    char *fname;
897    char *line;
898    FILE *fp;
899
900    if (noexec)
901	return (0);
902
903    /*
904     * The requested log is read directly from the user and stored in the
905     * file user,t.  If the "message" argument is set, use it as the
906     * initial creation log (which typically describes the file).
907     */
908    fname = xmalloc (strlen (user) + 80);
909    (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
910    fp = open_file (fname, "w+");
911    if (message && fputs (message, fp) == EOF)
912	    error (1, errno, "cannot write to %s", fname);
913    if (fclose(fp) == EOF)
914        error(1, errno, "cannot close %s", fname);
915    free (fname);
916
917    /*
918     * Create the entry now, since this allows the user to interrupt us above
919     * without needing to clean anything up (well, we could clean up the
920     * ,t file, but who cares).
921     */
922    line = xmalloc (strlen (user) + 20);
923    (void) sprintf (line, "Initial %s", user);
924    Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
925    free (line);
926    return (0);
927}
928