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