checkout.c revision 81405
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1992, Brian Berliner and Jeff Polk
31553Srgrimes * Copyright (c) 1989-1992, Brian Berliner
41553Srgrimes *
51553Srgrimes * You may distribute under the terms of the GNU General Public License as
61553Srgrimes * specified in the README file that comes with the CVS source distribution.
71553Srgrimes *
81553Srgrimes * Create Version
91553Srgrimes *
101553Srgrimes * "checkout" creates a "version" of an RCS repository.  This version is owned
111553Srgrimes * totally by the user and is actually an independent copy, to be dealt with
121553Srgrimes * as seen fit.  Once "checkout" has been called in a given directory, it
131553Srgrimes * never needs to be called again.  The user can keep up-to-date by calling
141553Srgrimes * "update" when he feels like it; this will supply him with a merge of his
151553Srgrimes * own modifications and the changes made in the RCS original.  See "update"
161553Srgrimes * for details.
171553Srgrimes *
181553Srgrimes * "checkout" can be given a list of directories or files to be updated and in
191553Srgrimes * the case of a directory, will recursivley create any sub-directories that
201553Srgrimes * exist in the repository.
211553Srgrimes *
221553Srgrimes * When the user is satisfied with his own modifications, the present version
231553Srgrimes * can be committed by "commit"; this keeps the present version in tact,
241553Srgrimes * usually.
251553Srgrimes *
261553Srgrimes * The call is cvs checkout [options] <module-name>...
271553Srgrimes *
281553Srgrimes * "checkout" creates a directory ./CVS, in which it keeps its administration,
291553Srgrimes * in two files, Repository and Entries. The first contains the name of the
301553Srgrimes * repository.  The second contains one line for each registered file,
311553Srgrimes * consisting of the version number it derives from, its time stamp at
321553Srgrimes * derivation time and its name.  Both files are normal files and can be
331553Srgrimes * edited by the user, if necessary (when the repository is moved, e.g.)
341553Srgrimes */
351553Srgrimes
3631492Swollman#include <assert.h>
371553Srgrimes#include "cvs.h"
381553Srgrimes
391553Srgrimesstatic char *findslash PROTO((char *start, char *p));
401553Srgrimesstatic int checkout_proc PROTO((int argc, char **argv, char *where,
41117554Sgad		          char *mwhere, char *mfile, int shorten,
42117587Sgad		          int local_specified, char *omodule,
4315648Sjoerg		          char *msg));
44117587Sgad
45117554Sgadstatic const char *const checkout_usage[] =
461553Srgrimes{
47117554Sgad    "Usage:\n  %s %s [-ANPRcflnps] [-r rev] [-D date] [-d dir]\n",
48117554Sgad    "    [-j rev1] [-j rev2] [-k kopt] modules...\n",
491553Srgrimes    "\t-A\tReset any sticky tags/date/kopts.\n",
501553Srgrimes    "\t-N\tDon't shorten module paths if -d specified.\n",
511553Srgrimes    "\t-P\tPrune empty directories.\n",
521553Srgrimes    "\t-R\tProcess directories recursively.\n",
531553Srgrimes    "\t-c\t\"cat\" the module database.\n",
541553Srgrimes    "\t-f\tForce a head revision match if tag/date not found.\n",
551553Srgrimes    "\t-l\tLocal directory only, not recursive\n",
561553Srgrimes    "\t-n\tDo not run module program (if any).\n",
571553Srgrimes    "\t-p\tCheck out files to standard output (avoids stickiness).\n",
581553Srgrimes    "\t-s\tLike -c, but include module status.\n",
591553Srgrimes    "\t-r rev\tCheck out revision or tag. (implies -P) (is sticky)\n",
601553Srgrimes    "\t-D date\tCheck out revisions as of date. (implies -P) (is sticky)\n",
611553Srgrimes    "\t-d dir\tCheck out into dir instead of module name.\n",
621553Srgrimes    "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
631553Srgrimes    "\t-j rev\tMerge in changes made between current revision and rev.\n",
641553Srgrimes    "(Specify the --help global option for a list of other help options)\n",
651553Srgrimes    NULL
661553Srgrimes};
671553Srgrimes
681553Srgrimesstatic const char *const export_usage[] =
691553Srgrimes{
701553Srgrimes    "Usage: %s %s [-NRfln] [-r rev] [-D date] [-d dir] [-k kopt] module...\n",
711553Srgrimes    "\t-N\tDon't shorten module paths if -d specified.\n",
7215032Ssef    "\t-f\tForce a head revision match if tag/date not found.\n",
7315032Ssef    "\t-l\tLocal directory only, not recursive\n",
7415703Sjoerg    "\t-R\tProcess directories recursively (default).\n",
751553Srgrimes    "\t-n\tDo not run module program (if any).\n",
761553Srgrimes    "\t-r rev\tExport revision or tag.\n",
771553Srgrimes    "\t-D date\tExport revisions as of date.\n",
781553Srgrimes    "\t-d dir\tExport into dir instead of module name.\n",
791553Srgrimes    "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
8080230Sgad    "(Specify the --help global option for a list of other help options)\n",
8180230Sgad    NULL
821553Srgrimes};
831553Srgrimes
841553Srgrimesstatic int checkout_prune_dirs;
851553Srgrimesstatic int force_tag_match = 1;
861553Srgrimesstatic int pipeout;
871553Srgrimesstatic int aflag;
881553Srgrimesstatic char *options = NULL;
891553Srgrimesstatic char *tag = NULL;
901553Srgrimesstatic int tag_validated = 0;
911553Srgrimesstatic char *date = NULL;
921553Srgrimesstatic char *join_rev1 = NULL;
931553Srgrimesstatic char *join_rev2 = NULL;
941553Srgrimesstatic int join_tags_validated = 0;
951553Srgrimesstatic char *preload_update_dir = NULL;
961553Srgrimesstatic char *history_name = NULL;
9797793Sgadstatic enum mtype m_type;
981553Srgrimes
9968253Sgadint
1001553Srgrimescheckout (argc, argv)
1011553Srgrimes    int argc;
10224831Sbrian    char **argv;
1031553Srgrimes{
1041553Srgrimes    int i;
1051553Srgrimes    int c;
10653956Sache    DBM *db;
1071553Srgrimes    int cat = 0, err = 0, status = 0;
10880230Sgad    int run_module_prog = 1;
10980230Sgad    int local = 0;
11080230Sgad    int shorten = -1;
11180230Sgad    char *where = NULL;
1121553Srgrimes    char *valid_options;
11378300Sgad    const char *const *valid_usage;
1141553Srgrimes
1158857Srgrimes    /*
1161553Srgrimes     * A smaller subset of options are allowed for the export command, which
1171553Srgrimes     * is essentially like checkout, except that it hard-codes certain
1181553Srgrimes     * options to be default (like -kv) and takes care to remove the CVS
1191553Srgrimes     * directory when it has done its duty
1201553Srgrimes     */
12168664Sgad    if (strcmp (command_name, "export") == 0)
12268664Sgad    {
1231553Srgrimes        m_type = EXPORT;
12424831Sbrian	valid_options = "+Nnk:d:flRQqr:D:";
12524831Sbrian	valid_usage = export_usage;
1261553Srgrimes    }
12778146Sgad    else
12878146Sgad    {
12978146Sgad        m_type = CHECKOUT;
13078146Sgad	valid_options = "+ANnk:d:flRpQqcsr:D:j:P";
13178146Sgad	valid_usage = checkout_usage;
13294032Sgad    }
13394032Sgad
13478146Sgad    if (argc == -1)
13578146Sgad	usage (valid_usage);
13678146Sgad
13778146Sgad    ign_setup ();
13878146Sgad    wrap_setup ();
13978146Sgad
14078146Sgad    optind = 0;
14179739Sgad    while ((c = getopt (argc, argv, valid_options)) != -1)
14279739Sgad    {
14378146Sgad	switch (c)
14478146Sgad	{
14578146Sgad	    case 'A':
14678146Sgad		aflag = 1;
14778146Sgad		break;
14895293Sgad	    case 'N':
14978146Sgad		shorten = 0;
15094038Sgad		break;
15178146Sgad	    case 'k':
1521553Srgrimes		if (options)
1531553Srgrimes		    free (options);
15478146Sgad		options = RCS_check_kflag (optarg);
1551553Srgrimes		break;
1561553Srgrimes	    case 'n':
15768401Sgad		run_module_prog = 0;
15868401Sgad		break;
1591553Srgrimes	    case 'Q':
16068733Sgad	    case 'q':
16197793Sgad#ifdef SERVER_SUPPORT
16268733Sgad		/* The CVS 1.5 client sends these options (in addition to
1631553Srgrimes		   Global_option requests), so we must ignore them.  */
16468733Sgad		if (!server_active)
16531492Swollman#endif
16631492Swollman		    error (1, 0,
1671553Srgrimes			   "-q or -Q must be specified before \"%s\"",
16831492Swollman			   command_name);
16997792Sgad		break;
17097792Sgad	    case 'l':
1711553Srgrimes		local = 1;
1721553Srgrimes		break;
1731553Srgrimes	    case 'R':
17497793Sgad		local = 0;
17597793Sgad		break;
17679735Sgad	    case 'P':
17779735Sgad		checkout_prune_dirs = 1;
17879735Sgad		break;
17979735Sgad	    case 'p':
18079735Sgad		pipeout = 1;
18179735Sgad		run_module_prog = 0;	/* don't run module prog when piping */
18279735Sgad		noexec = 1;		/* so no locks will be created */
18379735Sgad		break;
1841553Srgrimes	    case 'c':
1851553Srgrimes		cat = 1;
1861553Srgrimes		break;
1871553Srgrimes	    case 'd':
1881553Srgrimes		where = optarg;
1891553Srgrimes		if (shorten == -1)
1901553Srgrimes		    shorten = 1;
1911553Srgrimes		break;
19231492Swollman	    case 's':
19397792Sgad		cat = status = 1;
19497792Sgad		break;
1951553Srgrimes	    case 'f':
1961553Srgrimes		force_tag_match = 0;
19731492Swollman		break;
1981553Srgrimes	    case 'r':
19931492Swollman		tag = optarg;
20031492Swollman		checkout_prune_dirs = 1;
2011553Srgrimes		break;
20231492Swollman	    case 'D':
20331492Swollman		date = Make_Date (optarg);
20497792Sgad		checkout_prune_dirs = 1;
20597792Sgad		break;
2061553Srgrimes	    case 'j':
2071553Srgrimes		if (join_rev2)
20831492Swollman		    error (1, 0, "only two -j options can be specified");
20931492Swollman		if (join_rev1)
21097792Sgad		    join_rev2 = optarg;
21197792Sgad		else
2121553Srgrimes		    join_rev1 = optarg;
2131553Srgrimes		break;
2141553Srgrimes	    case '?':
2151553Srgrimes	    default:
2161553Srgrimes		usage (valid_usage);
2171553Srgrimes		break;
21897793Sgad	}
2191553Srgrimes    }
2201553Srgrimes    argc -= optind;
22197792Sgad    argv += optind;
22297792Sgad
2231553Srgrimes    if (shorten == -1)
2241553Srgrimes	shorten = 0;
2251553Srgrimes
2261553Srgrimes    if (cat && argc != 0)
2271553Srgrimes	error (1, 0, "-c and -s must not get any arguments");
22831492Swollman
22931492Swollman    if (!cat && argc == 0)
23097792Sgad	error (1, 0, "must specify at least one module or directory");
2311553Srgrimes
2321553Srgrimes    if (where && pipeout)
2331553Srgrimes	error (1, 0, "-d and -p are mutually exclusive");
2341553Srgrimes
23531492Swollman    if (m_type == EXPORT)
23631492Swollman    {
23797792Sgad	if (!tag && !date)
23897792Sgad	    error (1, 0, "must specify a tag or date");
2391553Srgrimes
24068664Sgad	if (tag && isdigit ((unsigned char) tag[0]))
24168664Sgad	    error (1, 0, "tag `%s' must be a symbolic tag", tag);
24268664Sgad    }
24368664Sgad
24497792Sgad#ifdef SERVER_SUPPORT
24568732Sgad    if (server_active && where != NULL)
24668664Sgad    {
24768664Sgad	server_pathname_check (where);
24868664Sgad    }
24997792Sgad#endif
25068732Sgad
25168664Sgad    if (!cat && !safe_location()) {
25268664Sgad        error(1, 0, "Cannot check out files into the repository itself");
25368664Sgad    }
25468664Sgad
25531492Swollman#ifdef CLIENT_SUPPORT
2561553Srgrimes    if (current_parsed_root->isremote)
2571553Srgrimes    {
2581553Srgrimes	int expand_modules;
2591553Srgrimes
2601553Srgrimes	start_server ();
2611553Srgrimes
2621553Srgrimes	ign_setup ();
2631553Srgrimes
26468401Sgad	/* We have to expand names here because the "expand-modules"
2651553Srgrimes           directive to the server has the side-effect of having the
26615648Sjoerg           server send the check-in and update programs for the
2671553Srgrimes           various modules/dirs requested.  If we turn this off and
26815648Sjoerg           simply request the names of the modules and directories (as
26968401Sgad           below in !expand_modules), those files (CVS/Checkin.prog
2701553Srgrimes           or CVS/Update.prog) don't get created.  Grrr.  */
2711553Srgrimes
27297792Sgad	expand_modules = (!cat && !pipeout
27397792Sgad			  && supported_request ("expand-modules"));
27431492Swollman
27568401Sgad	if (expand_modules)
2761553Srgrimes	{
27768401Sgad	    /* This is done here because we need to read responses
2781553Srgrimes               from the server before we send the command checkout or
2791553Srgrimes               export files. */
2801553Srgrimes
2811553Srgrimes	    client_expand_modules (argc, argv, local);
2821553Srgrimes	}
2831553Srgrimes
28431492Swollman	if (!run_module_prog)
2851553Srgrimes	    send_arg ("-n");
2861553Srgrimes	if (local)
28731492Swollman	    send_arg ("-l");
28831492Swollman	if (pipeout)
2891553Srgrimes	    send_arg ("-p");
29031492Swollman	if (!force_tag_match)
29131492Swollman	    send_arg ("-f");
29297792Sgad	if (aflag)
29397792Sgad	    send_arg("-A");
29497792Sgad	if (!shorten)
2951553Srgrimes	    send_arg("-N");
2961553Srgrimes	if (checkout_prune_dirs && m_type == CHECKOUT)
2971553Srgrimes	    send_arg("-P");
29868733Sgad	client_prune_dirs = checkout_prune_dirs;
29968733Sgad	if (cat && !status)
30015648Sjoerg	    send_arg("-c");
30115648Sjoerg	if (where != NULL)
30231492Swollman	    option_with_arg ("-d", where);
30397793Sgad	if (status)
30497793Sgad	    send_arg("-s");
3051553Srgrimes	if (options != NULL && options[0] != '\0')
30697793Sgad	    send_arg (options);
3071553Srgrimes	option_with_arg ("-r", tag);
30879735Sgad	if (date)
30979735Sgad	    client_senddate (date);
31097793Sgad	if (join_rev1 != NULL)
31197793Sgad	    option_with_arg ("-j", join_rev1);
3121553Srgrimes	if (join_rev2 != NULL)
3131553Srgrimes	    option_with_arg ("-j", join_rev2);
3141553Srgrimes
31597792Sgad	if (expand_modules)
31697792Sgad	{
31731492Swollman	    client_send_expansions (local, where, 1);
3181553Srgrimes	}
31915648Sjoerg	else
32031492Swollman	{
32197792Sgad	    int i;
32297792Sgad	    for (i = 0; i < argc; ++i)
32397792Sgad		send_arg (argv[i]);
32415648Sjoerg	    client_nonexpanded_setup ();
32527748Simp	}
32668401Sgad
32768401Sgad	send_to_server (m_type == EXPORT ? "export\012" : "co\012", 0);
32868401Sgad	return get_responses_and_close ();
32915648Sjoerg    }
33031492Swollman#endif /* CLIENT_SUPPORT */
33115648Sjoerg
3321553Srgrimes    if (cat)
3331553Srgrimes    {
33431492Swollman	cat_module (status);
3351553Srgrimes	if (options)
3361553Srgrimes	    free (options);
3371553Srgrimes	return (0);
33831492Swollman    }
33931492Swollman    db = open_module ();
34097792Sgad
3411553Srgrimes
3421553Srgrimes    /* If we've specified something like "cvs co foo/bar baz/quux"
3431553Srgrimes       don't try to shorten names.  There are a few cases in which we
3441553Srgrimes       could shorten (e.g. "cvs co foo/bar foo/baz"), but we don't
34568733Sgad       handle those yet.  Better to have an extra directory created
34631492Swollman       than the thing checked out under the wrong directory name. */
34731492Swollman
34831492Swollman    if (argc > 1)
34931492Swollman	shorten = 0;
35031492Swollman
35131492Swollman
3521553Srgrimes    /* If we will be calling history_write, work out the name to pass
35319202Simp       it.  */
35419202Simp    if (m_type == CHECKOUT && !pipeout)
35568664Sgad    {
3561553Srgrimes	if (tag && date)
3571553Srgrimes	{
3581553Srgrimes	    history_name = xmalloc (strlen (tag) + strlen (date) + 2);
3591553Srgrimes	    sprintf (history_name, "%s:%s", tag, date);
3601553Srgrimes	}
3611553Srgrimes	else if (tag)
3621553Srgrimes	    history_name = tag;
3631553Srgrimes	else
3641553Srgrimes	    history_name = date;
3651553Srgrimes    }
3661553Srgrimes
3671553Srgrimes
3681553Srgrimes    for (i = 0; i < argc; i++)
3691553Srgrimes	err += do_module (db, argv[i], m_type, "Updating", checkout_proc,
3701553Srgrimes			  where, shorten, local, run_module_prog, !pipeout,
3711553Srgrimes			  (char *) NULL);
3721553Srgrimes    close_module (db);
3731553Srgrimes    if (options)
3741553Srgrimes	free (options);
37578146Sgad    return (err);
3761553Srgrimes}
3771553Srgrimes
37868734Sgad/* FIXME: This is and emptydir_name are in checkout.c for historical
37968734Sgad   reasons, probably want to move them.  */
3801553Srgrimes
38168734Sgadint
38268734Sgadsafe_location ()
3831553Srgrimes{
3841553Srgrimes    char *current;
3851553Srgrimes    char hardpath[PATH_MAX+5];
3861553Srgrimes    size_t hardpath_len;
38797792Sgad    int  x;
38897791Sgad    int retval;
3891553Srgrimes
3901553Srgrimes#ifdef HAVE_READLINK
3911553Srgrimes    /* FIXME-arbitrary limit: should be retrying this like xgetwd.
3921553Srgrimes       But how does readlink let us know that the buffer was too small?
3931553Srgrimes       (by returning sizeof hardpath - 1?).  */
3941553Srgrimes    x = readlink(current_parsed_root->directory, hardpath, sizeof hardpath - 1);
39531492Swollman#else
3961553Srgrimes    x = -1;
3971553Srgrimes#endif
39868253Sgad    if (x == -1)
39968253Sgad    {
40068253Sgad        strcpy(hardpath, current_parsed_root->directory);
4011553Srgrimes    }
4021553Srgrimes    else
4031553Srgrimes    {
4041553Srgrimes        hardpath[x] = '\0';
4051553Srgrimes    }
4061553Srgrimes    current = xgetwd ();
4071553Srgrimes    if (current == NULL)
4081553Srgrimes	error (1, errno, "could not get working directory");
4091553Srgrimes    hardpath_len = strlen (hardpath);
4101553Srgrimes    if (strlen (current) >= hardpath_len
4111553Srgrimes	&& strncmp (current, hardpath, hardpath_len) == 0)
4121553Srgrimes    {
4131553Srgrimes	if (/* Current is a subdirectory of hardpath.  */
4141553Srgrimes	    current[hardpath_len] == '/'
4151553Srgrimes
41615648Sjoerg	    /* Current is hardpath itself.  */
4171553Srgrimes	    || current[hardpath_len] == '\0')
4181553Srgrimes	    retval = 0;
41983684Sgad	else
42083684Sgad	    /* It isn't a problem.  For example, current is
4211553Srgrimes	       "/foo/cvsroot-bar" and hardpath is "/foo/cvsroot".  */
4221553Srgrimes	    retval = 1;
4231553Srgrimes    }
4241553Srgrimes    else
4251553Srgrimes	retval = 1;
4261553Srgrimes    free (current);
4271553Srgrimes    return retval;
4281553Srgrimes}
4291553Srgrimes
4301553Srgrimesstruct dir_to_build
4311553Srgrimes{
4321553Srgrimes    /* What to put in CVS/Repository.  */
4331553Srgrimes    char *repository;
4341553Srgrimes    /* The path to the directory.  */
4351553Srgrimes    char *dirpath;
43653956Sache
4371553Srgrimes    /* If set, don't build the directory, just change to it.
4381553Srgrimes       The caller will also want to set REPOSITORY to NULL.  */
4391553Srgrimes    int just_chdir;
4401553Srgrimes
4411553Srgrimes    struct dir_to_build *next;
4421553Srgrimes};
4431553Srgrimes
4441553Srgrimesstatic int build_dirs_and_chdir PROTO ((struct dir_to_build *list,
4451553Srgrimes					int sticky));
44678300Sgad
44727748Simpstatic void build_one_dir PROTO ((char *, char *, int));
44880133Sgad
44927748Simpstatic void
4501553Srgrimesbuild_one_dir (repository, dirpath, sticky)
4511553Srgrimes    char *repository;
4521553Srgrimes    char *dirpath;
45380133Sgad    int sticky;
45431492Swollman{
4551553Srgrimes    FILE *fp;
4561553Srgrimes
45731492Swollman    if (isfile (CVSADM))
4581553Srgrimes    {
4591553Srgrimes	if (m_type == EXPORT)
4601553Srgrimes	    error (1, 0, "cannot export into a working directory");
4611553Srgrimes    }
4621553Srgrimes    else if (m_type == CHECKOUT)
4631553Srgrimes    {
4641553Srgrimes	/* I suspect that this check could be omitted.  */
4651553Srgrimes	if (!isdir (repository))
4661553Srgrimes	    error (1, 0, "there is no repository %s", repository);
4671553Srgrimes
4681553Srgrimes	if (Create_Admin (".", dirpath, repository,
4691553Srgrimes			  sticky ? tag : (char *) NULL,
4701553Srgrimes			  sticky ? date : (char *) NULL,
4711553Srgrimes
4721553Srgrimes			  /* FIXME?  This is a guess.  If it is important
4731553Srgrimes			     for nonbranch to be set correctly here I
4741553Srgrimes			     think we need to write it one way now and
4751553Srgrimes			     then rewrite it later via WriteTag, once
4761553Srgrimes			     we've had a chance to call RCS_nodeisbranch
47727748Simp			     on each file.  */
47880133Sgad			  0, 1, 1))
47927748Simp	    return;
4801553Srgrimes
4811553Srgrimes	if (!noexec)
4821553Srgrimes	{
4831553Srgrimes	    fp = open_file (CVSADM_ENTSTAT, "w+");
4841553Srgrimes	    if (fclose (fp) == EOF)
48580133Sgad		error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
48680133Sgad#ifdef SERVER_SUPPORT
48780133Sgad	    if (server_active)
48880133Sgad		server_set_entstat (dirpath, repository);
4891553Srgrimes#endif
49080133Sgad	}
49180133Sgad    }
4921553Srgrimes}
4931553Srgrimes
4941553Srgrimes/*
49580133Sgad * process_module calls us back here so we do the actual checkout stuff
4961553Srgrimes */
4971553Srgrimes/* ARGSUSED */
4981553Srgrimesstatic int
49931492Swollmancheckout_proc (argc, argv, where_orig, mwhere, mfile, shorten,
50031492Swollman	       local_specified, omodule, msg)
5011553Srgrimes    int argc;
5021553Srgrimes    char **argv;
5031553Srgrimes    char *where_orig;
5041553Srgrimes    char *mwhere;
5051553Srgrimes    char *mfile;
5061553Srgrimes    int shorten;
50727748Simp    int local_specified;
50880133Sgad    char *omodule;
50980133Sgad    char *msg;
51027748Simp{
5111553Srgrimes    char *myargv[2];
5121553Srgrimes    int err = 0;
5131553Srgrimes    int which;
51480133Sgad    char *cp;
5151553Srgrimes    char *repository;
5161553Srgrimes    char *oldupdate = NULL;
5171553Srgrimes    char *where;
51880133Sgad
5191553Srgrimes    /*
5201553Srgrimes     * OK, so we're doing the checkout! Our args are as follows:
52153956Sache     *  argc,argv contain either dir or dir followed by a list of files
52280133Sgad     *  where contains where to put it (if supplied by checkout)
52353956Sache     *  mwhere contains the module name or -d from module file
52453956Sache     *  mfile says do only that part of the module
5251553Srgrimes     *  shorten = 1 says shorten as much as possible
52668467Sgad     *  omodule is the original arg to do_module()
52768467Sgad     */
52868467Sgad
52968467Sgad    /* Set up the repository (maybe) for the bottom directory.
53068467Sgad       Allocate more space than we need so we don't need to keep
53168467Sgad       reallocating this string. */
53268467Sgad    repository = xmalloc (strlen (current_parsed_root->directory)
53397792Sgad			  + strlen (argv[0])
53468467Sgad			  + (mfile == NULL ? 0 : strlen (mfile))
53568467Sgad			  + 10);
53668467Sgad    (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
53797792Sgad    Sanitize_Repository_Name (repository);
53868467Sgad
53968467Sgad
54068467Sgad    /* save the original value of preload_update_dir */
54168467Sgad    if (preload_update_dir != NULL)
5421553Srgrimes	oldupdate = xstrdup (preload_update_dir);
5431553Srgrimes
5441553Srgrimes
5451553Srgrimes    /* Allocate space and set up the where variable.  We allocate more
5461553Srgrimes       space than necessary here so that we don't have to keep
5471553Srgrimes       reallocaing it later on. */
54897791Sgad
5491553Srgrimes    where = xmalloc (strlen (argv[0])
5501553Srgrimes		     + (mfile == NULL ? 0 : strlen (mfile))
5511553Srgrimes		     + (mwhere == NULL ? 0 : strlen (mwhere))
55231492Swollman		     + (where_orig == NULL ? 0 : strlen (where_orig))
5531553Srgrimes		     + 10);
5541553Srgrimes
5551553Srgrimes    /* Yes, this could be written in a less verbose way, but in this
5561553Srgrimes       form it is quite easy to read.
5571553Srgrimes
5581553Srgrimes       FIXME?  The following code that sets should probably be moved
5591553Srgrimes       to do_module in modules.c, since there is similar code in
56015648Sjoerg       patch.c and rtag.c. */
5611553Srgrimes
5621553Srgrimes    if (shorten)
5631553Srgrimes    {
5641553Srgrimes	if (where_orig != NULL)
5651553Srgrimes	{
5661553Srgrimes	    /* If the user has specified a directory with `-d' on the
5671553Srgrimes	       command line, use it preferentially, even over the `-d'
5681553Srgrimes	       flag in the modules file. */
5691553Srgrimes
5701553Srgrimes	    (void) strcpy (where, where_orig);
57131492Swollman	}
57231492Swollman	else if (mwhere != NULL)
5731553Srgrimes	{
5741553Srgrimes	    /* Second preference is the value of mwhere, which is from
5751553Srgrimes	       the `-d' flag in the modules file. */
5761553Srgrimes
57731492Swollman	    (void) strcpy (where, mwhere);
5781553Srgrimes	}
5791553Srgrimes	else
5801553Srgrimes	{
58127748Simp	    /* Third preference is the directory specified in argv[0]
58227748Simp	       which is this module'e directory in the repository. */
5831553Srgrimes
5841553Srgrimes	    (void) strcpy (where, argv[0]);
5851553Srgrimes	}
5861553Srgrimes    }
5871553Srgrimes    else
5881553Srgrimes    {
5891553Srgrimes	/* Use the same preferences here, bug don't shorten -- that
59097791Sgad           is, tack on where_orig if it exists. */
5911553Srgrimes
5921553Srgrimes	*where = '\0';
5931553Srgrimes
5941553Srgrimes	if (where_orig != NULL)
5951553Srgrimes	{
5961553Srgrimes	    (void) strcat (where, where_orig);
5971553Srgrimes	    (void) strcat (where, "/");
5981553Srgrimes	}
5991553Srgrimes
6001553Srgrimes	/* If the -d flag in the modules file specified an absolute
6011553Srgrimes           directory, let the user override it with the command-line
6021553Srgrimes           -d option. */
6031553Srgrimes
60478146Sgad	if ((mwhere != NULL) && (! isabsolute (mwhere)))
6051553Srgrimes	    (void) strcat (where, mwhere);
60653956Sache	else
6071553Srgrimes	    (void) strcat (where, argv[0]);
60831492Swollman    }
6091553Srgrimes    strip_trailing_slashes (where); /* necessary? */
6101553Srgrimes
61197793Sgad
61297793Sgad    /* At this point, the user may have asked for a single file or
6131553Srgrimes       directory from within a module.  In that case, we should modify
6141553Srgrimes       where, repository, and argv as appropriate. */
61568467Sgad
61668467Sgad    if (mfile != NULL)
61797792Sgad    {
61897791Sgad	/* The mfile variable can have one or more path elements.  If
61968467Sgad	   it has multiple elements, we want to tack those onto both
6201553Srgrimes	   repository and where.  The last element may refer to either
6211553Srgrimes	   a file or directory.  Here's what to do:
6221553Srgrimes
6231553Srgrimes	   it refers to a directory
6241553Srgrimes	     -> simply tack it on to where and repository
6251553Srgrimes	   it refers to a file
6261553Srgrimes	     -> munge argv to contain `basename mfile` */
62797791Sgad
62868253Sgad	char *cp;
62968253Sgad	char *path;
63068734Sgad
63168253Sgad
63268253Sgad	/* Paranoia check. */
63331492Swollman
63431492Swollman	if (mfile[strlen (mfile) - 1] == '/')
63531492Swollman	{
6361553Srgrimes	    error (0, 0, "checkout_proc: trailing slash on mfile (%s)!",
63731492Swollman		   mfile);
63883684Sgad	}
63931492Swollman
6401553Srgrimes
6411553Srgrimes	/* Does mfile have multiple path elements? */
6421553Srgrimes
64397791Sgad	cp = strrchr (mfile, '/');
6441553Srgrimes	if (cp != NULL)
6451553Srgrimes	{
64697791Sgad	    *cp = '\0';
6471553Srgrimes	    (void) strcat (repository, "/");
6481553Srgrimes	    (void) strcat (repository, mfile);
6491553Srgrimes	    (void) strcat (where, "/");
65031492Swollman	    (void) strcat (where, mfile);
6511553Srgrimes	    mfile = cp + 1;
65253956Sache	}
65353956Sache
65453956Sache
65553956Sache	/* Now mfile is a single path element. */
65653956Sache
65753956Sache	path = xmalloc (strlen (repository) + strlen (mfile) + 5);
65853956Sache	(void) sprintf (path, "%s/%s", repository, mfile);
65953956Sache	if (isdir (path))
66053956Sache	{
66153956Sache	    /* It's a directory, so tack it on to repository and
6621553Srgrimes               where, as we did above. */
6631553Srgrimes
6641553Srgrimes	    (void) strcat (repository, "/");
6651553Srgrimes	    (void) strcat (repository, mfile);
66631492Swollman	    (void) strcat (where, "/");
6671553Srgrimes	    (void) strcat (where, mfile);
6681553Srgrimes	}
6698094Sjkh	else
67031492Swollman	{
6711553Srgrimes	    /* It's a file, which means we have to screw around with
67253956Sache               argv. */
67353956Sache	    myargv[0] = argv[0];
67479452Sbrian	    myargv[1] = mfile;
6751553Srgrimes	    argc = 2;
6761553Srgrimes	    argv = myargv;
6771553Srgrimes	}
6781553Srgrimes	free (path);
6791553Srgrimes    }
6801553Srgrimes
6811553Srgrimes    if (preload_update_dir != NULL)
6821553Srgrimes    {
68397791Sgad	preload_update_dir =
6841553Srgrimes	    xrealloc (preload_update_dir,
6851553Srgrimes		      strlen (preload_update_dir) + strlen (where) + 5);
6861553Srgrimes	strcat (preload_update_dir, "/");
68731492Swollman	strcat (preload_update_dir, where);
6881553Srgrimes    }
6891553Srgrimes    else
6901553Srgrimes	preload_update_dir = xstrdup (where);
6911553Srgrimes
6921553Srgrimes    /*
69386935Sgad     * At this point, where is the directory we want to build, repository is
69486935Sgad     * the repository for the lowest level of the path.
69586935Sgad     *
69686935Sgad     * We need to tell build_dirs not only the path we want it to
69786935Sgad     * build, but also the repositories we want it to populate the
69886935Sgad     * path with.  To accomplish this, we walk the path backwards, one
69986935Sgad     * pathname component at a time, constucting a linked list of
70086935Sgad     * struct dir_to_build.
70186935Sgad     */
70286935Sgad
70386935Sgad    /*
7041553Srgrimes     * If we are sending everything to stdout, we can skip a whole bunch of
70531492Swollman     * work from here
7061553Srgrimes     */
7071553Srgrimes    if (!pipeout)
7081553Srgrimes    {
7091553Srgrimes	struct dir_to_build *head;
7101553Srgrimes	char *reposcopy;
7111553Srgrimes
7121553Srgrimes	if (strncmp (repository, current_parsed_root->directory,
71331492Swollman		     strlen (current_parsed_root->directory)) != 0)
7141553Srgrimes	    error (1, 0, "\
7151553Srgrimesinternal error: %s doesn't start with %s in checkout_proc",
7161553Srgrimes		   repository, current_parsed_root->directory);
7171553Srgrimes
7181553Srgrimes	/* We always create at least one directory, which corresponds to
7191553Srgrimes	   the entire strings for WHERE and REPOSITORY.  */
7201553Srgrimes	head = (struct dir_to_build *) xmalloc (sizeof (struct dir_to_build));
7211553Srgrimes	/* Special marker to indicate that we don't want build_dirs_and_chdir
7221553Srgrimes	   to create the CVSADM directory for us.  */
72331492Swollman	head->repository = NULL;
72497792Sgad	head->dirpath = xstrdup (where);
7251553Srgrimes	head->next = NULL;
7261553Srgrimes	head->just_chdir = 0;
7271553Srgrimes
7281553Srgrimes
7291553Srgrimes	/* Make a copy of the repository name to play with. */
7301553Srgrimes	reposcopy = xstrdup (repository);
7311553Srgrimes
7321553Srgrimes	/* FIXME: this should be written in terms of last_component
7331553Srgrimes	   instead of hardcoding '/'.  This presumably affects OS/2,
7341553Srgrimes	   NT, &c, if the user specifies '\'.  Likewise for the call
7351553Srgrimes	   to findslash.  */
73631492Swollman	cp = where + strlen (where);
73731492Swollman	while (cp > where)
73831492Swollman	{
7391553Srgrimes	    struct dir_to_build *new;
7401553Srgrimes
7411553Srgrimes	    cp = findslash (where, cp - 1);
7421553Srgrimes	    if (cp == NULL)
7431553Srgrimes		break;		/* we're done */
74431492Swollman
7451553Srgrimes	    new = (struct dir_to_build *)
7461553Srgrimes		xmalloc (sizeof (struct dir_to_build));
7471553Srgrimes	    new->dirpath = xmalloc (strlen (where));
7481553Srgrimes
7491553Srgrimes	    /* If the user specified an absolute path for where, the
75031492Swollman               last path element we create should be the top-level
7511553Srgrimes               directory. */
7521553Srgrimes
7531553Srgrimes	    if (cp > where)
7541553Srgrimes	    {
7551553Srgrimes		strncpy (new->dirpath, where, cp - where);
75631492Swollman		new->dirpath[cp - where] = '\0';
7571553Srgrimes	    }
7581553Srgrimes	    else
7591553Srgrimes	    {
7601553Srgrimes		/* where should always be at least one character long. */
7611553Srgrimes		assert (where[0] != '\0');
7621553Srgrimes		strcpy (new->dirpath, "/");
7631553Srgrimes	    }
76497792Sgad	    new->next = head;
76597791Sgad	    head = new;
7661553Srgrimes
76715648Sjoerg	    /* If where consists of multiple pathname components,
76815648Sjoerg	       then we want to just cd into it, without creating
76915648Sjoerg	       directories or modifying CVS directories as we go.
77015648Sjoerg	       In CVS 1.9 and earlier, the code actually does a
77131492Swollman	       CVS_CHDIR up-front; I'm not going to try to go back
77297791Sgad	       to that exact code but this is somewhat similar
77315648Sjoerg	       in spirit.  */
77427635Simp	    if (where_orig != NULL
7751553Srgrimes		&& cp - where < strlen (where_orig))
7761553Srgrimes	    {
7771553Srgrimes		new->repository = NULL;
7781553Srgrimes		new->just_chdir = 1;
7791553Srgrimes		continue;
7801553Srgrimes	    }
78178300Sgad
78231492Swollman	    new->just_chdir = 0;
7831553Srgrimes
7841553Srgrimes	    /* Now figure out what repository directory to generate.
78597793Sgad               The most complete case would be something like this:
7861553Srgrimes
78797793Sgad	       The modules file contains
78897793Sgad	         foo -d bar/baz quux
7891553Srgrimes
79097793Sgad	       The command issued was:
79197792Sgad	         cvs co -d what/ever -N foo
79297792Sgad
79379735Sgad	       The results in the CVS/Repository files should be:
79497781Sgad	         .     -> (don't touch CVS/Repository)
7951553Srgrimes			  (I think this case might be buggy currently)
79697781Sgad		 what  -> (don't touch CVS/Repository)
79797781Sgad		 ever  -> .          (same as "cd what/ever; cvs co -N foo")
79897793Sgad		 bar   -> Emptydir   (generated dir -- not in repos)
79997781Sgad		 baz   -> quux       (finally!) */
80097791Sgad
8011553Srgrimes	    if (strcmp (reposcopy, current_parsed_root->directory) == 0)
8021553Srgrimes	    {
8031553Srgrimes		/* We can't walk up past CVSROOT.  Instead, the
8041553Srgrimes                   repository should be Emptydir. */
80531492Swollman		new->repository = emptydir_name ();
8061553Srgrimes	    }
8071553Srgrimes	    else
80868664Sgad	    {
80968664Sgad		/* It's a directory in the repository! */
81068664Sgad
8111553Srgrimes		char *rp;
8121553Srgrimes
8138094Sjkh		/* We'll always be below CVSROOT, but check for
81431492Swollman		   paranoia's sake. */
8151553Srgrimes		rp = strrchr (reposcopy, '/');
81695067Sgad		if (rp == NULL)
81795067Sgad		    error (1, 0,
8181553Srgrimes			   "internal error: %s doesn't contain a slash",
8191553Srgrimes			   reposcopy);
8201553Srgrimes
82197789Sgad		*rp = '\0';
8221553Srgrimes		new->repository = xmalloc (strlen (reposcopy) + 5);
82397781Sgad		(void) strcpy (new->repository, reposcopy);
82479735Sgad
82597793Sgad		if (strcmp (reposcopy, current_parsed_root->directory) == 0)
8261553Srgrimes		{
82797793Sgad		    /* Special case -- the repository name needs
82897781Sgad		       to be "/path/to/repos/." (the trailing dot
82997792Sgad		       is important).  We might be able to get rid
83097792Sgad		       of this after the we check out the other
83179735Sgad		       code that handles repository names. */
83297789Sgad		    (void) strcat (new->repository, "/.");
83397789Sgad		}
83497781Sgad	    }
83597789Sgad	}
83679735Sgad
8371553Srgrimes	/* clean up */
8381553Srgrimes	free (reposcopy);
8391553Srgrimes
84097793Sgad	{
8411553Srgrimes	    int where_is_absolute = isabsolute (where);
8421553Srgrimes
8431553Srgrimes	    /* The top-level CVSADM directory should always be
8441553Srgrimes	       current_parsed_root->directory.  Create it, but only if WHERE is
84531492Swollman	       relative.  If WHERE is absolute, our current directory
8461553Srgrimes	       may not have a thing to do with where the sources are
84768664Sgad	       being checked out.  If it does, build_dirs_and_chdir
84868664Sgad	       will take care of creating adm files here. */
8491553Srgrimes	    /* FIXME: checking where_is_absolute is a horrid kludge;
8501553Srgrimes	       I suspect we probably can just skip the call to
8511553Srgrimes	       build_one_dir whenever the -d command option was specified
8521553Srgrimes	       to checkout.  */
8531553Srgrimes
85497789Sgad	    if (! where_is_absolute && top_level_admin)
85515648Sjoerg	    {
85697781Sgad		/* It may be argued that we shouldn't set any sticky
85797791Sgad		   bits for the top-level repository.  FIXME?  */
8581553Srgrimes		build_one_dir (current_parsed_root->directory, ".", argc <= 1);
85997781Sgad
8601553Srgrimes#ifdef SERVER_SUPPORT
86131492Swollman		/* We _always_ want to have a top-level admin
86297791Sgad		   directory.  If we're running in client/server mode,
8631553Srgrimes		   send a "Clear-static-directory" command to make
86497791Sgad		   sure it is created on the client side.  (See 5.10
86515648Sjoerg		   in cvsclient.dvi to convince yourself that this is
86697791Sgad		   OK.)  If this is a duplicate command being sent, it
8671553Srgrimes		   will be ignored on the client side.  */
86815648Sjoerg
86997792Sgad		if (server_active)
87097791Sgad		    server_clear_entstat (".", current_parsed_root->directory);
8711553Srgrimes#endif
8721553Srgrimes	    }
8731553Srgrimes
8741553Srgrimes
8751553Srgrimes	    /* Build dirs on the path if necessary and leave us in the
8761553Srgrimes	       bottom directory (where if where was specified) doesn't
8771553Srgrimes	       contain a CVS subdir yet, but all the others contain
8781553Srgrimes	       CVS and Entries.Static files */
8791553Srgrimes
88078146Sgad	    if (build_dirs_and_chdir (head, argc <= 1) != 0)
8811553Srgrimes	    {
88295293Sgad		error (0, 0, "ignoring module %s", omodule);
8831553Srgrimes		err = 1;
8841553Srgrimes		goto out;
8851553Srgrimes	    }
8861553Srgrimes	}
8871553Srgrimes
8881553Srgrimes	/* set up the repository (or make sure the old one matches) */
88997791Sgad	if (!isfile (CVSADM))
89068253Sgad	{
89168253Sgad	    FILE *fp;
89268253Sgad
89368253Sgad	    if (!noexec && argc > 1)
8941553Srgrimes	    {
8951553Srgrimes		/* I'm not sure whether this check is redundant.  */
8961553Srgrimes		if (!isdir (repository))
8971553Srgrimes		    error (1, 0, "there is no repository %s", repository);
8981553Srgrimes
8991553Srgrimes		Create_Admin (".", preload_update_dir, repository,
9001553Srgrimes			      (char *) NULL, (char *) NULL, 0, 0,
9011553Srgrimes			      m_type == CHECKOUT);
9021553Srgrimes		fp = open_file (CVSADM_ENTSTAT, "w+");
9031553Srgrimes		if (fclose(fp) == EOF)
9041553Srgrimes		    error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
9051553Srgrimes#ifdef SERVER_SUPPORT
9061553Srgrimes		if (server_active)
9071553Srgrimes		    server_set_entstat (where, repository);
9081553Srgrimes#endif
90995293Sgad	    }
9101553Srgrimes	    else
9111553Srgrimes	    {
9121553Srgrimes		/* I'm not sure whether this check is redundant.  */
9131553Srgrimes		if (!isdir (repository))
9141553Srgrimes		    error (1, 0, "there is no repository %s", repository);
9151553Srgrimes
9161553Srgrimes		Create_Admin (".", preload_update_dir, repository, tag, date,
9171553Srgrimes
9181553Srgrimes			      /* FIXME?  This is a guess.  If it is important
9191553Srgrimes				 for nonbranch to be set correctly here I
9201553Srgrimes				 think we need to write it one way now and
9211553Srgrimes				 then rewrite it later via WriteTag, once
9221553Srgrimes				 we've had a chance to call RCS_nodeisbranch
92324831Sbrian				 on each file.  */
92478300Sgad			      0, 0, m_type == CHECKOUT);
92568343Sgad	    }
92680133Sgad	}
92768343Sgad	else
92824831Sbrian	{
92980133Sgad	    char *repos;
93031492Swollman
93124831Sbrian	    if (m_type == EXPORT)
93231492Swollman		error (1, 0, "cannot export into working directory");
93324831Sbrian
93424831Sbrian	    /* get the contents of the previously existing repository */
93524831Sbrian	    repos = Name_Repository ((char *) NULL, preload_update_dir);
93624831Sbrian	    if (fncmp (repository, repos) != 0)
93724831Sbrian	    {
93880133Sgad		error (0, 0, "existing repository %s does not match %s",
93924831Sbrian		       repos, repository);
94095293Sgad		error (0, 0, "ignoring module %s", omodule);
9411553Srgrimes		free (repos);
94295293Sgad		err = 1;
94395293Sgad		goto out;
9441553Srgrimes	    }
94595293Sgad	    free (repos);
94695293Sgad	}
94795293Sgad    }
9481553Srgrimes
9491553Srgrimes    /*
9501553Srgrimes     * If we are going to be updating to stdout, we need to cd to the
9511553Srgrimes     * repository directory so the recursion processor can use the current
9521553Srgrimes     * directory as the place to find repository information
9531553Srgrimes     */
95497791Sgad    if (pipeout)
9551553Srgrimes    {
95631492Swollman	if ( CVS_CHDIR (repository) < 0)
9571553Srgrimes	{
9581553Srgrimes	    error (0, errno, "cannot chdir to %s", repository);
9591553Srgrimes	    err = 1;
9601553Srgrimes	    goto out;
9611553Srgrimes	}
9621553Srgrimes	which = W_REPOS;
96395293Sgad	if (tag != NULL && !tag_validated)
9641553Srgrimes	{
96597791Sgad	    tag_check_valid (tag, argc - 1, argv + 1, 0, aflag, NULL);
9661553Srgrimes	    tag_validated = 1;
9671553Srgrimes	}
9681553Srgrimes    }
9691553Srgrimes    else
9701553Srgrimes    {
9711553Srgrimes	which = W_LOCAL | W_REPOS;
97227748Simp	if (tag != NULL && !tag_validated)
9731553Srgrimes	{
9741553Srgrimes	    tag_check_valid (tag, argc - 1, argv + 1, 0, aflag,
9751553Srgrimes			     repository);
9761553Srgrimes	    tag_validated = 1;
9771553Srgrimes	}
9781553Srgrimes    }
97997791Sgad
9801553Srgrimes    if (tag != NULL || date != NULL || join_rev1 != NULL)
9811553Srgrimes	which |= W_ATTIC;
9821553Srgrimes
9831553Srgrimes    if (! join_tags_validated)
9841553Srgrimes    {
9851553Srgrimes        if (join_rev1 != NULL)
9861553Srgrimes	    tag_check_valid_join (join_rev1, argc - 1, argv + 1, 0, aflag,
98795293Sgad				  repository);
9881553Srgrimes	if (join_rev2 != NULL)
98994036Sgad	    tag_check_valid_join (join_rev2, argc - 1, argv + 1, 0, aflag,
9901553Srgrimes				  repository);
99194032Sgad	join_tags_validated = 1;
99294032Sgad    }
99395293Sgad
9941553Srgrimes    /*
99574124Sgad     * if we are going to be recursive (building dirs), go ahead and call the
99674124Sgad     * update recursion processor.  We will be recursive unless either local
99774124Sgad     * only was specified, or we were passed arguments
99874124Sgad     */
99994036Sgad    if (!(local_specified || argc > 1))
100074124Sgad    {
100194036Sgad	if (m_type == CHECKOUT && !pipeout)
100294036Sgad	    history_write ('O', preload_update_dir, history_name, where,
100374124Sgad			   repository);
100474124Sgad	else if (m_type == EXPORT && !pipeout)
100594036Sgad	    history_write ('E', preload_update_dir, tag ? tag : date, where,
100674124Sgad			   repository);
10071553Srgrimes	err += do_update (0, (char **) NULL, options, tag, date,
10081553Srgrimes			  force_tag_match, 0 /* !local */ ,
10091553Srgrimes			  1 /* update -d */ , aflag, checkout_prune_dirs,
10101553Srgrimes			  pipeout, which, join_rev1, join_rev2,
10111553Srgrimes			  preload_update_dir, m_type == CHECKOUT);
101294036Sgad	goto out;
101394036Sgad    }
101494036Sgad
101594036Sgad    if (!pipeout)
101694036Sgad    {
101724831Sbrian	int i;
101894032Sgad	List *entries;
101994032Sgad
102024831Sbrian	/* we are only doing files, so register them */
102194032Sgad	entries = Entries_Open (0, NULL);
102224831Sbrian	for (i = 1; i < argc; i++)
102394032Sgad	{
102494032Sgad	    char *line;
102594032Sgad	    Vers_TS *vers;
102694032Sgad	    struct file_info finfo;
102794032Sgad
102894032Sgad	    memset (&finfo, 0, sizeof finfo);
102994032Sgad	    finfo.file = argv[i];
103094032Sgad	    /* Shouldn't be used, so set to arbitrary value.  */
103194032Sgad	    finfo.update_dir = NULL;
103294032Sgad	    finfo.fullname = argv[i];
103394032Sgad	    finfo.repository = repository;
103494032Sgad	    finfo.entries = entries;
103594032Sgad	    /* The rcs slot is needed to get the options from the RCS
103694032Sgad               file */
103794032Sgad	    finfo.rcs = RCS_parse (finfo.file, repository);
103894032Sgad
103994032Sgad	    vers = Version_TS (&finfo, options, tag, date,
104094032Sgad			       force_tag_match, 0);
104194032Sgad	    if (vers->ts_user == NULL)
104294032Sgad	    {
104394032Sgad		line = xmalloc (strlen (finfo.file) + 15);
104494032Sgad		(void) sprintf (line, "Initial %s", finfo.file);
104531492Swollman		Register (entries, finfo.file,
104694032Sgad			  vers->vn_rcs ? vers->vn_rcs : "0",
104794032Sgad			  line, vers->options, vers->tag,
104894032Sgad			  vers->date, (char *) 0);
104994032Sgad		free (line);
105094032Sgad	    }
105194032Sgad	    freevers_ts (&vers);
105224831Sbrian	    freercsnode (&finfo.rcs);
105394032Sgad	}
105494032Sgad
105594032Sgad	Entries_Close (entries);
105694032Sgad    }
105794032Sgad
105894032Sgad    /* Don't log "export", just regular "checkouts" */
105994032Sgad    if (m_type == CHECKOUT && !pipeout)
106094032Sgad	history_write ('O', preload_update_dir, history_name, where,
106194032Sgad		       repository);
106294032Sgad
106394032Sgad    /* go ahead and call update now that everything is set */
106494032Sgad    err += do_update (argc - 1, argv + 1, options, tag, date,
106594032Sgad		      force_tag_match, local_specified, 1 /* update -d */,
106694032Sgad		      aflag, checkout_prune_dirs, pipeout, which, join_rev1,
106794032Sgad		      join_rev2, preload_update_dir, m_type == CHECKOUT);
106894032Sgadout:
106994032Sgad    free (preload_update_dir);
107024831Sbrian    preload_update_dir = oldupdate;
107124831Sbrian    free (where);
107294032Sgad    free (repository);
107394032Sgad    return (err);
107494032Sgad}
107594032Sgad
107694032Sgadstatic char *
107794032Sgadfindslash (start, p)
107894032Sgad    char *start;
107994032Sgad    char *p;
108094032Sgad{
108194032Sgad    for (;;)
108294032Sgad    {
108394032Sgad	if (*p == '/') return p;
108494032Sgad	if (p == start) break;
108594036Sgad	--p;
108694036Sgad    }
108794032Sgad    return NULL;
108894036Sgad}
108924831Sbrian
109094032Sgad/* Return a newly malloc'd string containing a pathname for CVSNULLREPOS,
109194032Sgad   and make sure that it exists.  If there is an error creating the
109294032Sgad   directory, give a fatal error.  Otherwise, the directory is guaranteed
109394032Sgad   to exist when we return.  */
109494032Sgadchar *
109594036Sgademptydir_name ()
109694036Sgad{
109794032Sgad    char *repository;
109894036Sgad
109994036Sgad    repository = xmalloc (strlen (current_parsed_root->directory)
110094032Sgad			  + sizeof (CVSROOTADM)
110194032Sgad			  + sizeof (CVSNULLREPOS)
110294032Sgad			  + 3);
110394032Sgad    (void) sprintf (repository, "%s/%s/%s", current_parsed_root->directory,
110494036Sgad		    CVSROOTADM, CVSNULLREPOS);
110594036Sgad    if (!isfile (repository))
110694032Sgad    {
110794032Sgad	mode_t omask;
110894032Sgad	omask = umask (cvsumask);
110994032Sgad	if (CVS_MKDIR (repository, 0777) < 0)
111094032Sgad	    error (1, errno, "cannot create %s", repository);
111194032Sgad	(void) umask (omask);
111294036Sgad    }
111394036Sgad    return repository;
111494032Sgad}
111594036Sgad
111694036Sgad/* Build all the dirs along the path to DIRS with CVS subdirs with appropriate
111794036Sgad   repositories.  If ->repository is NULL, do not create a CVSADM directory
111894032Sgad   for that subdirectory; just CVS_CHDIR into it.  */
111994032Sgadstatic int
112095293Sgadbuild_dirs_and_chdir (dirs, sticky)
112195293Sgad    struct dir_to_build *dirs;
112295293Sgad    int sticky;
112395293Sgad{
112495293Sgad    int retval = 0;
112595293Sgad    struct dir_to_build *nextdir;
112695293Sgad
112795293Sgad    while (dirs != NULL)
112895293Sgad    {
11291553Srgrimes	char *dir = last_component (dirs->dirpath);
11301553Srgrimes
11311553Srgrimes	if (!dirs->just_chdir)
113231492Swollman	{
113394036Sgad	    mkdir_if_needed (dir);
113494036Sgad	    Subdir_Register (NULL, NULL, dir);
11351553Srgrimes	}
11361553Srgrimes
11371553Srgrimes	if (CVS_CHDIR (dir) < 0)
113831492Swollman	{
113931492Swollman	    error (0, errno, "cannot chdir to %s", dir);
11401553Srgrimes	    retval = 1;
11411553Srgrimes	    goto out;
114297792Sgad	}
11431553Srgrimes	if (dirs->repository != NULL)
11441553Srgrimes	{
11451553Srgrimes	    build_one_dir (dirs->repository, dirs->dirpath, sticky);
114631492Swollman	    free (dirs->repository);
114795293Sgad	}
114895293Sgad	nextdir = dirs->next;
114995293Sgad	free (dirs->dirpath);
115095293Sgad	free (dirs);
115168253Sgad	dirs = nextdir;
115268253Sgad    }
11531553Srgrimes
11541553Srgrimes out:
11551553Srgrimes    while (dirs != NULL)
11561553Srgrimes    {
115794036Sgad	if (dirs->repository != NULL)
11581553Srgrimes	    free (dirs->repository);
11591553Srgrimes	nextdir = dirs->next;
116094036Sgad	free (dirs->dirpath);
116194036Sgad	free (dirs);
11621553Srgrimes	dirs = nextdir;
11631553Srgrimes    }
11641553Srgrimes    return retval;
11651553Srgrimes}
116631492Swollman