xref: /openbsd/gnu/usr.bin/cvs/src/add.c (revision 43c1707e)
11e72d8d2Sderaadt /*
21e72d8d2Sderaadt  * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt  * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt  *
51e72d8d2Sderaadt  * You may distribute under the terms of the GNU General Public License as
62286d8edStholo  * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt  *
81e72d8d2Sderaadt  * Add
91e72d8d2Sderaadt  *
101e72d8d2Sderaadt  * Adds a file or directory to the RCS source repository.  For a file,
111e72d8d2Sderaadt  * the entry is marked as "needing to be added" in the user's own CVS
121e72d8d2Sderaadt  * directory, and really added to the repository when it is committed.
131e72d8d2Sderaadt  * For a directory, it is added at the appropriate place in the source
141e72d8d2Sderaadt  * repository and a CVS directory is generated within the directory.
151e72d8d2Sderaadt  *
161e72d8d2Sderaadt  * The -m option is currently the only supported option.  Some may wish to
171e72d8d2Sderaadt  * supply standard "rcs" options here, but I've found that this causes more
181e72d8d2Sderaadt  * trouble than anything else.
191e72d8d2Sderaadt  *
201e72d8d2Sderaadt  * The user files or directories must already exist.  For a directory, it must
211e72d8d2Sderaadt  * not already have a CVS file in it.
221e72d8d2Sderaadt  *
231e72d8d2Sderaadt  * An "add" on a file that has been "remove"d but not committed will cause the
241e72d8d2Sderaadt  * file to be resurrected.
251e72d8d2Sderaadt  */
261e72d8d2Sderaadt 
271e72d8d2Sderaadt #include "cvs.h"
28c26070a5Stholo #include "savecwd.h"
292286d8edStholo #include "fileattr.h"
301e72d8d2Sderaadt 
312286d8edStholo static int add_directory PROTO ((struct file_info *finfo));
321e72d8d2Sderaadt static int build_entry PROTO((char *repository, char *user, char *options,
331e72d8d2Sderaadt 		        char *message, List * entries, char *tag));
341e72d8d2Sderaadt 
351e72d8d2Sderaadt static const char *const add_usage[] =
361e72d8d2Sderaadt {
371e72d8d2Sderaadt     "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
381e72d8d2Sderaadt     "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
391e72d8d2Sderaadt     "\t-m\tUse \"message\" for the creation log.\n",
402286d8edStholo     "(Specify the --help global option for a list of other help options)\n",
411e72d8d2Sderaadt     NULL
421e72d8d2Sderaadt };
431e72d8d2Sderaadt 
441e72d8d2Sderaadt int
add(argc,argv)451e72d8d2Sderaadt add (argc, argv)
461e72d8d2Sderaadt     int argc;
471e72d8d2Sderaadt     char **argv;
481e72d8d2Sderaadt {
491e72d8d2Sderaadt     char *message = NULL;
501e72d8d2Sderaadt     int i;
511e72d8d2Sderaadt     char *repository;
521e72d8d2Sderaadt     int c;
531e72d8d2Sderaadt     int err = 0;
541e72d8d2Sderaadt     int added_files = 0;
551e72d8d2Sderaadt     char *options = NULL;
561e72d8d2Sderaadt     List *entries;
571e72d8d2Sderaadt     Vers_TS *vers;
582286d8edStholo     struct saved_cwd cwd;
59b2346922Stholo     /* Nonzero if we found a slash, and are thus adding files in a
60b2346922Stholo        subdirectory.  */
61b2346922Stholo     int found_slash = 0;
62e77048c1Stholo     size_t cvsroot_len;
631e72d8d2Sderaadt 
641e72d8d2Sderaadt     if (argc == 1 || argc == -1)
651e72d8d2Sderaadt 	usage (add_usage);
661e72d8d2Sderaadt 
671e72d8d2Sderaadt     wrap_setup ();
681e72d8d2Sderaadt 
691e72d8d2Sderaadt     /* parse args */
702770ece5Stholo     optind = 0;
71b6c02222Stholo     while ((c = getopt (argc, argv, "+k:m:")) != -1)
721e72d8d2Sderaadt     {
731e72d8d2Sderaadt 	switch (c)
741e72d8d2Sderaadt 	{
751e72d8d2Sderaadt 	    case 'k':
761e72d8d2Sderaadt 		if (options)
771e72d8d2Sderaadt 		    free (options);
781e72d8d2Sderaadt 		options = RCS_check_kflag (optarg);
791e72d8d2Sderaadt 		break;
801e72d8d2Sderaadt 
811e72d8d2Sderaadt 	    case 'm':
821e72d8d2Sderaadt 		message = xstrdup (optarg);
831e72d8d2Sderaadt 		break;
841e72d8d2Sderaadt 	    case '?':
851e72d8d2Sderaadt 	    default:
861e72d8d2Sderaadt 		usage (add_usage);
871e72d8d2Sderaadt 		break;
881e72d8d2Sderaadt 	}
891e72d8d2Sderaadt     }
901e72d8d2Sderaadt     argc -= optind;
911e72d8d2Sderaadt     argv += optind;
921e72d8d2Sderaadt 
931e72d8d2Sderaadt     if (argc <= 0)
941e72d8d2Sderaadt 	usage (add_usage);
951e72d8d2Sderaadt 
96*43c1707eStholo     cvsroot_len = strlen (current_parsed_root->directory);
97e77048c1Stholo 
982286d8edStholo     /* First some sanity checks.  I know that the CVS case is (sort of)
992286d8edStholo        also handled by add_directory, but we need to check here so the
1002286d8edStholo        client won't get all confused in send_file_names.  */
1012286d8edStholo     for (i = 0; i < argc; i++)
1022286d8edStholo     {
1032286d8edStholo 	int skip_file = 0;
1042286d8edStholo 
1052286d8edStholo 	/* If it were up to me I'd probably make this a fatal error.
1062286d8edStholo 	   But some people are really fond of their "cvs add *", and
1072286d8edStholo 	   don't seem to object to the warnings.
1082286d8edStholo 	   Whatever.  */
1092286d8edStholo 	strip_trailing_slashes (argv[i]);
1102286d8edStholo 	if (strcmp (argv[i], ".") == 0
1112286d8edStholo 	    || strcmp (argv[i], "..") == 0
1122286d8edStholo 	    || fncmp (argv[i], CVSADM) == 0)
1132286d8edStholo 	{
114*43c1707eStholo 	    if (!quiet)
1152286d8edStholo 		error (0, 0, "cannot add special file `%s'; skipping", argv[i]);
1162286d8edStholo 	    skip_file = 1;
1172286d8edStholo 	}
118b2346922Stholo 	else
119b2346922Stholo 	{
120b2346922Stholo 	    char *p;
121b2346922Stholo 	    p = argv[i];
122b2346922Stholo 	    while (*p != '\0')
123b2346922Stholo 	    {
124b2346922Stholo 		if (ISDIRSEP (*p))
125b2346922Stholo 		{
126b2346922Stholo 		    found_slash = 1;
127b2346922Stholo 		    break;
128b2346922Stholo 		}
129b2346922Stholo 		++p;
130b2346922Stholo 	    }
131b2346922Stholo 	}
1322286d8edStholo 
1332286d8edStholo 	if (skip_file)
1342286d8edStholo 	{
1352286d8edStholo 	    int j;
1362286d8edStholo 
1372286d8edStholo 	    /* FIXME: We don't do anything about free'ing argv[i].  But
1382286d8edStholo 	       the problem is that it is only sometimes allocated (see
1392286d8edStholo 	       cvsrc.c).  */
1402286d8edStholo 
1412286d8edStholo 	    for (j = i; j < argc - 1; ++j)
1422286d8edStholo 		argv[j] = argv[j + 1];
1432286d8edStholo 	    --argc;
1442286d8edStholo 	    /* Check the new argv[i] again.  */
1452286d8edStholo 	    --i;
1462286d8edStholo 	    ++err;
1472286d8edStholo 	}
1482286d8edStholo     }
1491e72d8d2Sderaadt 
1501e72d8d2Sderaadt #ifdef CLIENT_SUPPORT
151*43c1707eStholo     if (current_parsed_root->isremote)
1521e72d8d2Sderaadt     {
1531e72d8d2Sderaadt 	int i;
1542286d8edStholo 
1552286d8edStholo 	if (argc == 0)
1562286d8edStholo 	    /* We snipped out all the arguments in the above sanity
1572286d8edStholo 	       check.  We can just forget the whole thing (and we
1582286d8edStholo 	       better, because if we fired up the server and passed it
1592286d8edStholo 	       nothing, it would spit back a usage message).  */
1602286d8edStholo 	    return err;
1612286d8edStholo 
1621e72d8d2Sderaadt 	start_server ();
1631e72d8d2Sderaadt 	ign_setup ();
164e77048c1Stholo 	if (options)
165e77048c1Stholo 	{
166e77048c1Stholo 	    send_arg (options);
167e77048c1Stholo 	    free (options);
168e77048c1Stholo 	}
1691e72d8d2Sderaadt 	option_with_arg ("-m", message);
1702286d8edStholo 
171b2346922Stholo 	/* If !found_slash, refrain from sending "Directory", for
172b2346922Stholo 	   CVS 1.9 compatibility.  If we only tried to deal with servers
173b2346922Stholo 	   which are at least CVS 1.9.26 or so, we wouldn't have to
174b2346922Stholo 	   special-case this.  */
175b2346922Stholo 	if (found_slash)
176b2346922Stholo 	{
1772286d8edStholo 	    repository = Name_Repository (NULL, NULL);
1782286d8edStholo 	    send_a_repository ("", repository, "");
1792286d8edStholo 	    free (repository);
180b2346922Stholo 	}
1812286d8edStholo 
1821e72d8d2Sderaadt 	for (i = 0; i < argc; ++i)
183892c0aadStholo 	{
1841e72d8d2Sderaadt 	    /* FIXME: Does this erroneously call Create_Admin in error
1851e72d8d2Sderaadt 	       conditions which are only detected once the server gets its
1861e72d8d2Sderaadt 	       hands on things?  */
187892c0aadStholo 	    /* FIXME-also: if filenames are case-insensitive on the
188892c0aadStholo 	       client, and the directory in the repository already
189892c0aadStholo 	       exists and is named "foo", and the command is "cvs add
190892c0aadStholo 	       FOO", this call to Create_Admin puts the wrong thing in
191892c0aadStholo 	       CVS/Repository and so a subsequent "cvs update" will
192892c0aadStholo 	       give an error.  The fix will be to have the server report
193892c0aadStholo 	       back what it actually did (e.g. use tagged text for the
194892c0aadStholo 	       "Directory %s added" message), and then Create_Admin,
195892c0aadStholo 	       which should also fix the error handling concerns.  */
196892c0aadStholo 
1971e72d8d2Sderaadt 	    if (isdir (argv[i]))
1981e72d8d2Sderaadt 	    {
1991e72d8d2Sderaadt 		char *tag;
2001e72d8d2Sderaadt 		char *date;
201b6c02222Stholo 		int nonbranch;
202780d15dfStholo 		char *rcsdir;
2032286d8edStholo 		char *p;
2042286d8edStholo 		char *update_dir;
2052286d8edStholo 		/* This is some mungeable storage into which we can point
2062286d8edStholo 		   with p and/or update_dir.  */
2072286d8edStholo 		char *filedir;
2082286d8edStholo 
2092286d8edStholo 		if (save_cwd (&cwd))
2102286d8edStholo 		    error_exit ();
2112286d8edStholo 
2122286d8edStholo 		filedir = xstrdup (argv[i]);
2132286d8edStholo 		p = last_component (filedir);
2142286d8edStholo 		if (p == filedir)
2152286d8edStholo 		{
2162286d8edStholo 		    update_dir = "";
2172286d8edStholo 		}
2182286d8edStholo 		else
2192286d8edStholo 		{
2202286d8edStholo 		    p[-1] = '\0';
2212286d8edStholo 		    update_dir = filedir;
2222286d8edStholo 		    if (CVS_CHDIR (update_dir) < 0)
2232286d8edStholo 			error (1, errno,
2242286d8edStholo 			       "could not chdir to %s", update_dir);
2252286d8edStholo 		}
2262286d8edStholo 
2272286d8edStholo 		/* find the repository associated with our current dir */
2282286d8edStholo 		repository = Name_Repository (NULL, update_dir);
2291e72d8d2Sderaadt 
230e77048c1Stholo 		/* don't add stuff to Emptydir */
231*43c1707eStholo 		if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0
232e77048c1Stholo 		    && ISDIRSEP (repository[cvsroot_len])
233e77048c1Stholo 		    && strncmp (repository + cvsroot_len + 1,
234e77048c1Stholo 				CVSROOTADM,
235e77048c1Stholo 				sizeof CVSROOTADM - 1) == 0
236e77048c1Stholo 		    && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM])
237e77048c1Stholo 		    && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1,
238e77048c1Stholo 			       CVSNULLREPOS) == 0)
239e77048c1Stholo 		    error (1, 0, "cannot add to %s", repository);
240e77048c1Stholo 
2411e72d8d2Sderaadt 		/* before we do anything else, see if we have any
2421e72d8d2Sderaadt 		   per-directory tags */
243b6c02222Stholo 		ParseTag (&tag, &date, &nonbranch);
2441e72d8d2Sderaadt 
2452286d8edStholo 		rcsdir = xmalloc (strlen (repository) + strlen (p) + 5);
2462286d8edStholo 		sprintf (rcsdir, "%s/%s", repository, p);
2471e72d8d2Sderaadt 
2482286d8edStholo 		Create_Admin (p, argv[i], rcsdir, tag, date,
249e77048c1Stholo 			      nonbranch, 0, 1);
25050bf276cStholo 
251b2346922Stholo 		if (found_slash)
2522286d8edStholo 		    send_a_repository ("", repository, update_dir);
2532286d8edStholo 
2542286d8edStholo 		if (restore_cwd (&cwd, NULL))
2552286d8edStholo 		    error_exit ();
2562286d8edStholo 		free_cwd (&cwd);
2571e72d8d2Sderaadt 
2581e72d8d2Sderaadt 		if (tag)
2591e72d8d2Sderaadt 		    free (tag);
2601e72d8d2Sderaadt 		if (date)
2611e72d8d2Sderaadt 		    free (date);
2621e72d8d2Sderaadt 		free (rcsdir);
26350bf276cStholo 
2642286d8edStholo 		if (p == filedir)
26550bf276cStholo 		    Subdir_Register ((List *) NULL, (char *) NULL, argv[i]);
26650bf276cStholo 		else
26750bf276cStholo 		{
2682286d8edStholo 		    Subdir_Register ((List *) NULL, update_dir, p);
26950bf276cStholo 		}
2702286d8edStholo 		free (repository);
2712286d8edStholo 		free (filedir);
2721e72d8d2Sderaadt 	    }
273892c0aadStholo 	}
2742286d8edStholo 	send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS);
275c71bc7e2Stholo 	send_file_names (argc, argv, SEND_EXPAND_WILD);
27613571821Stholo 	send_to_server ("add\012", 0);
2772770ece5Stholo 	if (message)
2782770ece5Stholo 	    free (message);
2792286d8edStholo 	return err + get_responses_and_close ();
2801e72d8d2Sderaadt     }
2811e72d8d2Sderaadt #endif
2821e72d8d2Sderaadt 
2831e72d8d2Sderaadt     /* walk the arg list adding files/dirs */
2841e72d8d2Sderaadt     for (i = 0; i < argc; i++)
2851e72d8d2Sderaadt     {
2861e72d8d2Sderaadt 	int begin_err = err;
287461cc63eStholo #ifdef SERVER_SUPPORT
2881e72d8d2Sderaadt 	int begin_added_files = added_files;
289461cc63eStholo #endif
29050bf276cStholo 	struct file_info finfo;
2912286d8edStholo 	char *p;
292892c0aadStholo #if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
293892c0aadStholo 	char *found_name;
294892c0aadStholo #endif
2951e72d8d2Sderaadt 
29650bf276cStholo 	memset (&finfo, 0, sizeof finfo);
2972286d8edStholo 
2982286d8edStholo 	if (save_cwd (&cwd))
2992286d8edStholo 	    error_exit ();
3002286d8edStholo 
3012286d8edStholo 	finfo.fullname = xstrdup (argv[i]);
3022286d8edStholo 	p = last_component (argv[i]);
3032286d8edStholo 	if (p == argv[i])
3042286d8edStholo 	{
30550bf276cStholo 	    finfo.update_dir = "";
3062286d8edStholo 	    finfo.file = p;
3072286d8edStholo 	}
3082286d8edStholo 	else
3092286d8edStholo 	{
3102286d8edStholo 	    p[-1] = '\0';
3112286d8edStholo 	    finfo.update_dir = argv[i];
3122286d8edStholo 	    finfo.file = p;
3132286d8edStholo 	    if (CVS_CHDIR (finfo.update_dir) < 0)
3142286d8edStholo 		error (1, errno, "could not chdir to %s", finfo.update_dir);
3152286d8edStholo 	}
3162286d8edStholo 
3172286d8edStholo 	/* Add wrappers for this directory.  They exist only until
3182286d8edStholo 	   the next call to wrap_add_file.  */
3192286d8edStholo 	wrap_add_file (CVSDOTWRAPPER, 1);
3202286d8edStholo 
3212286d8edStholo 	finfo.rcs = NULL;
3222286d8edStholo 
3232286d8edStholo 	/* Find the repository associated with our current dir.  */
3242286d8edStholo 	repository = Name_Repository (NULL, finfo.update_dir);
3252286d8edStholo 
326e77048c1Stholo 	/* don't add stuff to Emptydir */
327*43c1707eStholo 	if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0
328e77048c1Stholo 	    && ISDIRSEP (repository[cvsroot_len])
329e77048c1Stholo 	    && strncmp (repository + cvsroot_len + 1,
330e77048c1Stholo 			CVSROOTADM,
331e77048c1Stholo 			sizeof CVSROOTADM - 1) == 0
332e77048c1Stholo 	    && ISDIRSEP (repository[cvsroot_len + sizeof CVSROOTADM])
333e77048c1Stholo 	    && strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1,
334e77048c1Stholo 		       CVSNULLREPOS) == 0)
335e77048c1Stholo 	    error (1, 0, "cannot add to %s", repository);
336e77048c1Stholo 
3375e617892Stholo 	entries = Entries_Open (0, NULL);
3382286d8edStholo 
33950bf276cStholo 	finfo.repository = repository;
34050bf276cStholo 	finfo.entries = entries;
34150bf276cStholo 
342892c0aadStholo #if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
343892c0aadStholo 	if (ign_case)
344892c0aadStholo 	{
345892c0aadStholo 	    /* Need to check whether there is a directory with the
346892c0aadStholo 	       same name but different case.  We'll check for files
347892c0aadStholo 	       with the same name later (when Version_TS calls
348892c0aadStholo 	       RCS_parse which calls fopen_case).  If CVS some day
349892c0aadStholo 	       records directories in the RCS files, then we should be
350892c0aadStholo 	       able to skip the separate check here, which would be
351892c0aadStholo 	       cleaner.  */
352892c0aadStholo 	    DIR *dirp;
353892c0aadStholo 	    struct dirent *dp;
354892c0aadStholo 
355892c0aadStholo 	    dirp = CVS_OPENDIR (finfo.repository);
356892c0aadStholo 	    if (dirp == NULL)
357892c0aadStholo 		error (1, errno, "cannot read directory %s", finfo.repository);
358892c0aadStholo 	    found_name = NULL;
359892c0aadStholo 	    errno = 0;
360*43c1707eStholo 	    while ((dp = CVS_READDIR (dirp)) != NULL)
361892c0aadStholo 	    {
362892c0aadStholo 		if (cvs_casecmp (dp->d_name, finfo.file) == 0)
363892c0aadStholo 		{
364892c0aadStholo 		    if (found_name != NULL)
365892c0aadStholo 			error (1, 0, "%s is ambiguous; could mean %s or %s",
366892c0aadStholo 			       finfo.file, dp->d_name, found_name);
367892c0aadStholo 		    found_name = xstrdup (dp->d_name);
368892c0aadStholo 		}
369892c0aadStholo 	    }
370892c0aadStholo 	    if (errno != 0)
371892c0aadStholo 		error (1, errno, "cannot read directory %s", finfo.repository);
372*43c1707eStholo 	    CVS_CLOSEDIR (dirp);
373892c0aadStholo 
374892c0aadStholo 	    if (found_name != NULL)
375892c0aadStholo 	    {
376892c0aadStholo 		/* OK, we are about to patch up the name, so patch up
377892c0aadStholo 		   the temporary directory too to match.  The isdir
378892c0aadStholo 		   should "always" be true (since files have ,v), but
379892c0aadStholo 		   I guess we might as well make some attempt to not
380892c0aadStholo 		   get confused by stray files in the repository.  */
381892c0aadStholo 		if (isdir (finfo.file))
382892c0aadStholo 		{
383892c0aadStholo 		    if (CVS_MKDIR (found_name, 0777) < 0
384892c0aadStholo 			&& errno != EEXIST)
385892c0aadStholo 			error (0, errno, "cannot create %s", finfo.file);
386892c0aadStholo 		}
387892c0aadStholo 
388892c0aadStholo 		/* OK, we found a directory with the same name, maybe in
389892c0aadStholo 		   a different case.  Treat it as if the name were the
390892c0aadStholo 		   same.  */
391892c0aadStholo 		finfo.file = found_name;
392892c0aadStholo 	    }
393892c0aadStholo 	}
394892c0aadStholo #endif
395892c0aadStholo 
39650bf276cStholo 	/* We pass force_tag_match as 1.  If the directory has a
39750bf276cStholo            sticky branch tag, and there is already an RCS file which
39850bf276cStholo            does not have that tag, then the head revision is
39950bf276cStholo            meaningless to us.  */
40050bf276cStholo 	vers = Version_TS (&finfo, options, NULL, NULL, 1, 0);
4011e72d8d2Sderaadt 	if (vers->vn_user == NULL)
4021e72d8d2Sderaadt 	{
4031e72d8d2Sderaadt 	    /* No entry available, ts_rcs is invalid */
4041e72d8d2Sderaadt 	    if (vers->vn_rcs == NULL)
4051e72d8d2Sderaadt 	    {
4061e72d8d2Sderaadt 		/* There is no RCS file either */
4071e72d8d2Sderaadt 		if (vers->ts_user == NULL)
4081e72d8d2Sderaadt 		{
4091e72d8d2Sderaadt 		    /* There is no user file either */
4102286d8edStholo 		    error (0, 0, "nothing known about %s", finfo.fullname);
4111e72d8d2Sderaadt 		    err++;
4121e72d8d2Sderaadt 		}
4132286d8edStholo 		else if (!isdir (finfo.file)
4142286d8edStholo 			 || wrap_name_has (finfo.file, WRAP_TOCVS))
4151e72d8d2Sderaadt 		{
4161e72d8d2Sderaadt 		    /*
4171e72d8d2Sderaadt 		     * See if a directory exists in the repository with
4181e72d8d2Sderaadt 		     * the same name.  If so, blow this request off.
4191e72d8d2Sderaadt 		     */
4202286d8edStholo 		    char *dname = xmalloc (strlen (repository)
4212286d8edStholo 					   + strlen (finfo.file)
422461cc63eStholo 					   + 10);
4232286d8edStholo 		    (void) sprintf (dname, "%s/%s", repository, finfo.file);
4241e72d8d2Sderaadt 		    if (isdir (dname))
4251e72d8d2Sderaadt 		    {
4261e72d8d2Sderaadt 			error (0, 0,
4271e72d8d2Sderaadt 			       "cannot add file `%s' since the directory",
4282286d8edStholo 			       finfo.fullname);
4291e72d8d2Sderaadt 			error (0, 0, "`%s' already exists in the repository",
4301e72d8d2Sderaadt 			       dname);
4311e72d8d2Sderaadt 			error (1, 0, "illegal filename overlap");
4321e72d8d2Sderaadt 		    }
433461cc63eStholo 		    free (dname);
4341e72d8d2Sderaadt 
43550bf276cStholo 		    if (vers->options == NULL || *vers->options == '\0')
43650bf276cStholo 		    {
43750bf276cStholo 			/* No options specified on command line (or in
43850bf276cStholo 			   rcs file if it existed, e.g. the file exists
43950bf276cStholo 			   on another branch).  Check for a value from
44050bf276cStholo 			   the wrapper stuff.  */
4412286d8edStholo 			if (wrap_name_has (finfo.file, WRAP_RCSOPTION))
44250bf276cStholo 			{
44350bf276cStholo 			    if (vers->options)
44450bf276cStholo 				free (vers->options);
4452286d8edStholo 			    vers->options = wrap_rcsoption (finfo.file, 1);
44650bf276cStholo 			}
44750bf276cStholo 		    }
44850bf276cStholo 
449b6c02222Stholo 		    if (vers->nonbranch)
450b6c02222Stholo 		    {
451b6c02222Stholo 			error (0, 0,
452b6c02222Stholo 				"cannot add file on non-branch tag %s",
453b6c02222Stholo 				vers->tag);
454b6c02222Stholo 			++err;
455b6c02222Stholo 		    }
456b6c02222Stholo 		    else
457b6c02222Stholo 		    {
4581e72d8d2Sderaadt 			/* There is a user file, so build the entry for it */
4592286d8edStholo 			if (build_entry (repository, finfo.file, vers->options,
4601e72d8d2Sderaadt 					 message, entries, vers->tag) != 0)
4611e72d8d2Sderaadt 			    err++;
4621e72d8d2Sderaadt 			else
4631e72d8d2Sderaadt 			{
4641e72d8d2Sderaadt 			    added_files++;
4651e72d8d2Sderaadt 			    if (!quiet)
4661e72d8d2Sderaadt 			    {
4671e72d8d2Sderaadt 				if (vers->tag)
4681e72d8d2Sderaadt 				    error (0, 0, "\
4691e72d8d2Sderaadt scheduling %s `%s' for addition on branch `%s'",
4702286d8edStholo 					   (wrap_name_has (finfo.file,
4712286d8edStholo 							   WRAP_TOCVS)
4721e72d8d2Sderaadt 					    ? "wrapper"
4731e72d8d2Sderaadt 					    : "file"),
4742286d8edStholo 					   finfo.fullname, vers->tag);
4751e72d8d2Sderaadt 				else
476b6c02222Stholo 				    error (0, 0,
477b6c02222Stholo 					   "scheduling %s `%s' for addition",
4782286d8edStholo 					   (wrap_name_has (finfo.file,
4792286d8edStholo 							   WRAP_TOCVS)
4801e72d8d2Sderaadt 					    ? "wrapper"
4811e72d8d2Sderaadt 					    : "file"),
4822286d8edStholo 					   finfo.fullname);
4831e72d8d2Sderaadt 			    }
4841e72d8d2Sderaadt 			}
4851e72d8d2Sderaadt 		    }
4861e72d8d2Sderaadt 		}
487b6c02222Stholo 	    }
4881e72d8d2Sderaadt 	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
4891e72d8d2Sderaadt 	    {
4902286d8edStholo 		if (isdir (finfo.file)
4912286d8edStholo 		    && !wrap_name_has (finfo.file, WRAP_TOCVS))
4921e72d8d2Sderaadt 		{
493461cc63eStholo 		    error (0, 0, "\
4942286d8edStholo the directory `%s' cannot be added because a file of the", finfo.fullname);
495461cc63eStholo 		    error (1, 0, "\
496461cc63eStholo same name already exists in the repository.");
4971e72d8d2Sderaadt 		}
4981e72d8d2Sderaadt 		else
4991e72d8d2Sderaadt 		{
500b6c02222Stholo 		    if (vers->nonbranch)
501b6c02222Stholo 		    {
502b6c02222Stholo 			error (0, 0,
503b6c02222Stholo 			       "cannot add file on non-branch tag %s",
504b6c02222Stholo 			       vers->tag);
505b6c02222Stholo 			++err;
506b6c02222Stholo 		    }
507b6c02222Stholo 		    else
508b6c02222Stholo 		    {
509*43c1707eStholo 			if (!quiet)
510*43c1707eStholo 			{
5111e72d8d2Sderaadt 			    if (vers->tag)
512461cc63eStholo 				error (0, 0, "\
513461cc63eStholo file `%s' will be added on branch `%s' from version %s",
5142286d8edStholo 					finfo.fullname, vers->tag, vers->vn_rcs);
5151e72d8d2Sderaadt 			    else
516b6c02222Stholo 				/* I'm not sure that mentioning
517b6c02222Stholo 				   vers->vn_rcs makes any sense here; I
518b6c02222Stholo 				   can't think of a way to word the
51950bf276cStholo 				   message which is not confusing.  */
52050bf276cStholo 				error (0, 0, "\
52150bf276cStholo re-adding file %s (in place of dead revision %s)",
5222286d8edStholo 					finfo.fullname, vers->vn_rcs);
523*43c1707eStholo 			}
5242286d8edStholo 			Register (entries, finfo.file, "0", vers->ts_user,
525c71bc7e2Stholo 				  vers->options,
5261e72d8d2Sderaadt 				  vers->tag, NULL, NULL);
5271e72d8d2Sderaadt 			++added_files;
5281e72d8d2Sderaadt 		    }
5291e72d8d2Sderaadt 		}
530b6c02222Stholo 	    }
5311e72d8d2Sderaadt 	    else
5321e72d8d2Sderaadt 	    {
5331e72d8d2Sderaadt 		/*
5341e72d8d2Sderaadt 		 * There is an RCS file already, so somebody else must've
5351e72d8d2Sderaadt 		 * added it
5361e72d8d2Sderaadt 		 */
5372286d8edStholo 		error (0, 0, "%s added independently by second party",
5382286d8edStholo 		       finfo.fullname);
5391e72d8d2Sderaadt 		err++;
5401e72d8d2Sderaadt 	    }
5411e72d8d2Sderaadt 	}
5421e72d8d2Sderaadt 	else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
5431e72d8d2Sderaadt 	{
5441e72d8d2Sderaadt 
5451e72d8d2Sderaadt 	    /*
5461e72d8d2Sderaadt 	     * An entry for a new-born file, ts_rcs is dummy, but that is
5471e72d8d2Sderaadt 	     * inappropriate here
5481e72d8d2Sderaadt 	     */
549*43c1707eStholo 	    if (!quiet)
5502286d8edStholo 		error (0, 0, "%s has already been entered", finfo.fullname);
5511e72d8d2Sderaadt 	    err++;
5521e72d8d2Sderaadt 	}
5531e72d8d2Sderaadt 	else if (vers->vn_user[0] == '-')
5541e72d8d2Sderaadt 	{
5551e72d8d2Sderaadt 	    /* An entry for a removed file, ts_rcs is invalid */
5561e72d8d2Sderaadt 	    if (vers->ts_user == NULL)
5571e72d8d2Sderaadt 	    {
5581e72d8d2Sderaadt 		/* There is no user file (as it should be) */
5591e72d8d2Sderaadt 		if (vers->vn_rcs == NULL)
5601e72d8d2Sderaadt 		{
5611e72d8d2Sderaadt 
5621e72d8d2Sderaadt 		    /*
5631e72d8d2Sderaadt 		     * There is no RCS file, so somebody else must've removed
5641e72d8d2Sderaadt 		     * it from under us
5651e72d8d2Sderaadt 		     */
566461cc63eStholo 		    error (0, 0, "\
5672286d8edStholo cannot resurrect %s; RCS file removed by second party", finfo.fullname);
5681e72d8d2Sderaadt 		    err++;
5691e72d8d2Sderaadt 		}
5701e72d8d2Sderaadt 		else
5711e72d8d2Sderaadt 		{
5721e72d8d2Sderaadt 
5731e72d8d2Sderaadt 		    /*
5741e72d8d2Sderaadt 		     * There is an RCS file, so remove the "-" from the
5751e72d8d2Sderaadt 		     * version number and restore the file
5761e72d8d2Sderaadt 		     */
5772286d8edStholo 		    char *tmp = xmalloc (strlen (finfo.file) + 50);
5781e72d8d2Sderaadt 
5791e72d8d2Sderaadt 		    (void) strcpy (tmp, vers->vn_user + 1);
5801e72d8d2Sderaadt 		    (void) strcpy (vers->vn_user, tmp);
5812286d8edStholo 		    (void) sprintf (tmp, "Resurrected %s", finfo.file);
5822286d8edStholo 		    Register (entries, finfo.file, vers->vn_user, tmp,
5832286d8edStholo 			      vers->options,
5841e72d8d2Sderaadt 			      vers->tag, vers->date, vers->ts_conflict);
5851e72d8d2Sderaadt 		    free (tmp);
5861e72d8d2Sderaadt 
5871e72d8d2Sderaadt 		    /* XXX - bugs here; this really resurrect the head */
5881e72d8d2Sderaadt 		    /* Note that this depends on the Register above actually
5891e72d8d2Sderaadt 		       having written Entries, or else it won't really
5901e72d8d2Sderaadt 		       check the file out.  */
5911e72d8d2Sderaadt 		    if (update (2, argv + i - 1) == 0)
5921e72d8d2Sderaadt 		    {
5932286d8edStholo 			error (0, 0, "%s, version %s, resurrected",
5942286d8edStholo 			       finfo.fullname,
5951e72d8d2Sderaadt 			       vers->vn_user);
5961e72d8d2Sderaadt 		    }
5971e72d8d2Sderaadt 		    else
5981e72d8d2Sderaadt 		    {
5992286d8edStholo 			error (0, 0, "could not resurrect %s", finfo.fullname);
6001e72d8d2Sderaadt 			err++;
6011e72d8d2Sderaadt 		    }
6021e72d8d2Sderaadt 		}
6031e72d8d2Sderaadt 	    }
6041e72d8d2Sderaadt 	    else
6051e72d8d2Sderaadt 	    {
6061e72d8d2Sderaadt 		/* The user file shouldn't be there */
607461cc63eStholo 		error (0, 0, "\
6082286d8edStholo %s should be removed and is still there (or is back again)", finfo.fullname);
6091e72d8d2Sderaadt 		err++;
6101e72d8d2Sderaadt 	    }
6111e72d8d2Sderaadt 	}
6121e72d8d2Sderaadt 	else
6131e72d8d2Sderaadt 	{
6141e72d8d2Sderaadt 	    /* A normal entry, ts_rcs is valid, so it must already be there */
615*43c1707eStholo 	    if (!quiet)
6162286d8edStholo 		error (0, 0, "%s already exists, with version number %s",
6172286d8edStholo 			finfo.fullname,
6181e72d8d2Sderaadt 			vers->vn_user);
6191e72d8d2Sderaadt 	    err++;
6201e72d8d2Sderaadt 	}
6211e72d8d2Sderaadt 	freevers_ts (&vers);
6221e72d8d2Sderaadt 
6231e72d8d2Sderaadt 	/* passed all the checks.  Go ahead and add it if its a directory */
6241e72d8d2Sderaadt 	if (begin_err == err
6252286d8edStholo 	    && isdir (finfo.file)
6262286d8edStholo 	    && !wrap_name_has (finfo.file, WRAP_TOCVS))
6271e72d8d2Sderaadt 	{
6282286d8edStholo 	    err += add_directory (&finfo);
6291e72d8d2Sderaadt 	}
6302286d8edStholo 	else
6312286d8edStholo 	{
6321e72d8d2Sderaadt #ifdef SERVER_SUPPORT
6331e72d8d2Sderaadt 	    if (server_active && begin_added_files != added_files)
6342286d8edStholo 		server_checked_in (finfo.file, finfo.update_dir, repository);
6351e72d8d2Sderaadt #endif
6361e72d8d2Sderaadt 	}
6372286d8edStholo 	free (repository);
6381e72d8d2Sderaadt 	Entries_Close (entries);
6391e72d8d2Sderaadt 
6402286d8edStholo 	if (restore_cwd (&cwd, NULL))
6412286d8edStholo 	    error_exit ();
6422286d8edStholo 	free_cwd (&cwd);
6432286d8edStholo 
6442286d8edStholo 	free (finfo.fullname);
645892c0aadStholo #if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
646892c0aadStholo 	if (ign_case && found_name != NULL)
647892c0aadStholo 	    free (found_name);
648892c0aadStholo #endif
6492286d8edStholo     }
650*43c1707eStholo     if (added_files && !really_quiet)
6512286d8edStholo 	error (0, 0, "use '%s commit' to add %s permanently",
6522286d8edStholo 	       program_name,
6532286d8edStholo 	       (added_files == 1) ? "this file" : "these files");
6542286d8edStholo 
6551e72d8d2Sderaadt     if (message)
6561e72d8d2Sderaadt 	free (message);
657e77048c1Stholo     if (options)
658e77048c1Stholo 	free (options);
6591e72d8d2Sderaadt 
6601e72d8d2Sderaadt     return (err);
6611e72d8d2Sderaadt }
6621e72d8d2Sderaadt 
6631e72d8d2Sderaadt /*
6641e72d8d2Sderaadt  * The specified user file is really a directory.  So, let's make sure that
6651e72d8d2Sderaadt  * it is created in the RCS source repository, and that the user's directory
6661e72d8d2Sderaadt  * is updated to include a CVS directory.
6671e72d8d2Sderaadt  *
6681e72d8d2Sderaadt  * Returns 1 on failure, 0 on success.
6691e72d8d2Sderaadt  */
6701e72d8d2Sderaadt static int
add_directory(finfo)6712286d8edStholo add_directory (finfo)
6722286d8edStholo     struct file_info *finfo;
6731e72d8d2Sderaadt {
6742286d8edStholo     char *repository = finfo->repository;
6752286d8edStholo     List *entries = finfo->entries;
6762286d8edStholo     char *dir = finfo->file;
6772286d8edStholo 
678461cc63eStholo     char *rcsdir = NULL;
6791e72d8d2Sderaadt     struct saved_cwd cwd;
680461cc63eStholo     char *message = NULL;
6811e72d8d2Sderaadt     char *tag, *date;
682b6c02222Stholo     int nonbranch;
6832286d8edStholo     char *attrs;
6841e72d8d2Sderaadt 
6851e72d8d2Sderaadt     if (strchr (dir, '/') != NULL)
6861e72d8d2Sderaadt     {
6872286d8edStholo 	/* "Can't happen".  */
6881e72d8d2Sderaadt 	error (0, 0,
6891e72d8d2Sderaadt 	       "directory %s not added; must be a direct sub-directory", dir);
6901e72d8d2Sderaadt 	return (1);
6911e72d8d2Sderaadt     }
6922286d8edStholo     if (fncmp (dir, CVSADM) == 0)
6931e72d8d2Sderaadt     {
6941e72d8d2Sderaadt 	error (0, 0, "cannot add a `%s' directory", CVSADM);
6951e72d8d2Sderaadt 	return (1);
6961e72d8d2Sderaadt     }
6971e72d8d2Sderaadt 
6981e72d8d2Sderaadt     /* before we do anything else, see if we have any per-directory tags */
699b6c02222Stholo     ParseTag (&tag, &date, &nonbranch);
7001e72d8d2Sderaadt 
7012286d8edStholo     /* Remember the default attributes from this directory, so we can apply
7022286d8edStholo        them to the new directory.  */
7032286d8edStholo     fileattr_startdir (repository);
7042286d8edStholo     attrs = fileattr_getall (NULL);
7052286d8edStholo     fileattr_free ();
7062286d8edStholo 
7071e72d8d2Sderaadt     /* now, remember where we were, so we can get back */
7081e72d8d2Sderaadt     if (save_cwd (&cwd))
7091e72d8d2Sderaadt 	return (1);
71050bf276cStholo     if ( CVS_CHDIR (dir) < 0)
7111e72d8d2Sderaadt     {
7122286d8edStholo 	error (0, errno, "cannot chdir to %s", finfo->fullname);
7131e72d8d2Sderaadt 	return (1);
7141e72d8d2Sderaadt     }
7151e72d8d2Sderaadt #ifdef SERVER_SUPPORT
7161e72d8d2Sderaadt     if (!server_active && isfile (CVSADM))
7171e72d8d2Sderaadt #else
7181e72d8d2Sderaadt     if (isfile (CVSADM))
7191e72d8d2Sderaadt #endif
7201e72d8d2Sderaadt     {
7212286d8edStholo 	error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM);
7221e72d8d2Sderaadt 	goto out;
7231e72d8d2Sderaadt     }
7241e72d8d2Sderaadt 
7252286d8edStholo     rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5);
7262286d8edStholo     sprintf (rcsdir, "%s/%s", repository, dir);
7271e72d8d2Sderaadt     if (isfile (rcsdir) && !isdir (rcsdir))
7281e72d8d2Sderaadt     {
7292286d8edStholo 	error (0, 0, "%s is not a directory; %s not added", rcsdir,
7302286d8edStholo 	       finfo->fullname);
7311e72d8d2Sderaadt 	goto out;
7321e72d8d2Sderaadt     }
7331e72d8d2Sderaadt 
7341e72d8d2Sderaadt     /* setup the log message */
7352286d8edStholo     message = xmalloc (strlen (rcsdir)
7362286d8edStholo 		       + 80
7372286d8edStholo 		       + (tag == NULL ? 0 : strlen (tag) + 80)
7382286d8edStholo 		       + (date == NULL ? 0 : strlen (date) + 80));
7391e72d8d2Sderaadt     (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
7401e72d8d2Sderaadt     if (tag)
7411e72d8d2Sderaadt     {
7421e72d8d2Sderaadt 	(void) strcat (message, "--> Using per-directory sticky tag `");
7431e72d8d2Sderaadt 	(void) strcat (message, tag);
7441e72d8d2Sderaadt 	(void) strcat (message, "'\n");
7451e72d8d2Sderaadt     }
7461e72d8d2Sderaadt     if (date)
7471e72d8d2Sderaadt     {
7481e72d8d2Sderaadt 	(void) strcat (message, "--> Using per-directory sticky date `");
7491e72d8d2Sderaadt 	(void) strcat (message, date);
7501e72d8d2Sderaadt 	(void) strcat (message, "'\n");
7511e72d8d2Sderaadt     }
7521e72d8d2Sderaadt 
7531e72d8d2Sderaadt     if (!isdir (rcsdir))
7541e72d8d2Sderaadt     {
7551e72d8d2Sderaadt 	mode_t omask;
7561e72d8d2Sderaadt 	Node *p;
7571e72d8d2Sderaadt 	List *ulist;
75850bf276cStholo 	struct logfile_info *li;
7591e72d8d2Sderaadt 
760780d15dfStholo 	/* There used to be some code here which would prompt for
761780d15dfStholo 	   whether to add the directory.  The details of that code had
762780d15dfStholo 	   bitrotted, but more to the point it can't work
763780d15dfStholo 	   client/server, doesn't ask in the right way for GUIs, etc.
764780d15dfStholo 	   A better way of making it harder to accidentally add
765780d15dfStholo 	   directories would be to have to add and commit directories
766780d15dfStholo 	   like for files.  The code was #if 0'd at least since CVS 1.5.  */
7671e72d8d2Sderaadt 
76850bf276cStholo 	if (!noexec)
76950bf276cStholo 	{
77013571821Stholo 	    omask = umask (cvsumask);
7711e72d8d2Sderaadt 	    if (CVS_MKDIR (rcsdir, 0777) < 0)
7721e72d8d2Sderaadt 	    {
7731e72d8d2Sderaadt 		error (0, errno, "cannot mkdir %s", rcsdir);
7741e72d8d2Sderaadt 		(void) umask (omask);
7751e72d8d2Sderaadt 		goto out;
7761e72d8d2Sderaadt 	    }
7771e72d8d2Sderaadt 	    (void) umask (omask);
77850bf276cStholo 	}
7791e72d8d2Sderaadt 
7802286d8edStholo 	/* Now set the default file attributes to the ones we inherited
7812286d8edStholo 	   from the parent directory.  */
7822286d8edStholo 	fileattr_startdir (rcsdir);
7832286d8edStholo 	fileattr_setall (NULL, attrs);
7842286d8edStholo 	fileattr_write ();
7852286d8edStholo 	fileattr_free ();
7862286d8edStholo 	if (attrs != NULL)
7872286d8edStholo 	    free (attrs);
7882286d8edStholo 
7891e72d8d2Sderaadt 	/*
7901e72d8d2Sderaadt 	 * Set up an update list with a single title node for Update_Logfile
7911e72d8d2Sderaadt 	 */
7921e72d8d2Sderaadt 	ulist = getlist ();
7931e72d8d2Sderaadt 	p = getnode ();
7941e72d8d2Sderaadt 	p->type = UPDATE;
7951e72d8d2Sderaadt 	p->delproc = update_delproc;
7961e72d8d2Sderaadt 	p->key = xstrdup ("- New directory");
79750bf276cStholo 	li = (struct logfile_info *) xmalloc (sizeof (struct logfile_info));
79850bf276cStholo 	li->type = T_TITLE;
79950bf276cStholo 	li->tag = xstrdup (tag);
800461cc63eStholo 	li->rev_old = li->rev_new = NULL;
80150bf276cStholo 	p->data = (char *) li;
8021e72d8d2Sderaadt 	(void) addnode (ulist, p);
80350bf276cStholo 	Update_Logfile (rcsdir, message, (FILE *) NULL, ulist);
8041e72d8d2Sderaadt 	dellist (&ulist);
8051e72d8d2Sderaadt     }
8061e72d8d2Sderaadt 
8071e72d8d2Sderaadt #ifdef SERVER_SUPPORT
8081e72d8d2Sderaadt     if (!server_active)
8091e72d8d2Sderaadt #endif
810e77048c1Stholo         Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1);
8111e72d8d2Sderaadt     if (tag)
8121e72d8d2Sderaadt 	free (tag);
8131e72d8d2Sderaadt     if (date)
8141e72d8d2Sderaadt 	free (date);
8151e72d8d2Sderaadt 
81650bf276cStholo     if (restore_cwd (&cwd, NULL))
817461cc63eStholo 	error_exit ();
81850bf276cStholo     free_cwd (&cwd);
81950bf276cStholo 
82050bf276cStholo     Subdir_Register (entries, (char *) NULL, dir);
82150bf276cStholo 
8222286d8edStholo     cvs_output (message, 0);
8232286d8edStholo 
824461cc63eStholo     free (rcsdir);
825461cc63eStholo     free (message);
82650bf276cStholo 
82750bf276cStholo     return (0);
82850bf276cStholo 
8291e72d8d2Sderaadt out:
8301e72d8d2Sderaadt     if (restore_cwd (&cwd, NULL))
831461cc63eStholo 	error_exit ();
8321e72d8d2Sderaadt     free_cwd (&cwd);
833461cc63eStholo     if (rcsdir != NULL)
834461cc63eStholo 	free (rcsdir);
8351e72d8d2Sderaadt     return (0);
8361e72d8d2Sderaadt }
8371e72d8d2Sderaadt 
8381e72d8d2Sderaadt /*
8391e72d8d2Sderaadt  * Builds an entry for a new file and sets up "CVS/file",[pt] by
8401e72d8d2Sderaadt  * interrogating the user.  Returns non-zero on error.
8411e72d8d2Sderaadt  */
8421e72d8d2Sderaadt static int
build_entry(repository,user,options,message,entries,tag)8431e72d8d2Sderaadt build_entry (repository, user, options, message, entries, tag)
8441e72d8d2Sderaadt     char *repository;
8451e72d8d2Sderaadt     char *user;
8461e72d8d2Sderaadt     char *options;
8471e72d8d2Sderaadt     char *message;
8481e72d8d2Sderaadt     List *entries;
8491e72d8d2Sderaadt     char *tag;
8501e72d8d2Sderaadt {
851461cc63eStholo     char *fname;
852780d15dfStholo     char *line;
8531e72d8d2Sderaadt     FILE *fp;
8541e72d8d2Sderaadt 
8551e72d8d2Sderaadt     if (noexec)
8561e72d8d2Sderaadt 	return (0);
8571e72d8d2Sderaadt 
8581e72d8d2Sderaadt     /*
85913571821Stholo      * The requested log is read directly from the user and stored in the
8601e72d8d2Sderaadt      * file user,t.  If the "message" argument is set, use it as the
8611e72d8d2Sderaadt      * initial creation log (which typically describes the file).
8621e72d8d2Sderaadt      */
863461cc63eStholo     fname = xmalloc (strlen (user) + 80);
8641e72d8d2Sderaadt     (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
8651e72d8d2Sderaadt     fp = open_file (fname, "w+");
8661e72d8d2Sderaadt     if (message && fputs (message, fp) == EOF)
8671e72d8d2Sderaadt 	    error (1, errno, "cannot write to %s", fname);
8681e72d8d2Sderaadt     if (fclose(fp) == EOF)
8691e72d8d2Sderaadt         error(1, errno, "cannot close %s", fname);
870461cc63eStholo     free (fname);
8711e72d8d2Sderaadt 
8721e72d8d2Sderaadt     /*
8731e72d8d2Sderaadt      * Create the entry now, since this allows the user to interrupt us above
87413571821Stholo      * without needing to clean anything up (well, we could clean up the
87513571821Stholo      * ,t file, but who cares).
8761e72d8d2Sderaadt      */
877780d15dfStholo     line = xmalloc (strlen (user) + 20);
8781e72d8d2Sderaadt     (void) sprintf (line, "Initial %s", user);
8791e72d8d2Sderaadt     Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
880780d15dfStholo     free (line);
8811e72d8d2Sderaadt     return (0);
8821e72d8d2Sderaadt }
883