xref: /dflybsd-src/contrib/cvs-1.12/src/checkout.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5*86d7f5d3SJohn Marino  *                                  and others.
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8*86d7f5d3SJohn Marino  * Portions Copyright (C) 1989-1992, Brian Berliner
9*86d7f5d3SJohn Marino  *
10*86d7f5d3SJohn Marino  * You may distribute under the terms of the GNU General Public License as
11*86d7f5d3SJohn Marino  * specified in the README file that comes with the CVS source distribution.
12*86d7f5d3SJohn Marino  *
13*86d7f5d3SJohn Marino  * Create Version
14*86d7f5d3SJohn Marino  *
15*86d7f5d3SJohn Marino  * "checkout" creates a "version" of an RCS repository.  This version is owned
16*86d7f5d3SJohn Marino  * totally by the user and is actually an independent copy, to be dealt with
17*86d7f5d3SJohn Marino  * as seen fit.  Once "checkout" has been called in a given directory, it
18*86d7f5d3SJohn Marino  * never needs to be called again.  The user can keep up-to-date by calling
19*86d7f5d3SJohn Marino  * "update" when he feels like it; this will supply him with a merge of his
20*86d7f5d3SJohn Marino  * own modifications and the changes made in the RCS original.  See "update"
21*86d7f5d3SJohn Marino  * for details.
22*86d7f5d3SJohn Marino  *
23*86d7f5d3SJohn Marino  * "checkout" can be given a list of directories or files to be updated and in
24*86d7f5d3SJohn Marino  * the case of a directory, will recursivley create any sub-directories that
25*86d7f5d3SJohn Marino  * exist in the repository.
26*86d7f5d3SJohn Marino  *
27*86d7f5d3SJohn Marino  * When the user is satisfied with his own modifications, the present version
28*86d7f5d3SJohn Marino  * can be committed by "commit"; this keeps the present version in tact,
29*86d7f5d3SJohn Marino  * usually.
30*86d7f5d3SJohn Marino  *
31*86d7f5d3SJohn Marino  * The call is cvs checkout [options] <module-name>...
32*86d7f5d3SJohn Marino  *
33*86d7f5d3SJohn Marino  * "checkout" creates a directory ./CVS, in which it keeps its administration,
34*86d7f5d3SJohn Marino  * in two files, Repository and Entries. The first contains the name of the
35*86d7f5d3SJohn Marino  * repository.  The second contains one line for each registered file,
36*86d7f5d3SJohn Marino  * consisting of the version number it derives from, its time stamp at
37*86d7f5d3SJohn Marino  * derivation time and its name.  Both files are normal files and can be
38*86d7f5d3SJohn Marino  * edited by the user, if necessary (when the repository is moved, e.g.)
39*86d7f5d3SJohn Marino  */
40*86d7f5d3SJohn Marino 
41*86d7f5d3SJohn Marino #include "cvs.h"
42*86d7f5d3SJohn Marino 
43*86d7f5d3SJohn Marino static char *findslash (char *start, char *p);
44*86d7f5d3SJohn Marino static int checkout_proc (int argc, char **argv, char *where,
45*86d7f5d3SJohn Marino 		          char *mwhere, char *mfile, int shorten,
46*86d7f5d3SJohn Marino 		          int local_specified, char *omodule,
47*86d7f5d3SJohn Marino 		          char *msg);
48*86d7f5d3SJohn Marino 
49*86d7f5d3SJohn Marino static const char *const checkout_usage[] =
50*86d7f5d3SJohn Marino {
51*86d7f5d3SJohn Marino     "Usage:\n  %s %s [-ANPRcflnps] [-r rev] [-D date] [-d dir]\n",
52*86d7f5d3SJohn Marino     "    [-j rev1] [-j rev2] [-k kopt] modules...\n",
53*86d7f5d3SJohn Marino     "\t-A\tReset any sticky tags/date/kopts.\n",
54*86d7f5d3SJohn Marino     "\t-N\tDon't shorten module paths if -d specified.\n",
55*86d7f5d3SJohn Marino     "\t-P\tPrune empty directories.\n",
56*86d7f5d3SJohn Marino     "\t-R\tProcess directories recursively.\n",
57*86d7f5d3SJohn Marino     "\t-c\t\"cat\" the module database.\n",
58*86d7f5d3SJohn Marino     "\t-f\tForce a head revision match if tag/date not found.\n",
59*86d7f5d3SJohn Marino     "\t-l\tLocal directory only, not recursive\n",
60*86d7f5d3SJohn Marino     "\t-n\tDo not run module program (if any).\n",
61*86d7f5d3SJohn Marino     "\t-p\tCheck out files to standard output (avoids stickiness).\n",
62*86d7f5d3SJohn Marino     "\t-s\tLike -c, but include module status.\n",
63*86d7f5d3SJohn Marino     "\t-r rev\tCheck out revision or tag. (implies -P) (is sticky)\n",
64*86d7f5d3SJohn Marino     "\t-D date\tCheck out revisions as of date. (implies -P) (is sticky)\n",
65*86d7f5d3SJohn Marino     "\t-d dir\tCheck out into dir instead of module name.\n",
66*86d7f5d3SJohn Marino     "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
67*86d7f5d3SJohn Marino     "\t-j rev\tMerge in changes made between current revision and rev.\n",
68*86d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options)\n",
69*86d7f5d3SJohn Marino     NULL
70*86d7f5d3SJohn Marino };
71*86d7f5d3SJohn Marino 
72*86d7f5d3SJohn Marino static const char *const export_usage[] =
73*86d7f5d3SJohn Marino {
74*86d7f5d3SJohn Marino     "Usage: %s %s [-NRfln] [-r tag] [-D date] [-d dir] [-k kopt] module...\n",
75*86d7f5d3SJohn Marino     "\t-N\tDon't shorten module paths if -d specified.\n",
76*86d7f5d3SJohn Marino     "\t-f\tForce a head revision match if tag/date not found.\n",
77*86d7f5d3SJohn Marino     "\t-l\tLocal directory only, not recursive\n",
78*86d7f5d3SJohn Marino     "\t-R\tProcess directories recursively (default).\n",
79*86d7f5d3SJohn Marino     "\t-n\tDo not run module program (if any).\n",
80*86d7f5d3SJohn Marino     "\t-r tag\tExport tagged revisions.\n",
81*86d7f5d3SJohn Marino     "\t-D date\tExport revisions as of date.\n",
82*86d7f5d3SJohn Marino     "\t-d dir\tExport into dir instead of module name.\n",
83*86d7f5d3SJohn Marino     "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
84*86d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options)\n",
85*86d7f5d3SJohn Marino     NULL
86*86d7f5d3SJohn Marino };
87*86d7f5d3SJohn Marino 
88*86d7f5d3SJohn Marino static int checkout_prune_dirs;
89*86d7f5d3SJohn Marino static int force_tag_match;
90*86d7f5d3SJohn Marino static int pipeout;
91*86d7f5d3SJohn Marino static int aflag;
92*86d7f5d3SJohn Marino static char *options;
93*86d7f5d3SJohn Marino static char *tag;
94*86d7f5d3SJohn Marino static bool tag_validated;
95*86d7f5d3SJohn Marino static char *date;
96*86d7f5d3SJohn Marino static char *join_rev1, *join_date1;
97*86d7f5d3SJohn Marino static char *join_rev2, *join_date2;
98*86d7f5d3SJohn Marino static bool join_tags_validated;
99*86d7f5d3SJohn Marino static char *preload_update_dir;
100*86d7f5d3SJohn Marino static char *history_name;
101*86d7f5d3SJohn Marino static enum mtype m_type;
102*86d7f5d3SJohn Marino 
103*86d7f5d3SJohn Marino int
checkout(int argc,char ** argv)104*86d7f5d3SJohn Marino checkout (int argc, char **argv)
105*86d7f5d3SJohn Marino {
106*86d7f5d3SJohn Marino     int i;
107*86d7f5d3SJohn Marino     int c;
108*86d7f5d3SJohn Marino     DBM *db;
109*86d7f5d3SJohn Marino     int cat = 0, err = 0, status = 0;
110*86d7f5d3SJohn Marino     int run_module_prog = 1;
111*86d7f5d3SJohn Marino     int local = 0;
112*86d7f5d3SJohn Marino     int shorten = -1;
113*86d7f5d3SJohn Marino     char *where = NULL;
114*86d7f5d3SJohn Marino     const char *valid_options;
115*86d7f5d3SJohn Marino     const char *const *valid_usage;
116*86d7f5d3SJohn Marino     char *join_orig1, *join_orig2;
117*86d7f5d3SJohn Marino 
118*86d7f5d3SJohn Marino     /* initialize static options */
119*86d7f5d3SJohn Marino     force_tag_match = 1;
120*86d7f5d3SJohn Marino     if (options)
121*86d7f5d3SJohn Marino     {
122*86d7f5d3SJohn Marino 	free (options);
123*86d7f5d3SJohn Marino 	options = NULL;
124*86d7f5d3SJohn Marino     }
125*86d7f5d3SJohn Marino     tag = date = join_rev1 = join_date1 = join_rev2 = join_date2 =
126*86d7f5d3SJohn Marino 	  join_orig1 = join_orig2 = preload_update_dir = NULL;
127*86d7f5d3SJohn Marino     history_name = NULL;
128*86d7f5d3SJohn Marino     tag_validated = join_tags_validated = false;
129*86d7f5d3SJohn Marino 
130*86d7f5d3SJohn Marino 
131*86d7f5d3SJohn Marino     /*
132*86d7f5d3SJohn Marino      * A smaller subset of options are allowed for the export command, which
133*86d7f5d3SJohn Marino      * is essentially like checkout, except that it hard-codes certain
134*86d7f5d3SJohn Marino      * options to be default (like -kv) and takes care to remove the CVS
135*86d7f5d3SJohn Marino      * directory when it has done its duty
136*86d7f5d3SJohn Marino      */
137*86d7f5d3SJohn Marino     if (strcmp (cvs_cmd_name, "export") == 0)
138*86d7f5d3SJohn Marino     {
139*86d7f5d3SJohn Marino         m_type = EXPORT;
140*86d7f5d3SJohn Marino 	valid_options = "+Nnk:d:flRQqr:D:";
141*86d7f5d3SJohn Marino 	valid_usage = export_usage;
142*86d7f5d3SJohn Marino     }
143*86d7f5d3SJohn Marino     else
144*86d7f5d3SJohn Marino     {
145*86d7f5d3SJohn Marino         m_type = CHECKOUT;
146*86d7f5d3SJohn Marino 	valid_options = "+ANnk:d:flRpQqcsr:D:j:P";
147*86d7f5d3SJohn Marino 	valid_usage = checkout_usage;
148*86d7f5d3SJohn Marino     }
149*86d7f5d3SJohn Marino 
150*86d7f5d3SJohn Marino     if (argc == -1)
151*86d7f5d3SJohn Marino 	usage (valid_usage);
152*86d7f5d3SJohn Marino 
153*86d7f5d3SJohn Marino     ign_setup ();
154*86d7f5d3SJohn Marino     wrap_setup ();
155*86d7f5d3SJohn Marino 
156*86d7f5d3SJohn Marino     optind = 0;
157*86d7f5d3SJohn Marino     while ((c = getopt (argc, argv, valid_options)) != -1)
158*86d7f5d3SJohn Marino     {
159*86d7f5d3SJohn Marino 	switch (c)
160*86d7f5d3SJohn Marino 	{
161*86d7f5d3SJohn Marino 	    case 'A':
162*86d7f5d3SJohn Marino 		aflag = 1;
163*86d7f5d3SJohn Marino 		break;
164*86d7f5d3SJohn Marino 	    case 'N':
165*86d7f5d3SJohn Marino 		shorten = 0;
166*86d7f5d3SJohn Marino 		break;
167*86d7f5d3SJohn Marino 	    case 'k':
168*86d7f5d3SJohn Marino 		if (options)
169*86d7f5d3SJohn Marino 		    free (options);
170*86d7f5d3SJohn Marino 		options = RCS_check_kflag (optarg);
171*86d7f5d3SJohn Marino 		break;
172*86d7f5d3SJohn Marino 	    case 'n':
173*86d7f5d3SJohn Marino 		run_module_prog = 0;
174*86d7f5d3SJohn Marino 		break;
175*86d7f5d3SJohn Marino 	    case 'Q':
176*86d7f5d3SJohn Marino 	    case 'q':
177*86d7f5d3SJohn Marino 		/* The CVS 1.5 client sends these options (in addition to
178*86d7f5d3SJohn Marino 		   Global_option requests), so we must ignore them.  */
179*86d7f5d3SJohn Marino 		if (!server_active)
180*86d7f5d3SJohn Marino 		    error (1, 0,
181*86d7f5d3SJohn Marino 			   "-q or -Q must be specified before \"%s\"",
182*86d7f5d3SJohn Marino 			   cvs_cmd_name);
183*86d7f5d3SJohn Marino 		break;
184*86d7f5d3SJohn Marino 	    case 'l':
185*86d7f5d3SJohn Marino 		local = 1;
186*86d7f5d3SJohn Marino 		break;
187*86d7f5d3SJohn Marino 	    case 'R':
188*86d7f5d3SJohn Marino 		local = 0;
189*86d7f5d3SJohn Marino 		break;
190*86d7f5d3SJohn Marino 	    case 'P':
191*86d7f5d3SJohn Marino 		checkout_prune_dirs = 1;
192*86d7f5d3SJohn Marino 		break;
193*86d7f5d3SJohn Marino 	    case 'p':
194*86d7f5d3SJohn Marino 		pipeout = 1;
195*86d7f5d3SJohn Marino 		run_module_prog = 0;	/* don't run module prog when piping */
196*86d7f5d3SJohn Marino 		noexec = 1;		/* so no locks will be created */
197*86d7f5d3SJohn Marino 		break;
198*86d7f5d3SJohn Marino 	    case 'c':
199*86d7f5d3SJohn Marino 		cat = 1;
200*86d7f5d3SJohn Marino 		break;
201*86d7f5d3SJohn Marino 	    case 'd':
202*86d7f5d3SJohn Marino 		where = optarg;
203*86d7f5d3SJohn Marino 		if (shorten == -1)
204*86d7f5d3SJohn Marino 		    shorten = 1;
205*86d7f5d3SJohn Marino 		break;
206*86d7f5d3SJohn Marino 	    case 's':
207*86d7f5d3SJohn Marino 		cat = status = 1;
208*86d7f5d3SJohn Marino 		break;
209*86d7f5d3SJohn Marino 	    case 'f':
210*86d7f5d3SJohn Marino 		force_tag_match = 0;
211*86d7f5d3SJohn Marino 		break;
212*86d7f5d3SJohn Marino 	    case 'r':
213*86d7f5d3SJohn Marino 		parse_tagdate (&tag, &date, optarg);
214*86d7f5d3SJohn Marino 		checkout_prune_dirs = 1;
215*86d7f5d3SJohn Marino 		break;
216*86d7f5d3SJohn Marino 	    case 'D':
217*86d7f5d3SJohn Marino 		if (date) free (date);
218*86d7f5d3SJohn Marino 		date = Make_Date (optarg);
219*86d7f5d3SJohn Marino 		checkout_prune_dirs = 1;
220*86d7f5d3SJohn Marino 		break;
221*86d7f5d3SJohn Marino 	    case 'j':
222*86d7f5d3SJohn Marino 		if (join_rev2 || join_date2)
223*86d7f5d3SJohn Marino 		    error (1, 0, "only two -j options can be specified");
224*86d7f5d3SJohn Marino 		if (join_rev1 || join_date1)
225*86d7f5d3SJohn Marino 		{
226*86d7f5d3SJohn Marino 		    if (join_orig2) free (join_orig2);
227*86d7f5d3SJohn Marino 		    join_orig2 = xstrdup (optarg);
228*86d7f5d3SJohn Marino 		    parse_tagdate (&join_rev2, &join_date2, optarg);
229*86d7f5d3SJohn Marino 		}
230*86d7f5d3SJohn Marino 		else
231*86d7f5d3SJohn Marino 		{
232*86d7f5d3SJohn Marino 		    if (join_orig1) free (join_orig1);
233*86d7f5d3SJohn Marino 		    join_orig1 = xstrdup (optarg);
234*86d7f5d3SJohn Marino 		    parse_tagdate (&join_rev1, &join_date1, optarg);
235*86d7f5d3SJohn Marino 		}
236*86d7f5d3SJohn Marino 		break;
237*86d7f5d3SJohn Marino 	    case '?':
238*86d7f5d3SJohn Marino 	    default:
239*86d7f5d3SJohn Marino 		usage (valid_usage);
240*86d7f5d3SJohn Marino 		break;
241*86d7f5d3SJohn Marino 	}
242*86d7f5d3SJohn Marino     }
243*86d7f5d3SJohn Marino     argc -= optind;
244*86d7f5d3SJohn Marino     argv += optind;
245*86d7f5d3SJohn Marino 
246*86d7f5d3SJohn Marino     if (shorten == -1)
247*86d7f5d3SJohn Marino 	shorten = 0;
248*86d7f5d3SJohn Marino 
249*86d7f5d3SJohn Marino     if (cat && argc != 0)
250*86d7f5d3SJohn Marino 	error (1, 0, "-c and -s must not get any arguments");
251*86d7f5d3SJohn Marino 
252*86d7f5d3SJohn Marino     if (!cat && argc == 0)
253*86d7f5d3SJohn Marino 	error (1, 0, "must specify at least one module or directory");
254*86d7f5d3SJohn Marino 
255*86d7f5d3SJohn Marino     if (where && pipeout)
256*86d7f5d3SJohn Marino 	error (1, 0, "-d and -p are mutually exclusive");
257*86d7f5d3SJohn Marino 
258*86d7f5d3SJohn Marino     if (m_type == EXPORT)
259*86d7f5d3SJohn Marino     {
260*86d7f5d3SJohn Marino 	if (!tag && !date)
261*86d7f5d3SJohn Marino 	    error (1, 0, "must specify a tag or date");
262*86d7f5d3SJohn Marino 
263*86d7f5d3SJohn Marino 	if (tag && isdigit (tag[0]))
264*86d7f5d3SJohn Marino 	    error (1, 0, "tag `%s' must be a symbolic tag", tag);
265*86d7f5d3SJohn Marino     }
266*86d7f5d3SJohn Marino 
267*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
268*86d7f5d3SJohn Marino     if (server_active && where != NULL)
269*86d7f5d3SJohn Marino     {
270*86d7f5d3SJohn Marino 	server_pathname_check (where);
271*86d7f5d3SJohn Marino     }
272*86d7f5d3SJohn Marino #endif
273*86d7f5d3SJohn Marino 
274*86d7f5d3SJohn Marino     if (!cat && !pipeout && !safe_location (where))
275*86d7f5d3SJohn Marino     {
276*86d7f5d3SJohn Marino         error (1, 0, "Cannot check out files into the repository itself");
277*86d7f5d3SJohn Marino     }
278*86d7f5d3SJohn Marino 
279*86d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
280*86d7f5d3SJohn Marino     if (current_parsed_root->isremote)
281*86d7f5d3SJohn Marino     {
282*86d7f5d3SJohn Marino 	int expand_modules;
283*86d7f5d3SJohn Marino 
284*86d7f5d3SJohn Marino 	start_server ();
285*86d7f5d3SJohn Marino 
286*86d7f5d3SJohn Marino 	ign_setup ();
287*86d7f5d3SJohn Marino 
288*86d7f5d3SJohn Marino 	expand_modules = (!cat && !pipeout
289*86d7f5d3SJohn Marino 			  && supported_request ("expand-modules"));
290*86d7f5d3SJohn Marino 
291*86d7f5d3SJohn Marino 	if (expand_modules)
292*86d7f5d3SJohn Marino 	{
293*86d7f5d3SJohn Marino 	    /* This is done here because we need to read responses
294*86d7f5d3SJohn Marino                from the server before we send the command checkout or
295*86d7f5d3SJohn Marino                export files. */
296*86d7f5d3SJohn Marino 
297*86d7f5d3SJohn Marino 	    client_expand_modules (argc, argv, local);
298*86d7f5d3SJohn Marino 	}
299*86d7f5d3SJohn Marino 
300*86d7f5d3SJohn Marino 	if (!run_module_prog)
301*86d7f5d3SJohn Marino 	    send_arg ("-n");
302*86d7f5d3SJohn Marino 	if (local)
303*86d7f5d3SJohn Marino 	    send_arg ("-l");
304*86d7f5d3SJohn Marino 	if (pipeout)
305*86d7f5d3SJohn Marino 	    send_arg ("-p");
306*86d7f5d3SJohn Marino 	if (!force_tag_match)
307*86d7f5d3SJohn Marino 	    send_arg ("-f");
308*86d7f5d3SJohn Marino 	if (aflag)
309*86d7f5d3SJohn Marino 	    send_arg ("-A");
310*86d7f5d3SJohn Marino 	if (!shorten)
311*86d7f5d3SJohn Marino 	    send_arg ("-N");
312*86d7f5d3SJohn Marino 	if (checkout_prune_dirs && m_type == CHECKOUT)
313*86d7f5d3SJohn Marino 	    send_arg ("-P");
314*86d7f5d3SJohn Marino 	client_prune_dirs = checkout_prune_dirs;
315*86d7f5d3SJohn Marino 	if (cat && !status)
316*86d7f5d3SJohn Marino 	    send_arg ("-c");
317*86d7f5d3SJohn Marino 	if (where != NULL)
318*86d7f5d3SJohn Marino 	    option_with_arg ("-d", where);
319*86d7f5d3SJohn Marino 	if (status)
320*86d7f5d3SJohn Marino 	    send_arg ("-s");
321*86d7f5d3SJohn Marino 	if (options != NULL && options[0] != '\0')
322*86d7f5d3SJohn Marino 	    send_arg (options);
323*86d7f5d3SJohn Marino 	option_with_arg ("-r", tag);
324*86d7f5d3SJohn Marino 	if (date)
325*86d7f5d3SJohn Marino 	    client_senddate (date);
326*86d7f5d3SJohn Marino 	if (join_orig1)
327*86d7f5d3SJohn Marino 	    option_with_arg ("-j", join_orig1);
328*86d7f5d3SJohn Marino 	if (join_orig2)
329*86d7f5d3SJohn Marino 	    option_with_arg ("-j", join_orig2);
330*86d7f5d3SJohn Marino 	send_arg ("--");
331*86d7f5d3SJohn Marino 
332*86d7f5d3SJohn Marino 	if (expand_modules)
333*86d7f5d3SJohn Marino 	{
334*86d7f5d3SJohn Marino 	    client_send_expansions (local, where, 1);
335*86d7f5d3SJohn Marino 	}
336*86d7f5d3SJohn Marino 	else
337*86d7f5d3SJohn Marino 	{
338*86d7f5d3SJohn Marino 	    int i;
339*86d7f5d3SJohn Marino 	    for (i = 0; i < argc; ++i)
340*86d7f5d3SJohn Marino 		send_arg (argv[i]);
341*86d7f5d3SJohn Marino 	    client_nonexpanded_setup ();
342*86d7f5d3SJohn Marino 	}
343*86d7f5d3SJohn Marino 
344*86d7f5d3SJohn Marino 	send_to_server (m_type == EXPORT ? "export\012" : "co\012", 0);
345*86d7f5d3SJohn Marino 	return get_responses_and_close ();
346*86d7f5d3SJohn Marino     }
347*86d7f5d3SJohn Marino #endif /* CLIENT_SUPPORT */
348*86d7f5d3SJohn Marino 
349*86d7f5d3SJohn Marino     if (cat)
350*86d7f5d3SJohn Marino     {
351*86d7f5d3SJohn Marino 	cat_module (status);
352*86d7f5d3SJohn Marino 	if (options)
353*86d7f5d3SJohn Marino 	{
354*86d7f5d3SJohn Marino 	    free (options);
355*86d7f5d3SJohn Marino 	    options = NULL;
356*86d7f5d3SJohn Marino 	}
357*86d7f5d3SJohn Marino 	return 0;
358*86d7f5d3SJohn Marino     }
359*86d7f5d3SJohn Marino     db = open_module ();
360*86d7f5d3SJohn Marino 
361*86d7f5d3SJohn Marino 
362*86d7f5d3SJohn Marino     /* If we've specified something like "cvs co foo/bar baz/quux"
363*86d7f5d3SJohn Marino        don't try to shorten names.  There are a few cases in which we
364*86d7f5d3SJohn Marino        could shorten (e.g. "cvs co foo/bar foo/baz"), but we don't
365*86d7f5d3SJohn Marino        handle those yet.  Better to have an extra directory created
366*86d7f5d3SJohn Marino        than the thing checked out under the wrong directory name. */
367*86d7f5d3SJohn Marino 
368*86d7f5d3SJohn Marino     if (argc > 1)
369*86d7f5d3SJohn Marino 	shorten = 0;
370*86d7f5d3SJohn Marino 
371*86d7f5d3SJohn Marino 
372*86d7f5d3SJohn Marino     /* If we will be calling history_write, work out the name to pass
373*86d7f5d3SJohn Marino        it.  */
374*86d7f5d3SJohn Marino     if (!pipeout)
375*86d7f5d3SJohn Marino     {
376*86d7f5d3SJohn Marino 	if (!date)
377*86d7f5d3SJohn Marino 	    history_name = tag;
378*86d7f5d3SJohn Marino 	else if (!tag)
379*86d7f5d3SJohn Marino 	    history_name = date;
380*86d7f5d3SJohn Marino 	else
381*86d7f5d3SJohn Marino 	    history_name = Xasprintf ("%s:%s", tag, date);
382*86d7f5d3SJohn Marino     }
383*86d7f5d3SJohn Marino 
384*86d7f5d3SJohn Marino 
385*86d7f5d3SJohn Marino     for (i = 0; i < argc; i++)
386*86d7f5d3SJohn Marino 	err += do_module (db, argv[i], m_type, "Updating", checkout_proc,
387*86d7f5d3SJohn Marino 			  where, shorten, local, run_module_prog, !pipeout,
388*86d7f5d3SJohn Marino 			  NULL);
389*86d7f5d3SJohn Marino     close_module (db);
390*86d7f5d3SJohn Marino     if (options)
391*86d7f5d3SJohn Marino     {
392*86d7f5d3SJohn Marino 	free (options);
393*86d7f5d3SJohn Marino 	options = NULL;
394*86d7f5d3SJohn Marino     }
395*86d7f5d3SJohn Marino     if (history_name != tag && history_name != date && history_name != NULL)
396*86d7f5d3SJohn Marino 	free (history_name);
397*86d7f5d3SJohn Marino     return err;
398*86d7f5d3SJohn Marino }
399*86d7f5d3SJohn Marino 
400*86d7f5d3SJohn Marino 
401*86d7f5d3SJohn Marino 
402*86d7f5d3SJohn Marino /* FIXME: This is and emptydir_name are in checkout.c for historical
403*86d7f5d3SJohn Marino    reasons, probably want to move them.  */
404*86d7f5d3SJohn Marino 
405*86d7f5d3SJohn Marino /* int
406*86d7f5d3SJohn Marino  * safe_location ( char *where )
407*86d7f5d3SJohn Marino  *
408*86d7f5d3SJohn Marino  * Return true if where is a safe destination for a checkout.
409*86d7f5d3SJohn Marino  *
410*86d7f5d3SJohn Marino  * INPUTS
411*86d7f5d3SJohn Marino  *  where	The requested destination directory.
412*86d7f5d3SJohn Marino  *
413*86d7f5d3SJohn Marino  * GLOBALS
414*86d7f5d3SJohn Marino  *  current_parsed_root->directory
415*86d7f5d3SJohn Marino  *  current_parsed_root->isremote
416*86d7f5d3SJohn Marino  *  		Used to locate our CVSROOT.
417*86d7f5d3SJohn Marino  *
418*86d7f5d3SJohn Marino  * RETURNS
419*86d7f5d3SJohn Marino  *  true	If we are running in client mode or if where is not located
420*86d7f5d3SJohn Marino  *  		within the CVSROOT.
421*86d7f5d3SJohn Marino  *  false	Otherwise.
422*86d7f5d3SJohn Marino  *
423*86d7f5d3SJohn Marino  * ERRORS
424*86d7f5d3SJohn Marino  *  Exits with a fatal error message when various events occur, such as not
425*86d7f5d3SJohn Marino  *  being able to resolve a path or failing ot chdir to a path.
426*86d7f5d3SJohn Marino  */
427*86d7f5d3SJohn Marino int
safe_location(char * where)428*86d7f5d3SJohn Marino safe_location (char *where)
429*86d7f5d3SJohn Marino {
430*86d7f5d3SJohn Marino     char *current;
431*86d7f5d3SJohn Marino     char *hardpath;
432*86d7f5d3SJohn Marino     size_t hardpath_len;
433*86d7f5d3SJohn Marino     int retval;
434*86d7f5d3SJohn Marino 
435*86d7f5d3SJohn Marino     TRACE (TRACE_FUNCTION, "safe_location( where=%s )",
436*86d7f5d3SJohn Marino            where ? where : "(null)");
437*86d7f5d3SJohn Marino 
438*86d7f5d3SJohn Marino     /* Don't compare remote CVSROOTs to our destination directory. */
439*86d7f5d3SJohn Marino     if (current_parsed_root->isremote) return 1;
440*86d7f5d3SJohn Marino 
441*86d7f5d3SJohn Marino     /* set current - even if where is set we'll need to cd back... */
442*86d7f5d3SJohn Marino     current = xgetcwd ();
443*86d7f5d3SJohn Marino     if (current == NULL)
444*86d7f5d3SJohn Marino 	error (1, errno, "could not get working directory");
445*86d7f5d3SJohn Marino 
446*86d7f5d3SJohn Marino     hardpath = xcanonicalize_file_name (current_parsed_root->directory);
447*86d7f5d3SJohn Marino 
448*86d7f5d3SJohn Marino     /* if where is set, set current to as much of where as exists,
449*86d7f5d3SJohn Marino      * or fail.
450*86d7f5d3SJohn Marino      */
451*86d7f5d3SJohn Marino     if (where != NULL)
452*86d7f5d3SJohn Marino     {
453*86d7f5d3SJohn Marino 	char *where_this_pass = xstrdup (where);
454*86d7f5d3SJohn Marino 	while (1)
455*86d7f5d3SJohn Marino 	{
456*86d7f5d3SJohn Marino 	    if (CVS_CHDIR (where_this_pass) != -1)
457*86d7f5d3SJohn Marino 	    {
458*86d7f5d3SJohn Marino 		/* where */
459*86d7f5d3SJohn Marino 		free (where_this_pass);
460*86d7f5d3SJohn Marino 		where_this_pass = xgetcwd ();
461*86d7f5d3SJohn Marino 		if (where_this_pass == NULL)
462*86d7f5d3SJohn Marino 		    error (1, errno, "could not get working directory");
463*86d7f5d3SJohn Marino 
464*86d7f5d3SJohn Marino 		if (CVS_CHDIR (current) == -1)
465*86d7f5d3SJohn Marino 		    error (1, errno,
466*86d7f5d3SJohn Marino 			   "could not restore directory to `%s'", current);
467*86d7f5d3SJohn Marino 
468*86d7f5d3SJohn Marino 		free (current);
469*86d7f5d3SJohn Marino 		current = where_this_pass;
470*86d7f5d3SJohn Marino 		break;
471*86d7f5d3SJohn Marino 	    }
472*86d7f5d3SJohn Marino 	    else if (errno == ENOENT)
473*86d7f5d3SJohn Marino 	    {
474*86d7f5d3SJohn Marino 		/* where_this_pass - last_component (where_this_pass) */
475*86d7f5d3SJohn Marino 		char *parent;
476*86d7f5d3SJohn Marino 
477*86d7f5d3SJohn Marino 		/* It's okay to cast out the const below since we know we
478*86d7f5d3SJohn Marino 		 * allocated where_this_pass and have control of it.
479*86d7f5d3SJohn Marino 		 */
480*86d7f5d3SJohn Marino 		if ((parent = (char *)last_component (where_this_pass))
481*86d7f5d3SJohn Marino 		        != where_this_pass)
482*86d7f5d3SJohn Marino 		{
483*86d7f5d3SJohn Marino 		    /* strip the last_component */
484*86d7f5d3SJohn Marino 		    parent[-1] = '\0';
485*86d7f5d3SJohn Marino 		    /* continue */
486*86d7f5d3SJohn Marino 		}
487*86d7f5d3SJohn Marino 		else
488*86d7f5d3SJohn Marino 		{
489*86d7f5d3SJohn Marino 		    /* ERRNO == ENOENT
490*86d7f5d3SJohn Marino 		     *   && last_component (where_this_pass) == where_this_pass
491*86d7f5d3SJohn Marino 		     * means we've tried all the parent diretories and not one
492*86d7f5d3SJohn Marino 		     * exists, so there is no need to test any portion of where
493*86d7f5d3SJohn Marino 		     * - it is all being created.
494*86d7f5d3SJohn Marino 		     */
495*86d7f5d3SJohn Marino 		    free (where_this_pass);
496*86d7f5d3SJohn Marino 		    break;
497*86d7f5d3SJohn Marino 		}
498*86d7f5d3SJohn Marino 	    }
499*86d7f5d3SJohn Marino 	    else
500*86d7f5d3SJohn Marino 		/* we don't know how to handle other errors, so fail */
501*86d7f5d3SJohn Marino 		error (1, errno, "\
502*86d7f5d3SJohn Marino could not change directory to requested checkout directory `%s'",
503*86d7f5d3SJohn Marino 		       where_this_pass);
504*86d7f5d3SJohn Marino 	} /* while (1) */
505*86d7f5d3SJohn Marino     } /* where != NULL */
506*86d7f5d3SJohn Marino 
507*86d7f5d3SJohn Marino     hardpath_len = strlen (hardpath);
508*86d7f5d3SJohn Marino     if (strlen (current) >= hardpath_len
509*86d7f5d3SJohn Marino 	&& strncmp (current, hardpath, hardpath_len) == 0)
510*86d7f5d3SJohn Marino     {
511*86d7f5d3SJohn Marino 	if (/* Current is a subdirectory of hardpath.  */
512*86d7f5d3SJohn Marino 	    current[hardpath_len] == '/'
513*86d7f5d3SJohn Marino 
514*86d7f5d3SJohn Marino 	    /* Current is hardpath itself.  */
515*86d7f5d3SJohn Marino 	    || current[hardpath_len] == '\0')
516*86d7f5d3SJohn Marino 	    retval = 0;
517*86d7f5d3SJohn Marino 	else
518*86d7f5d3SJohn Marino 	    /* It isn't a problem.  For example, current is
519*86d7f5d3SJohn Marino 	       "/foo/cvsroot-bar" and hardpath is "/foo/cvsroot".  */
520*86d7f5d3SJohn Marino 	    retval = 1;
521*86d7f5d3SJohn Marino     }
522*86d7f5d3SJohn Marino     else
523*86d7f5d3SJohn Marino 	retval = 1;
524*86d7f5d3SJohn Marino     free (current);
525*86d7f5d3SJohn Marino     free (hardpath);
526*86d7f5d3SJohn Marino     return retval;
527*86d7f5d3SJohn Marino }
528*86d7f5d3SJohn Marino 
529*86d7f5d3SJohn Marino 
530*86d7f5d3SJohn Marino 
531*86d7f5d3SJohn Marino struct dir_to_build
532*86d7f5d3SJohn Marino {
533*86d7f5d3SJohn Marino     /* What to put in CVS/Repository.  */
534*86d7f5d3SJohn Marino     char *repository;
535*86d7f5d3SJohn Marino     /* The path to the directory.  */
536*86d7f5d3SJohn Marino     char *dirpath;
537*86d7f5d3SJohn Marino 
538*86d7f5d3SJohn Marino     struct dir_to_build *next;
539*86d7f5d3SJohn Marino };
540*86d7f5d3SJohn Marino 
541*86d7f5d3SJohn Marino 
542*86d7f5d3SJohn Marino 
543*86d7f5d3SJohn Marino static int build_dirs_and_chdir (struct dir_to_build *list,
544*86d7f5d3SJohn Marino 					int sticky);
545*86d7f5d3SJohn Marino 
546*86d7f5d3SJohn Marino static void
build_one_dir(char * repository,char * dirpath,int sticky)547*86d7f5d3SJohn Marino build_one_dir (char *repository, char *dirpath, int sticky)
548*86d7f5d3SJohn Marino {
549*86d7f5d3SJohn Marino     FILE *fp;
550*86d7f5d3SJohn Marino 
551*86d7f5d3SJohn Marino     if (isfile (CVSADM))
552*86d7f5d3SJohn Marino     {
553*86d7f5d3SJohn Marino 	if (m_type == EXPORT)
554*86d7f5d3SJohn Marino 	    error (1, 0, "cannot export into a working directory");
555*86d7f5d3SJohn Marino     }
556*86d7f5d3SJohn Marino     else if (m_type == CHECKOUT)
557*86d7f5d3SJohn Marino     {
558*86d7f5d3SJohn Marino 	/* I suspect that this check could be omitted.  */
559*86d7f5d3SJohn Marino 	if (!isdir (repository))
560*86d7f5d3SJohn Marino 	    error (1, 0, "there is no repository %s", repository);
561*86d7f5d3SJohn Marino 
562*86d7f5d3SJohn Marino 	if (Create_Admin (".", dirpath, repository,
563*86d7f5d3SJohn Marino 			  sticky ? tag : NULL,
564*86d7f5d3SJohn Marino 			  sticky ? date : NULL,
565*86d7f5d3SJohn Marino 
566*86d7f5d3SJohn Marino 			  /* FIXME?  This is a guess.  If it is important
567*86d7f5d3SJohn Marino 			     for nonbranch to be set correctly here I
568*86d7f5d3SJohn Marino 			     think we need to write it one way now and
569*86d7f5d3SJohn Marino 			     then rewrite it later via WriteTag, once
570*86d7f5d3SJohn Marino 			     we've had a chance to call RCS_nodeisbranch
571*86d7f5d3SJohn Marino 			     on each file.  */
572*86d7f5d3SJohn Marino 			  0, 1, 1))
573*86d7f5d3SJohn Marino 	    return;
574*86d7f5d3SJohn Marino 
575*86d7f5d3SJohn Marino 	if (!noexec)
576*86d7f5d3SJohn Marino 	{
577*86d7f5d3SJohn Marino 	    fp = xfopen (CVSADM_ENTSTAT, "w+");
578*86d7f5d3SJohn Marino 	    if (fclose (fp) == EOF)
579*86d7f5d3SJohn Marino 		error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
580*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
581*86d7f5d3SJohn Marino 	    if (server_active)
582*86d7f5d3SJohn Marino 		server_set_entstat (dirpath, repository);
583*86d7f5d3SJohn Marino #endif
584*86d7f5d3SJohn Marino 	}
585*86d7f5d3SJohn Marino     }
586*86d7f5d3SJohn Marino }
587*86d7f5d3SJohn Marino 
588*86d7f5d3SJohn Marino 
589*86d7f5d3SJohn Marino 
590*86d7f5d3SJohn Marino /*
591*86d7f5d3SJohn Marino  * process_module calls us back here so we do the actual checkout stuff
592*86d7f5d3SJohn Marino  */
593*86d7f5d3SJohn Marino /* ARGSUSED */
594*86d7f5d3SJohn Marino static int
checkout_proc(int argc,char ** argv,char * where_orig,char * mwhere,char * mfile,int shorten,int local_specified,char * omodule,char * msg)595*86d7f5d3SJohn Marino checkout_proc (int argc, char **argv, char *where_orig, char *mwhere,
596*86d7f5d3SJohn Marino 	       char *mfile, int shorten, int local_specified, char *omodule,
597*86d7f5d3SJohn Marino 	       char *msg)
598*86d7f5d3SJohn Marino {
599*86d7f5d3SJohn Marino     char *myargv[2];
600*86d7f5d3SJohn Marino     int err = 0;
601*86d7f5d3SJohn Marino     int which;
602*86d7f5d3SJohn Marino     char *cp;
603*86d7f5d3SJohn Marino     char *repository;
604*86d7f5d3SJohn Marino     char *oldupdate = NULL;
605*86d7f5d3SJohn Marino     char *where;
606*86d7f5d3SJohn Marino 
607*86d7f5d3SJohn Marino     TRACE (TRACE_FUNCTION, "checkout_proc (%s, %s, %s, %d, %d, %s, %s)\n",
608*86d7f5d3SJohn Marino 	   where_orig ? where_orig : "(null)",
609*86d7f5d3SJohn Marino 	   mwhere ? mwhere : "(null)",
610*86d7f5d3SJohn Marino 	   mfile ? mfile : "(null)",
611*86d7f5d3SJohn Marino 	   shorten, local_specified,
612*86d7f5d3SJohn Marino 	   omodule ? omodule : "(null)",
613*86d7f5d3SJohn Marino 	   msg ? msg : "(null)"
614*86d7f5d3SJohn Marino 	  );
615*86d7f5d3SJohn Marino 
616*86d7f5d3SJohn Marino     /*
617*86d7f5d3SJohn Marino      * OK, so we're doing the checkout! Our args are as follows:
618*86d7f5d3SJohn Marino      *  argc,argv contain either dir or dir followed by a list of files
619*86d7f5d3SJohn Marino      *  where contains where to put it (if supplied by checkout)
620*86d7f5d3SJohn Marino      *  mwhere contains the module name or -d from module file
621*86d7f5d3SJohn Marino      *  mfile says do only that part of the module
622*86d7f5d3SJohn Marino      *  shorten = 1 says shorten as much as possible
623*86d7f5d3SJohn Marino      *  omodule is the original arg to do_module()
624*86d7f5d3SJohn Marino      */
625*86d7f5d3SJohn Marino 
626*86d7f5d3SJohn Marino     /* Set up the repository (maybe) for the bottom directory.
627*86d7f5d3SJohn Marino        Allocate more space than we need so we don't need to keep
628*86d7f5d3SJohn Marino        reallocating this string. */
629*86d7f5d3SJohn Marino     repository = xmalloc (strlen (current_parsed_root->directory)
630*86d7f5d3SJohn Marino 			  + strlen (argv[0])
631*86d7f5d3SJohn Marino 			  + (mfile == NULL ? 0 : strlen (mfile))
632*86d7f5d3SJohn Marino 			  + 10);
633*86d7f5d3SJohn Marino     (void) sprintf (repository, "%s/%s",
634*86d7f5d3SJohn Marino                     current_parsed_root->directory, argv[0]);
635*86d7f5d3SJohn Marino     Sanitize_Repository_Name (repository);
636*86d7f5d3SJohn Marino 
637*86d7f5d3SJohn Marino 
638*86d7f5d3SJohn Marino     /* save the original value of preload_update_dir */
639*86d7f5d3SJohn Marino     if (preload_update_dir != NULL)
640*86d7f5d3SJohn Marino 	oldupdate = xstrdup (preload_update_dir);
641*86d7f5d3SJohn Marino 
642*86d7f5d3SJohn Marino 
643*86d7f5d3SJohn Marino     /* Allocate space and set up the where variable.  We allocate more
644*86d7f5d3SJohn Marino        space than necessary here so that we don't have to keep
645*86d7f5d3SJohn Marino        reallocaing it later on. */
646*86d7f5d3SJohn Marino 
647*86d7f5d3SJohn Marino     where = xmalloc (strlen (argv[0])
648*86d7f5d3SJohn Marino 		     + (mfile == NULL ? 0 : strlen (mfile))
649*86d7f5d3SJohn Marino 		     + (mwhere == NULL ? 0 : strlen (mwhere))
650*86d7f5d3SJohn Marino 		     + (where_orig == NULL ? 0 : strlen (where_orig))
651*86d7f5d3SJohn Marino 		     + 10);
652*86d7f5d3SJohn Marino 
653*86d7f5d3SJohn Marino     /* Yes, this could be written in a less verbose way, but in this
654*86d7f5d3SJohn Marino        form it is quite easy to read.
655*86d7f5d3SJohn Marino 
656*86d7f5d3SJohn Marino        FIXME?  The following code that sets should probably be moved
657*86d7f5d3SJohn Marino        to do_module in modules.c, since there is similar code in
658*86d7f5d3SJohn Marino        patch.c and rtag.c. */
659*86d7f5d3SJohn Marino 
660*86d7f5d3SJohn Marino     if (shorten)
661*86d7f5d3SJohn Marino     {
662*86d7f5d3SJohn Marino 	if (where_orig != NULL)
663*86d7f5d3SJohn Marino 	{
664*86d7f5d3SJohn Marino 	    /* If the user has specified a directory with `-d' on the
665*86d7f5d3SJohn Marino 	       command line, use it preferentially, even over the `-d'
666*86d7f5d3SJohn Marino 	       flag in the modules file. */
667*86d7f5d3SJohn Marino 
668*86d7f5d3SJohn Marino 	    (void) strcpy (where, where_orig);
669*86d7f5d3SJohn Marino 	}
670*86d7f5d3SJohn Marino 	else if (mwhere != NULL)
671*86d7f5d3SJohn Marino 	{
672*86d7f5d3SJohn Marino 	    /* Second preference is the value of mwhere, which is from
673*86d7f5d3SJohn Marino 	       the `-d' flag in the modules file. */
674*86d7f5d3SJohn Marino 
675*86d7f5d3SJohn Marino 	    (void) strcpy (where, mwhere);
676*86d7f5d3SJohn Marino 	}
677*86d7f5d3SJohn Marino 	else
678*86d7f5d3SJohn Marino 	{
679*86d7f5d3SJohn Marino 	    /* Third preference is the directory specified in argv[0]
680*86d7f5d3SJohn Marino 	       which is this module'e directory in the repository. */
681*86d7f5d3SJohn Marino 
682*86d7f5d3SJohn Marino 	    (void) strcpy (where, argv[0]);
683*86d7f5d3SJohn Marino 	}
684*86d7f5d3SJohn Marino     }
685*86d7f5d3SJohn Marino     else
686*86d7f5d3SJohn Marino     {
687*86d7f5d3SJohn Marino 	/* Use the same preferences here, bug don't shorten -- that
688*86d7f5d3SJohn Marino            is, tack on where_orig if it exists. */
689*86d7f5d3SJohn Marino 
690*86d7f5d3SJohn Marino 	*where = '\0';
691*86d7f5d3SJohn Marino 
692*86d7f5d3SJohn Marino 	if (where_orig != NULL)
693*86d7f5d3SJohn Marino 	{
694*86d7f5d3SJohn Marino 	    (void) strcat (where, where_orig);
695*86d7f5d3SJohn Marino 	    (void) strcat (where, "/");
696*86d7f5d3SJohn Marino 	}
697*86d7f5d3SJohn Marino 
698*86d7f5d3SJohn Marino 	/* If the -d flag in the modules file specified an absolute
699*86d7f5d3SJohn Marino            directory, let the user override it with the command-line
700*86d7f5d3SJohn Marino            -d option. */
701*86d7f5d3SJohn Marino 
702*86d7f5d3SJohn Marino 	if (mwhere && !ISABSOLUTE (mwhere))
703*86d7f5d3SJohn Marino 	    (void) strcat (where, mwhere);
704*86d7f5d3SJohn Marino 	else
705*86d7f5d3SJohn Marino 	    (void) strcat (where, argv[0]);
706*86d7f5d3SJohn Marino     }
707*86d7f5d3SJohn Marino     strip_trailing_slashes (where); /* necessary? */
708*86d7f5d3SJohn Marino 
709*86d7f5d3SJohn Marino 
710*86d7f5d3SJohn Marino     /* At this point, the user may have asked for a single file or
711*86d7f5d3SJohn Marino        directory from within a module.  In that case, we should modify
712*86d7f5d3SJohn Marino        where, repository, and argv as appropriate. */
713*86d7f5d3SJohn Marino 
714*86d7f5d3SJohn Marino     if (mfile != NULL)
715*86d7f5d3SJohn Marino     {
716*86d7f5d3SJohn Marino 	/* The mfile variable can have one or more path elements.  If
717*86d7f5d3SJohn Marino 	   it has multiple elements, we want to tack those onto both
718*86d7f5d3SJohn Marino 	   repository and where.  The last element may refer to either
719*86d7f5d3SJohn Marino 	   a file or directory.  Here's what to do:
720*86d7f5d3SJohn Marino 
721*86d7f5d3SJohn Marino 	   it refers to a directory
722*86d7f5d3SJohn Marino 	     -> simply tack it on to where and repository
723*86d7f5d3SJohn Marino 	   it refers to a file
724*86d7f5d3SJohn Marino 	     -> munge argv to contain `basename mfile` */
725*86d7f5d3SJohn Marino 
726*86d7f5d3SJohn Marino 	char *cp;
727*86d7f5d3SJohn Marino 	char *path;
728*86d7f5d3SJohn Marino 
729*86d7f5d3SJohn Marino 
730*86d7f5d3SJohn Marino 	/* Paranoia check. */
731*86d7f5d3SJohn Marino 
732*86d7f5d3SJohn Marino 	if (mfile[strlen (mfile) - 1] == '/')
733*86d7f5d3SJohn Marino 	{
734*86d7f5d3SJohn Marino 	    error (0, 0, "checkout_proc: trailing slash on mfile (%s)!",
735*86d7f5d3SJohn Marino 		   mfile);
736*86d7f5d3SJohn Marino 	}
737*86d7f5d3SJohn Marino 
738*86d7f5d3SJohn Marino 
739*86d7f5d3SJohn Marino 	/* Does mfile have multiple path elements? */
740*86d7f5d3SJohn Marino 
741*86d7f5d3SJohn Marino 	cp = strrchr (mfile, '/');
742*86d7f5d3SJohn Marino 	if (cp != NULL)
743*86d7f5d3SJohn Marino 	{
744*86d7f5d3SJohn Marino 	    *cp = '\0';
745*86d7f5d3SJohn Marino 	    (void) strcat (repository, "/");
746*86d7f5d3SJohn Marino 	    (void) strcat (repository, mfile);
747*86d7f5d3SJohn Marino 	    (void) strcat (where, "/");
748*86d7f5d3SJohn Marino 	    (void) strcat (where, mfile);
749*86d7f5d3SJohn Marino 	    mfile = cp + 1;
750*86d7f5d3SJohn Marino 	}
751*86d7f5d3SJohn Marino 
752*86d7f5d3SJohn Marino 
753*86d7f5d3SJohn Marino 	/* Now mfile is a single path element. */
754*86d7f5d3SJohn Marino 
755*86d7f5d3SJohn Marino 	path = Xasprintf ("%s/%s", repository, mfile);
756*86d7f5d3SJohn Marino 	if (isdir (path))
757*86d7f5d3SJohn Marino 	{
758*86d7f5d3SJohn Marino 	    /* It's a directory, so tack it on to repository and
759*86d7f5d3SJohn Marino                where, as we did above. */
760*86d7f5d3SJohn Marino 
761*86d7f5d3SJohn Marino 	    (void) strcat (repository, "/");
762*86d7f5d3SJohn Marino 	    (void) strcat (repository, mfile);
763*86d7f5d3SJohn Marino 	    (void) strcat (where, "/");
764*86d7f5d3SJohn Marino 	    (void) strcat (where, mfile);
765*86d7f5d3SJohn Marino 	}
766*86d7f5d3SJohn Marino 	else
767*86d7f5d3SJohn Marino 	{
768*86d7f5d3SJohn Marino 	    /* It's a file, which means we have to screw around with
769*86d7f5d3SJohn Marino                argv. */
770*86d7f5d3SJohn Marino 	    myargv[0] = argv[0];
771*86d7f5d3SJohn Marino 	    myargv[1] = mfile;
772*86d7f5d3SJohn Marino 	    argc = 2;
773*86d7f5d3SJohn Marino 	    argv = myargv;
774*86d7f5d3SJohn Marino 	}
775*86d7f5d3SJohn Marino 	free (path);
776*86d7f5d3SJohn Marino     }
777*86d7f5d3SJohn Marino 
778*86d7f5d3SJohn Marino     if (preload_update_dir != NULL)
779*86d7f5d3SJohn Marino     {
780*86d7f5d3SJohn Marino 	preload_update_dir =
781*86d7f5d3SJohn Marino 	    xrealloc (preload_update_dir,
782*86d7f5d3SJohn Marino 		      strlen (preload_update_dir) + strlen (where) + 5);
783*86d7f5d3SJohn Marino 	strcat (preload_update_dir, "/");
784*86d7f5d3SJohn Marino 	strcat (preload_update_dir, where);
785*86d7f5d3SJohn Marino     }
786*86d7f5d3SJohn Marino     else
787*86d7f5d3SJohn Marino 	preload_update_dir = xstrdup (where);
788*86d7f5d3SJohn Marino 
789*86d7f5d3SJohn Marino     /*
790*86d7f5d3SJohn Marino      * At this point, where is the directory we want to build, repository is
791*86d7f5d3SJohn Marino      * the repository for the lowest level of the path.
792*86d7f5d3SJohn Marino      *
793*86d7f5d3SJohn Marino      * We need to tell build_dirs not only the path we want it to
794*86d7f5d3SJohn Marino      * build, but also the repositories we want it to populate the
795*86d7f5d3SJohn Marino      * path with.  To accomplish this, we walk the path backwards, one
796*86d7f5d3SJohn Marino      * pathname component at a time, constucting a linked list of
797*86d7f5d3SJohn Marino      * struct dir_to_build.
798*86d7f5d3SJohn Marino      */
799*86d7f5d3SJohn Marino 
800*86d7f5d3SJohn Marino     /*
801*86d7f5d3SJohn Marino      * If we are sending everything to stdout, we can skip a whole bunch of
802*86d7f5d3SJohn Marino      * work from here
803*86d7f5d3SJohn Marino      */
804*86d7f5d3SJohn Marino     if (!pipeout)
805*86d7f5d3SJohn Marino     {
806*86d7f5d3SJohn Marino 	struct dir_to_build *head;
807*86d7f5d3SJohn Marino 	char *reposcopy;
808*86d7f5d3SJohn Marino 
809*86d7f5d3SJohn Marino 	if (strncmp (repository, current_parsed_root->directory,
810*86d7f5d3SJohn Marino 		     strlen (current_parsed_root->directory)) != 0)
811*86d7f5d3SJohn Marino 	    error (1, 0, "\
812*86d7f5d3SJohn Marino internal error: %s doesn't start with %s in checkout_proc",
813*86d7f5d3SJohn Marino 		   repository, current_parsed_root->directory);
814*86d7f5d3SJohn Marino 
815*86d7f5d3SJohn Marino 	/* We always create at least one directory, which corresponds to
816*86d7f5d3SJohn Marino 	   the entire strings for WHERE and REPOSITORY.  */
817*86d7f5d3SJohn Marino 	head = xmalloc (sizeof (struct dir_to_build));
818*86d7f5d3SJohn Marino 	/* Special marker to indicate that we don't want build_dirs_and_chdir
819*86d7f5d3SJohn Marino 	   to create the CVSADM directory for us.  */
820*86d7f5d3SJohn Marino 	head->repository = NULL;
821*86d7f5d3SJohn Marino 	head->dirpath = xstrdup (where);
822*86d7f5d3SJohn Marino 	head->next = NULL;
823*86d7f5d3SJohn Marino 
824*86d7f5d3SJohn Marino 	/* Make a copy of the repository name to play with. */
825*86d7f5d3SJohn Marino 	reposcopy = xstrdup (repository);
826*86d7f5d3SJohn Marino 
827*86d7f5d3SJohn Marino 	/* FIXME: this should be written in terms of last_component
828*86d7f5d3SJohn Marino 	   instead of hardcoding '/'.  This presumably affects OS/2,
829*86d7f5d3SJohn Marino 	   NT, &c, if the user specifies '\'.  Likewise for the call
830*86d7f5d3SJohn Marino 	   to findslash.  */
831*86d7f5d3SJohn Marino 	cp = where + strlen (where);
832*86d7f5d3SJohn Marino 	while (cp > where)
833*86d7f5d3SJohn Marino 	{
834*86d7f5d3SJohn Marino 	    struct dir_to_build *new;
835*86d7f5d3SJohn Marino 
836*86d7f5d3SJohn Marino 	    cp = findslash (where, cp - 1);
837*86d7f5d3SJohn Marino 	    if (cp == NULL)
838*86d7f5d3SJohn Marino 		break;		/* we're done */
839*86d7f5d3SJohn Marino 
840*86d7f5d3SJohn Marino 	    new = xmalloc (sizeof (struct dir_to_build));
841*86d7f5d3SJohn Marino 	    new->dirpath = xmalloc (strlen (where));
842*86d7f5d3SJohn Marino 
843*86d7f5d3SJohn Marino 	    /* If the user specified an absolute path for where, the
844*86d7f5d3SJohn Marino                last path element we create should be the top-level
845*86d7f5d3SJohn Marino                directory. */
846*86d7f5d3SJohn Marino 
847*86d7f5d3SJohn Marino 	    if (cp > where)
848*86d7f5d3SJohn Marino 	    {
849*86d7f5d3SJohn Marino 		strncpy (new->dirpath, where, cp - where);
850*86d7f5d3SJohn Marino 		new->dirpath[cp - where] = '\0';
851*86d7f5d3SJohn Marino 	    }
852*86d7f5d3SJohn Marino 	    else
853*86d7f5d3SJohn Marino 	    {
854*86d7f5d3SJohn Marino 		/* where should always be at least one character long. */
855*86d7f5d3SJohn Marino 		assert (where[0] != '\0');
856*86d7f5d3SJohn Marino 		strcpy (new->dirpath, "/");
857*86d7f5d3SJohn Marino 	    }
858*86d7f5d3SJohn Marino 	    new->next = head;
859*86d7f5d3SJohn Marino 	    head = new;
860*86d7f5d3SJohn Marino 
861*86d7f5d3SJohn Marino 	    /* Now figure out what repository directory to generate.
862*86d7f5d3SJohn Marino                The most complete case would be something like this:
863*86d7f5d3SJohn Marino 
864*86d7f5d3SJohn Marino 	       The modules file contains
865*86d7f5d3SJohn Marino 	         foo -d bar/baz quux
866*86d7f5d3SJohn Marino 
867*86d7f5d3SJohn Marino 	       The command issued was:
868*86d7f5d3SJohn Marino 	         cvs co -d what/ever -N foo
869*86d7f5d3SJohn Marino 
870*86d7f5d3SJohn Marino 	       The results in the CVS/Repository files should be:
871*86d7f5d3SJohn Marino 	         .     -> (don't touch CVS/Repository)
872*86d7f5d3SJohn Marino 			  (I think this case might be buggy currently)
873*86d7f5d3SJohn Marino 		 what  -> (don't touch CVS/Repository)
874*86d7f5d3SJohn Marino 		 ever  -> .          (same as "cd what/ever; cvs co -N foo")
875*86d7f5d3SJohn Marino 		 bar   -> Emptydir   (generated dir -- not in repos)
876*86d7f5d3SJohn Marino 		 baz   -> quux       (finally!) */
877*86d7f5d3SJohn Marino 
878*86d7f5d3SJohn Marino 	    if (strcmp (reposcopy, current_parsed_root->directory) == 0)
879*86d7f5d3SJohn Marino 	    {
880*86d7f5d3SJohn Marino 		/* We can't walk up past CVSROOT.  Instead, the
881*86d7f5d3SJohn Marino                    repository should be Emptydir. */
882*86d7f5d3SJohn Marino 		new->repository = emptydir_name ();
883*86d7f5d3SJohn Marino 	    }
884*86d7f5d3SJohn Marino 	    else
885*86d7f5d3SJohn Marino 	    {
886*86d7f5d3SJohn Marino 		/* It's a directory in the repository! */
887*86d7f5d3SJohn Marino 
888*86d7f5d3SJohn Marino 		char *rp;
889*86d7f5d3SJohn Marino 
890*86d7f5d3SJohn Marino 		/* We'll always be below CVSROOT, but check for
891*86d7f5d3SJohn Marino 		   paranoia's sake. */
892*86d7f5d3SJohn Marino 		rp = strrchr (reposcopy, '/');
893*86d7f5d3SJohn Marino 		if (rp == NULL)
894*86d7f5d3SJohn Marino 		    error (1, 0,
895*86d7f5d3SJohn Marino 			   "internal error: %s doesn't contain a slash",
896*86d7f5d3SJohn Marino 			   reposcopy);
897*86d7f5d3SJohn Marino 
898*86d7f5d3SJohn Marino 		*rp = '\0';
899*86d7f5d3SJohn Marino 
900*86d7f5d3SJohn Marino 		if (strcmp (reposcopy, current_parsed_root->directory) == 0)
901*86d7f5d3SJohn Marino 		{
902*86d7f5d3SJohn Marino 		    /* Special case -- the repository name needs
903*86d7f5d3SJohn Marino 		       to be "/path/to/repos/." (the trailing dot
904*86d7f5d3SJohn Marino 		       is important).  We might be able to get rid
905*86d7f5d3SJohn Marino 		       of this after the we check out the other
906*86d7f5d3SJohn Marino 		       code that handles repository names. */
907*86d7f5d3SJohn Marino 		    new-> repository = Xasprintf ("%s/.", reposcopy);
908*86d7f5d3SJohn Marino 		}
909*86d7f5d3SJohn Marino 		else
910*86d7f5d3SJohn Marino 		    new->repository = xstrdup (reposcopy);
911*86d7f5d3SJohn Marino 	    }
912*86d7f5d3SJohn Marino 	}
913*86d7f5d3SJohn Marino 
914*86d7f5d3SJohn Marino 	/* clean up */
915*86d7f5d3SJohn Marino 	free (reposcopy);
916*86d7f5d3SJohn Marino 
917*86d7f5d3SJohn Marino 	/* The top-level CVSADM directory should always be
918*86d7f5d3SJohn Marino 	   current_parsed_root->directory.  Create it, but only if WHERE is
919*86d7f5d3SJohn Marino 	   relative.  If WHERE is absolute, our current directory
920*86d7f5d3SJohn Marino 	   may not have a thing to do with where the sources are
921*86d7f5d3SJohn Marino 	   being checked out.  If it does, build_dirs_and_chdir
922*86d7f5d3SJohn Marino 	   will take care of creating adm files here. */
923*86d7f5d3SJohn Marino 	/* FIXME: checking where_is_absolute is a horrid kludge;
924*86d7f5d3SJohn Marino 	   I suspect we probably can just skip the call to
925*86d7f5d3SJohn Marino 	   build_one_dir whenever the -d command option was specified
926*86d7f5d3SJohn Marino 	   to checkout.  */
927*86d7f5d3SJohn Marino 
928*86d7f5d3SJohn Marino 	if (!ISABSOLUTE (where) && config->top_level_admin
929*86d7f5d3SJohn Marino 	    && m_type == CHECKOUT)
930*86d7f5d3SJohn Marino 	{
931*86d7f5d3SJohn Marino 	    /* It may be argued that we shouldn't set any sticky
932*86d7f5d3SJohn Marino 	       bits for the top-level repository.  FIXME?  */
933*86d7f5d3SJohn Marino 	    build_one_dir (current_parsed_root->directory, ".", argc <= 1);
934*86d7f5d3SJohn Marino 
935*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
936*86d7f5d3SJohn Marino 	    /* We _always_ want to have a top-level admin
937*86d7f5d3SJohn Marino 	       directory.  If we're running in client/server mode,
938*86d7f5d3SJohn Marino 	       send a "Clear-static-directory" command to make
939*86d7f5d3SJohn Marino 	       sure it is created on the client side.  (See 5.10
940*86d7f5d3SJohn Marino 	       in cvsclient.dvi to convince yourself that this is
941*86d7f5d3SJohn Marino 	       OK.)  If this is a duplicate command being sent, it
942*86d7f5d3SJohn Marino 	       will be ignored on the client side.  */
943*86d7f5d3SJohn Marino 
944*86d7f5d3SJohn Marino 	    if (server_active)
945*86d7f5d3SJohn Marino 		server_clear_entstat (".", current_parsed_root->directory);
946*86d7f5d3SJohn Marino #endif
947*86d7f5d3SJohn Marino 	}
948*86d7f5d3SJohn Marino 
949*86d7f5d3SJohn Marino 
950*86d7f5d3SJohn Marino 	/* Build dirs on the path if necessary and leave us in the
951*86d7f5d3SJohn Marino 	   bottom directory (where if where was specified) doesn't
952*86d7f5d3SJohn Marino 	   contain a CVS subdir yet, but all the others contain
953*86d7f5d3SJohn Marino 	   CVS and Entries.Static files */
954*86d7f5d3SJohn Marino 
955*86d7f5d3SJohn Marino 	if (build_dirs_and_chdir (head, argc <= 1) != 0)
956*86d7f5d3SJohn Marino 	{
957*86d7f5d3SJohn Marino 	    error (0, 0, "ignoring module %s", omodule);
958*86d7f5d3SJohn Marino 	    err = 1;
959*86d7f5d3SJohn Marino 	    goto out;
960*86d7f5d3SJohn Marino 	}
961*86d7f5d3SJohn Marino 
962*86d7f5d3SJohn Marino 	/* set up the repository (or make sure the old one matches) */
963*86d7f5d3SJohn Marino 	if (!isfile (CVSADM))
964*86d7f5d3SJohn Marino 	{
965*86d7f5d3SJohn Marino 	    FILE *fp;
966*86d7f5d3SJohn Marino 
967*86d7f5d3SJohn Marino 	    if (!noexec && argc > 1)
968*86d7f5d3SJohn Marino 	    {
969*86d7f5d3SJohn Marino 		/* I'm not sure whether this check is redundant.  */
970*86d7f5d3SJohn Marino 		if (!isdir (repository))
971*86d7f5d3SJohn Marino 		    error (1, 0, "there is no repository %s", repository);
972*86d7f5d3SJohn Marino 
973*86d7f5d3SJohn Marino 		Create_Admin (".", preload_update_dir, repository,
974*86d7f5d3SJohn Marino 			      NULL, NULL, 0, 0, m_type == CHECKOUT);
975*86d7f5d3SJohn Marino 		fp = xfopen (CVSADM_ENTSTAT, "w+");
976*86d7f5d3SJohn Marino 		if (fclose (fp) == EOF)
977*86d7f5d3SJohn Marino 		    error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
978*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
979*86d7f5d3SJohn Marino 		if (server_active)
980*86d7f5d3SJohn Marino 		    server_set_entstat (where, repository);
981*86d7f5d3SJohn Marino #endif
982*86d7f5d3SJohn Marino 	    }
983*86d7f5d3SJohn Marino 	    else
984*86d7f5d3SJohn Marino 	    {
985*86d7f5d3SJohn Marino 		/* I'm not sure whether this check is redundant.  */
986*86d7f5d3SJohn Marino 		if (!isdir (repository))
987*86d7f5d3SJohn Marino 		    error (1, 0, "there is no repository %s", repository);
988*86d7f5d3SJohn Marino 
989*86d7f5d3SJohn Marino 		Create_Admin (".", preload_update_dir, repository, tag, date,
990*86d7f5d3SJohn Marino 
991*86d7f5d3SJohn Marino 			      /* FIXME?  This is a guess.  If it is important
992*86d7f5d3SJohn Marino 				 for nonbranch to be set correctly here I
993*86d7f5d3SJohn Marino 				 think we need to write it one way now and
994*86d7f5d3SJohn Marino 				 then rewrite it later via WriteTag, once
995*86d7f5d3SJohn Marino 				 we've had a chance to call RCS_nodeisbranch
996*86d7f5d3SJohn Marino 				 on each file.  */
997*86d7f5d3SJohn Marino 			      0, 0, m_type == CHECKOUT);
998*86d7f5d3SJohn Marino 	    }
999*86d7f5d3SJohn Marino 	}
1000*86d7f5d3SJohn Marino 	else
1001*86d7f5d3SJohn Marino 	{
1002*86d7f5d3SJohn Marino 	    char *repos;
1003*86d7f5d3SJohn Marino 
1004*86d7f5d3SJohn Marino 	    if (m_type == EXPORT)
1005*86d7f5d3SJohn Marino 		error (1, 0, "cannot export into working directory");
1006*86d7f5d3SJohn Marino 
1007*86d7f5d3SJohn Marino 	    /* get the contents of the previously existing repository */
1008*86d7f5d3SJohn Marino 	    repos = Name_Repository (NULL, preload_update_dir);
1009*86d7f5d3SJohn Marino 	    if (fncmp (repository, repos) != 0)
1010*86d7f5d3SJohn Marino 	    {
1011*86d7f5d3SJohn Marino 		char *prepos = xstrdup (primary_root_inverse_translate (repos));
1012*86d7f5d3SJohn Marino 		char *prepository =
1013*86d7f5d3SJohn Marino 		    xstrdup (primary_root_inverse_translate (repository));
1014*86d7f5d3SJohn Marino 		error (0, 0, "existing repository %s does not match %s",
1015*86d7f5d3SJohn Marino 		       prepos, prepository);
1016*86d7f5d3SJohn Marino 		error (0, 0, "ignoring module %s", omodule);
1017*86d7f5d3SJohn Marino 		free (repos);
1018*86d7f5d3SJohn Marino 		free (prepos);
1019*86d7f5d3SJohn Marino 		free (prepository);
1020*86d7f5d3SJohn Marino 		err = 1;
1021*86d7f5d3SJohn Marino 		goto out;
1022*86d7f5d3SJohn Marino 	    }
1023*86d7f5d3SJohn Marino 	    free (repos);
1024*86d7f5d3SJohn Marino 	}
1025*86d7f5d3SJohn Marino     }
1026*86d7f5d3SJohn Marino 
1027*86d7f5d3SJohn Marino     /*
1028*86d7f5d3SJohn Marino      * If we are going to be updating to stdout, we need to cd to the
1029*86d7f5d3SJohn Marino      * repository directory so the recursion processor can use the current
1030*86d7f5d3SJohn Marino      * directory as the place to find repository information
1031*86d7f5d3SJohn Marino      */
1032*86d7f5d3SJohn Marino     if (pipeout)
1033*86d7f5d3SJohn Marino     {
1034*86d7f5d3SJohn Marino 	if (CVS_CHDIR (repository) < 0)
1035*86d7f5d3SJohn Marino 	{
1036*86d7f5d3SJohn Marino 	    error (0, errno, "cannot chdir to %s", repository);
1037*86d7f5d3SJohn Marino 	    err = 1;
1038*86d7f5d3SJohn Marino 	    goto out;
1039*86d7f5d3SJohn Marino 	}
1040*86d7f5d3SJohn Marino 	which = W_REPOS;
1041*86d7f5d3SJohn Marino 	if (tag && !tag_validated)
1042*86d7f5d3SJohn Marino 	{
1043*86d7f5d3SJohn Marino 	    tag_check_valid (tag, argc - 1, argv + 1, 0, aflag,
1044*86d7f5d3SJohn Marino 			     repository, false);
1045*86d7f5d3SJohn Marino 	    tag_validated = true;
1046*86d7f5d3SJohn Marino 	}
1047*86d7f5d3SJohn Marino     }
1048*86d7f5d3SJohn Marino     else
1049*86d7f5d3SJohn Marino     {
1050*86d7f5d3SJohn Marino 	which = W_LOCAL | W_REPOS;
1051*86d7f5d3SJohn Marino 	if (tag && !tag_validated)
1052*86d7f5d3SJohn Marino 	{
1053*86d7f5d3SJohn Marino 	    tag_check_valid (tag, argc - 1, argv + 1, 0, aflag,
1054*86d7f5d3SJohn Marino 			     repository, false);
1055*86d7f5d3SJohn Marino 	    tag_validated = true;
1056*86d7f5d3SJohn Marino 	}
1057*86d7f5d3SJohn Marino     }
1058*86d7f5d3SJohn Marino 
1059*86d7f5d3SJohn Marino     if (tag || date || join_rev1 || join_date2)
1060*86d7f5d3SJohn Marino 	which |= W_ATTIC;
1061*86d7f5d3SJohn Marino 
1062*86d7f5d3SJohn Marino     if (!join_tags_validated)
1063*86d7f5d3SJohn Marino     {
1064*86d7f5d3SJohn Marino         if (join_rev1)
1065*86d7f5d3SJohn Marino 	    tag_check_valid (join_rev1, argc - 1, argv + 1, 0, aflag,
1066*86d7f5d3SJohn Marino 			     repository, false);
1067*86d7f5d3SJohn Marino 	if (join_rev2)
1068*86d7f5d3SJohn Marino 	    tag_check_valid (join_rev2, argc - 1, argv + 1, 0, aflag,
1069*86d7f5d3SJohn Marino 			     repository, false);
1070*86d7f5d3SJohn Marino 	join_tags_validated = true;
1071*86d7f5d3SJohn Marino     }
1072*86d7f5d3SJohn Marino 
1073*86d7f5d3SJohn Marino     /*
1074*86d7f5d3SJohn Marino      * if we are going to be recursive (building dirs), go ahead and call the
1075*86d7f5d3SJohn Marino      * update recursion processor.  We will be recursive unless either local
1076*86d7f5d3SJohn Marino      * only was specified, or we were passed arguments
1077*86d7f5d3SJohn Marino      */
1078*86d7f5d3SJohn Marino     if (!(local_specified || argc > 1))
1079*86d7f5d3SJohn Marino     {
1080*86d7f5d3SJohn Marino 	if (!pipeout)
1081*86d7f5d3SJohn Marino 	    history_write (m_type == CHECKOUT ? 'O' : 'E', preload_update_dir,
1082*86d7f5d3SJohn Marino 			   history_name, where, repository);
1083*86d7f5d3SJohn Marino 	err += do_update (0, NULL, options, tag, date,
1084*86d7f5d3SJohn Marino 			  force_tag_match, false /* !local */ ,
1085*86d7f5d3SJohn Marino 			  true /* update -d */ , aflag, checkout_prune_dirs,
1086*86d7f5d3SJohn Marino 			  pipeout, which, join_rev1, join_date1,
1087*86d7f5d3SJohn Marino 			  join_rev2, join_date2,
1088*86d7f5d3SJohn Marino 			  preload_update_dir, m_type == CHECKOUT,
1089*86d7f5d3SJohn Marino 			  repository);
1090*86d7f5d3SJohn Marino 	goto out;
1091*86d7f5d3SJohn Marino     }
1092*86d7f5d3SJohn Marino 
1093*86d7f5d3SJohn Marino     if (!pipeout)
1094*86d7f5d3SJohn Marino     {
1095*86d7f5d3SJohn Marino 	int i;
1096*86d7f5d3SJohn Marino 	List *entries;
1097*86d7f5d3SJohn Marino 
1098*86d7f5d3SJohn Marino 	/* we are only doing files, so register them */
1099*86d7f5d3SJohn Marino 	entries = Entries_Open (0, NULL);
1100*86d7f5d3SJohn Marino 	for (i = 1; i < argc; i++)
1101*86d7f5d3SJohn Marino 	{
1102*86d7f5d3SJohn Marino 	    char *line;
1103*86d7f5d3SJohn Marino 	    Vers_TS *vers;
1104*86d7f5d3SJohn Marino 	    struct file_info finfo;
1105*86d7f5d3SJohn Marino 
1106*86d7f5d3SJohn Marino 	    memset (&finfo, 0, sizeof finfo);
1107*86d7f5d3SJohn Marino 	    finfo.file = argv[i];
1108*86d7f5d3SJohn Marino 	    /* Shouldn't be used, so set to arbitrary value.  */
1109*86d7f5d3SJohn Marino 	    finfo.update_dir = NULL;
1110*86d7f5d3SJohn Marino 	    finfo.fullname = argv[i];
1111*86d7f5d3SJohn Marino 	    finfo.repository = repository;
1112*86d7f5d3SJohn Marino 	    finfo.entries = entries;
1113*86d7f5d3SJohn Marino 	    /* The rcs slot is needed to get the options from the RCS
1114*86d7f5d3SJohn Marino                file */
1115*86d7f5d3SJohn Marino 	    finfo.rcs = RCS_parse (finfo.file, repository);
1116*86d7f5d3SJohn Marino 
1117*86d7f5d3SJohn Marino 	    vers = Version_TS (&finfo, options, tag, date,
1118*86d7f5d3SJohn Marino 			       force_tag_match, 0);
1119*86d7f5d3SJohn Marino 	    if (vers->ts_user == NULL)
1120*86d7f5d3SJohn Marino 	    {
1121*86d7f5d3SJohn Marino 		line = Xasprintf ("Initial %s", finfo.file);
1122*86d7f5d3SJohn Marino 		Register (entries, finfo.file,
1123*86d7f5d3SJohn Marino 			  vers->vn_rcs ? vers->vn_rcs : "0",
1124*86d7f5d3SJohn Marino 			  line, vers->options, vers->tag,
1125*86d7f5d3SJohn Marino 			  vers->date, NULL);
1126*86d7f5d3SJohn Marino 		free (line);
1127*86d7f5d3SJohn Marino 	    }
1128*86d7f5d3SJohn Marino 	    freevers_ts (&vers);
1129*86d7f5d3SJohn Marino 	    freercsnode (&finfo.rcs);
1130*86d7f5d3SJohn Marino 	}
1131*86d7f5d3SJohn Marino 
1132*86d7f5d3SJohn Marino 	Entries_Close (entries);
1133*86d7f5d3SJohn Marino     }
1134*86d7f5d3SJohn Marino 
1135*86d7f5d3SJohn Marino     /* Don't log "export", just regular "checkouts" */
1136*86d7f5d3SJohn Marino     if (m_type == CHECKOUT && !pipeout)
1137*86d7f5d3SJohn Marino 	history_write ('O', preload_update_dir, history_name, where,
1138*86d7f5d3SJohn Marino 		       repository);
1139*86d7f5d3SJohn Marino 
1140*86d7f5d3SJohn Marino     /* go ahead and call update now that everything is set */
1141*86d7f5d3SJohn Marino     err += do_update (argc - 1, argv + 1, options, tag, date,
1142*86d7f5d3SJohn Marino 		      force_tag_match, local_specified, true /* update -d */,
1143*86d7f5d3SJohn Marino 		      aflag, checkout_prune_dirs, pipeout, which, join_rev1,
1144*86d7f5d3SJohn Marino 		      join_date1, join_rev2, join_date2, preload_update_dir,
1145*86d7f5d3SJohn Marino 		      m_type == CHECKOUT, repository);
1146*86d7f5d3SJohn Marino out:
1147*86d7f5d3SJohn Marino     free (preload_update_dir);
1148*86d7f5d3SJohn Marino     preload_update_dir = oldupdate;
1149*86d7f5d3SJohn Marino     free (where);
1150*86d7f5d3SJohn Marino     free (repository);
1151*86d7f5d3SJohn Marino     return err;
1152*86d7f5d3SJohn Marino }
1153*86d7f5d3SJohn Marino 
1154*86d7f5d3SJohn Marino 
1155*86d7f5d3SJohn Marino 
1156*86d7f5d3SJohn Marino static char *
findslash(char * start,char * p)1157*86d7f5d3SJohn Marino findslash (char *start, char *p)
1158*86d7f5d3SJohn Marino {
1159*86d7f5d3SJohn Marino     for (;;)
1160*86d7f5d3SJohn Marino     {
1161*86d7f5d3SJohn Marino 	if (*p == '/') return p;
1162*86d7f5d3SJohn Marino 	if (p == start) break;
1163*86d7f5d3SJohn Marino 	--p;
1164*86d7f5d3SJohn Marino     }
1165*86d7f5d3SJohn Marino     return NULL;
1166*86d7f5d3SJohn Marino }
1167*86d7f5d3SJohn Marino 
1168*86d7f5d3SJohn Marino 
1169*86d7f5d3SJohn Marino 
1170*86d7f5d3SJohn Marino /* Return a newly malloc'd string containing a pathname for CVSNULLREPOS,
1171*86d7f5d3SJohn Marino    and make sure that it exists.  If there is an error creating the
1172*86d7f5d3SJohn Marino    directory, give a fatal error.  Otherwise, the directory is guaranteed
1173*86d7f5d3SJohn Marino    to exist when we return.  */
1174*86d7f5d3SJohn Marino char *
emptydir_name(void)1175*86d7f5d3SJohn Marino emptydir_name (void)
1176*86d7f5d3SJohn Marino {
1177*86d7f5d3SJohn Marino     char *repository;
1178*86d7f5d3SJohn Marino 
1179*86d7f5d3SJohn Marino     repository = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
1180*86d7f5d3SJohn Marino 			    CVSROOTADM, CVSNULLREPOS);
1181*86d7f5d3SJohn Marino     if (!isfile (repository))
1182*86d7f5d3SJohn Marino     {
1183*86d7f5d3SJohn Marino 	mode_t omask;
1184*86d7f5d3SJohn Marino 	omask = umask (cvsumask);
1185*86d7f5d3SJohn Marino 	if (CVS_MKDIR (repository, 0777) < 0)
1186*86d7f5d3SJohn Marino 	    error (1, errno, "cannot create %s", repository);
1187*86d7f5d3SJohn Marino 	(void) umask (omask);
1188*86d7f5d3SJohn Marino     }
1189*86d7f5d3SJohn Marino     return repository;
1190*86d7f5d3SJohn Marino }
1191*86d7f5d3SJohn Marino 
1192*86d7f5d3SJohn Marino 
1193*86d7f5d3SJohn Marino 
1194*86d7f5d3SJohn Marino /* Build all the dirs along the path to DIRS with CVS subdirs with appropriate
1195*86d7f5d3SJohn Marino  * repositories.  If DIRS->repository is NULL or the directory already exists,
1196*86d7f5d3SJohn Marino  * do not create a CVSADM directory for that subdirectory; just CVS_CHDIR into
1197*86d7f5d3SJohn Marino  * it.  Frees all storage used by DIRS.
1198*86d7f5d3SJohn Marino  *
1199*86d7f5d3SJohn Marino  * ASSUMPTIONS
1200*86d7f5d3SJohn Marino  *   1. Parent directories will be listed in DIRS before their children.
1201*86d7f5d3SJohn Marino  *   2. At most a single directory will need to be changed at one time.  In
1202*86d7f5d3SJohn Marino  *      other words, if we are in /a/b/c, and our final destination is
1203*86d7f5d3SJohn Marino  *      /a/b/c/d/e/f, then we will build d, then d/e, then d/e/f.
1204*86d7f5d3SJohn Marino  *
1205*86d7f5d3SJohn Marino  * INPUTS
1206*86d7f5d3SJohn Marino  *   dirs	Simple list composed of dir_to_build structures, listing
1207*86d7f5d3SJohn Marino  *		information about directories to build.
1208*86d7f5d3SJohn Marino  *   sticky	Passed to build_one_dir to tell it whether there are any sticky
1209*86d7f5d3SJohn Marino  *		tags or dates to be concerned with.
1210*86d7f5d3SJohn Marino  *
1211*86d7f5d3SJohn Marino  * RETURNS
1212*86d7f5d3SJohn Marino  *   1 on error, 0 otherwise.
1213*86d7f5d3SJohn Marino  *
1214*86d7f5d3SJohn Marino  * ERRORS
1215*86d7f5d3SJohn Marino  *  The only nonfatal error this function may return is if the CHDIR fails.
1216*86d7f5d3SJohn Marino  */
1217*86d7f5d3SJohn Marino static int
build_dirs_and_chdir(struct dir_to_build * dirs,int sticky)1218*86d7f5d3SJohn Marino build_dirs_and_chdir (struct dir_to_build *dirs, int sticky)
1219*86d7f5d3SJohn Marino {
1220*86d7f5d3SJohn Marino     int retval = 0;
1221*86d7f5d3SJohn Marino     struct dir_to_build *nextdir;
1222*86d7f5d3SJohn Marino 
1223*86d7f5d3SJohn Marino     while (dirs != NULL)
1224*86d7f5d3SJohn Marino     {
1225*86d7f5d3SJohn Marino 	const char *dir = last_component (dirs->dirpath);
1226*86d7f5d3SJohn Marino 	int made_dir = 0;
1227*86d7f5d3SJohn Marino 
1228*86d7f5d3SJohn Marino 	made_dir = !mkdir_if_needed (dir);
1229*86d7f5d3SJohn Marino 	if (made_dir) Subdir_Register (NULL, NULL, dir);
1230*86d7f5d3SJohn Marino 
1231*86d7f5d3SJohn Marino 	if (CVS_CHDIR (dir) < 0)
1232*86d7f5d3SJohn Marino 	{
1233*86d7f5d3SJohn Marino 	    error (0, errno, "cannot chdir to %s", dir);
1234*86d7f5d3SJohn Marino 	    retval = 1;
1235*86d7f5d3SJohn Marino 	    goto out;
1236*86d7f5d3SJohn Marino 	}
1237*86d7f5d3SJohn Marino 	if (dirs->repository != NULL)
1238*86d7f5d3SJohn Marino 	{
1239*86d7f5d3SJohn Marino 	    if (made_dir)
1240*86d7f5d3SJohn Marino 		build_one_dir (dirs->repository, dirs->dirpath, sticky);
1241*86d7f5d3SJohn Marino 	    free (dirs->repository);
1242*86d7f5d3SJohn Marino 	}
1243*86d7f5d3SJohn Marino 	nextdir = dirs->next;
1244*86d7f5d3SJohn Marino 	free (dirs->dirpath);
1245*86d7f5d3SJohn Marino 	free (dirs);
1246*86d7f5d3SJohn Marino 	dirs = nextdir;
1247*86d7f5d3SJohn Marino     }
1248*86d7f5d3SJohn Marino 
1249*86d7f5d3SJohn Marino  out:
1250*86d7f5d3SJohn Marino     while (dirs != NULL)
1251*86d7f5d3SJohn Marino     {
1252*86d7f5d3SJohn Marino 	if (dirs->repository != NULL)
1253*86d7f5d3SJohn Marino 	    free (dirs->repository);
1254*86d7f5d3SJohn Marino 	nextdir = dirs->next;
1255*86d7f5d3SJohn Marino 	free (dirs->dirpath);
1256*86d7f5d3SJohn Marino 	free (dirs);
1257*86d7f5d3SJohn Marino 	dirs = nextdir;
1258*86d7f5d3SJohn Marino     }
1259*86d7f5d3SJohn Marino     return retval;
1260*86d7f5d3SJohn Marino }
1261