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