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