1*431378d1Snaddy /* $OpenBSD: checkout.c,v 1.172 2020/10/19 19:51:20 naddy Exp $ */
208f90673Sjfb /*
33ad3fb45Sjoris * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
408f90673Sjfb *
53ad3fb45Sjoris * Permission to use, copy, modify, and distribute this software for any
63ad3fb45Sjoris * purpose with or without fee is hereby granted, provided that the above
73ad3fb45Sjoris * copyright notice and this permission notice appear in all copies.
808f90673Sjfb *
93ad3fb45Sjoris * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103ad3fb45Sjoris * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113ad3fb45Sjoris * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123ad3fb45Sjoris * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133ad3fb45Sjoris * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143ad3fb45Sjoris * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153ad3fb45Sjoris * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1608f90673Sjfb */
1708f90673Sjfb
18b9fc9a72Sderaadt #include <sys/types.h>
191f8531bdSotto #include <sys/dirent.h>
201f8531bdSotto #include <sys/stat.h>
21bed6eeadStobias #include <sys/time.h>
221f8531bdSotto
231f8531bdSotto #include <errno.h>
241f8531bdSotto #include <fcntl.h>
251526ef95Stobias #include <libgen.h>
2612cb1c4eSxsa #include <stdlib.h>
271f8531bdSotto #include <string.h>
286534056aStobias #include <time.h>
291f8531bdSotto #include <unistd.h>
3008f90673Sjfb
3108f90673Sjfb #include "cvs.h"
323ad3fb45Sjoris #include "diff.h"
339fac60a5Sjoris #include "remote.h"
3408f90673Sjfb
3590f9bb03Sxsa static void checkout_check_repository(int, char **);
36b0d656e9Stobias static int checkout_classify(const char *, const char *);
373ad3fb45Sjoris static void checkout_repository(const char *, const char *);
3808f90673Sjfb
397ac78d1dSjoris extern int print_stdout;
409f820608Sjoris extern int prune_dirs;
41cabc4131Sjoris extern int build_dirs;
429f820608Sjoris
43a2f23aefSxsa static int flags = CR_REPO | CR_RECURSE_DIRS;
44a10a48e5Stobias static int Aflag = 0;
45dbeb8705Sjoris static char *dflag = NULL;
4637fdff3fStobias static char *koptstr = NULL;
4702d9e5c9Sjoris static char *dateflag = NULL;
48a2f23aefSxsa
4944b842f2Sjoris static int nflag = 0;
5044b842f2Sjoris
5102d9e5c9Sjoris char *checkout_target_dir = NULL;
521b5598b0Sjoris
537f0c79fcSjoris time_t cvs_specified_date = -1;
54b87788a5Stobias time_t cvs_directory_date = -1;
551b5598b0Sjoris int disable_fast_checkout = 0;
56113cb29fStobias
57e4276007Sjfb struct cvs_cmd cvs_cmd_checkout = {
58f331ff59Stobias CVS_OP_CHECKOUT, CVS_USE_WDIR, "checkout",
59e4276007Sjfb { "co", "get" },
603ad3fb45Sjoris "Checkout a working copy of a repository",
6138019573Sxsa "[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] "
62e4276007Sjfb "[-t id] module ...",
63e3095c02Sxsa "AcD:d:fj:k:lNnPpRr:st:",
64e4276007Sjfb NULL,
653ad3fb45Sjoris cvs_checkout
6616cfc147Sjoris };
6716cfc147Sjoris
6825654a94Sjoris struct cvs_cmd cvs_cmd_export = {
69f331ff59Stobias CVS_OP_EXPORT, CVS_USE_WDIR, "export",
7025654a94Sjoris { "exp", "ex" },
7125654a94Sjoris "Export sources from CVS, similar to checkout",
7290f9bb03Sxsa "[-flNnR] [-d dir] [-k mode] -D date | -r rev module ...",
7390f9bb03Sxsa "D:d:k:flNnRr:",
7425654a94Sjoris NULL,
7590f9bb03Sxsa cvs_export
7625654a94Sjoris };
7725654a94Sjoris
783ad3fb45Sjoris int
cvs_checkout(int argc,char ** argv)793ad3fb45Sjoris cvs_checkout(int argc, char **argv)
8008f90673Sjfb {
81a2f23aefSxsa int ch;
8208f90673Sjfb
833ad3fb45Sjoris while ((ch = getopt(argc, argv, cvs_cmd_checkout.cmd_opts)) != -1) {
8408f90673Sjfb switch (ch) {
853734fd29Sxsa case 'A':
86a10a48e5Stobias Aflag = 1;
87a10a48e5Stobias if (koptstr == NULL)
88a10a48e5Stobias reset_option = 1;
89a10a48e5Stobias if (cvs_specified_tag == NULL)
90a10a48e5Stobias reset_tag = 1;
913734fd29Sxsa break;
92dee76bdfSjoris case 'c':
93dee76bdfSjoris cvs_modules_list();
94dee76bdfSjoris exit(0);
95113cb29fStobias case 'D':
9602d9e5c9Sjoris dateflag = optarg;
97092db204Sray if ((cvs_specified_date = date_parse(dateflag)) == -1)
98092db204Sray fatal("invalid date: %s", dateflag);
99b87788a5Stobias reset_tag = 0;
100113cb29fStobias break;
101dbeb8705Sjoris case 'd':
102dbeb8705Sjoris if (dflag != NULL)
103dbeb8705Sjoris fatal("-d specified two or more times");
104dbeb8705Sjoris dflag = optarg;
10502d9e5c9Sjoris checkout_target_dir = dflag;
1061b5598b0Sjoris
1071b5598b0Sjoris if (cvs_server_active == 1)
1081b5598b0Sjoris disable_fast_checkout = 1;
109dbeb8705Sjoris break;
11025d391d7Sjoris case 'j':
11125d391d7Sjoris if (cvs_join_rev1 == NULL)
11225d391d7Sjoris cvs_join_rev1 = optarg;
11325d391d7Sjoris else if (cvs_join_rev2 == NULL)
11425d391d7Sjoris cvs_join_rev2 = optarg;
11525d391d7Sjoris else
11625d391d7Sjoris fatal("too many -j options");
11725d391d7Sjoris break;
11837fdff3fStobias case 'k':
119a10a48e5Stobias reset_option = 0;
12037fdff3fStobias koptstr = optarg;
12137fdff3fStobias kflag = rcs_kflag_get(koptstr);
12237fdff3fStobias if (RCS_KWEXP_INVAL(kflag)) {
12337fdff3fStobias cvs_log(LP_ERR,
1240bc1d395Stobias "invalid RCS keyword expansion mode");
12557ec9c21Ssthen fatal("%s", cvs_cmd_checkout.cmd_synopsis);
12637fdff3fStobias }
12737fdff3fStobias break;
128e3095c02Sxsa case 'l':
129e3095c02Sxsa flags &= ~CR_RECURSE_DIRS;
130e3095c02Sxsa break;
13151ef6581Sjoris case 'N':
13251ef6581Sjoris break;
13344b842f2Sjoris case 'n':
13444b842f2Sjoris nflag = 1;
13544b842f2Sjoris break;
1369f820608Sjoris case 'P':
1379f820608Sjoris prune_dirs = 1;
1389f820608Sjoris break;
1397ac78d1dSjoris case 'p':
140f331ff59Stobias cmdp->cmd_flags &= ~CVS_USE_WDIR;
1417ac78d1dSjoris print_stdout = 1;
1427ac78d1dSjoris cvs_noexec = 1;
14344b842f2Sjoris nflag = 1;
1447ac78d1dSjoris break;
145e3095c02Sxsa case 'R':
146bcf22459Stobias flags |= CR_RECURSE_DIRS;
147e3095c02Sxsa break;
148ca2dc546Sniallo case 'r':
149a10a48e5Stobias reset_tag = 0;
1505dd120b0Sjoris cvs_specified_tag = optarg;
151ca2dc546Sniallo break;
15208f90673Sjfb default:
1533ad3fb45Sjoris fatal("%s", cvs_cmd_checkout.cmd_synopsis);
15408f90673Sjfb }
15508f90673Sjfb }
15608f90673Sjfb
15708f90673Sjfb argc -= optind;
15808f90673Sjfb argv += optind;
15908f90673Sjfb
1603ad3fb45Sjoris if (argc == 0)
1613ad3fb45Sjoris fatal("%s", cvs_cmd_checkout.cmd_synopsis);
162e4276007Sjfb
1631b5598b0Sjoris if (cvs_server_active == 1 && disable_fast_checkout != 1) {
1641b5598b0Sjoris cmdp->cmd_flags &= ~CVS_USE_WDIR;
1651b5598b0Sjoris cvs_noexec = 1;
1661b5598b0Sjoris }
1671b5598b0Sjoris
16890f9bb03Sxsa checkout_check_repository(argc, argv);
16990f9bb03Sxsa
1701b5598b0Sjoris if (cvs_server_active == 1 && disable_fast_checkout != 1)
1711b5598b0Sjoris cvs_noexec = 0;
1721b5598b0Sjoris
17390f9bb03Sxsa return (0);
17490f9bb03Sxsa }
17590f9bb03Sxsa
17690f9bb03Sxsa int
cvs_export(int argc,char ** argv)17790f9bb03Sxsa cvs_export(int argc, char **argv)
17890f9bb03Sxsa {
179a2f23aefSxsa int ch;
18090f9bb03Sxsa
18190f9bb03Sxsa prune_dirs = 1;
18290f9bb03Sxsa
18390f9bb03Sxsa while ((ch = getopt(argc, argv, cvs_cmd_export.cmd_opts)) != -1) {
18490f9bb03Sxsa switch (ch) {
185da22aafdSpyr case 'd':
186da22aafdSpyr if (dflag != NULL)
187da22aafdSpyr fatal("-d specified two or more times");
188da22aafdSpyr dflag = optarg;
189da22aafdSpyr checkout_target_dir = dflag;
190da22aafdSpyr
191da22aafdSpyr if (cvs_server_active == 1)
192da22aafdSpyr disable_fast_checkout = 1;
193da22aafdSpyr break;
19437fdff3fStobias case 'k':
19537fdff3fStobias koptstr = optarg;
19637fdff3fStobias kflag = rcs_kflag_get(koptstr);
19737fdff3fStobias if (RCS_KWEXP_INVAL(kflag)) {
19837fdff3fStobias cvs_log(LP_ERR,
1990bc1d395Stobias "invalid RCS keyword expansion mode");
20057ec9c21Ssthen fatal("%s", cvs_cmd_export.cmd_synopsis);
20137fdff3fStobias }
20237fdff3fStobias break;
20390f9bb03Sxsa case 'l':
20490f9bb03Sxsa flags &= ~CR_RECURSE_DIRS;
20590f9bb03Sxsa break;
206a5a3988bSjoris case 'N':
207a5a3988bSjoris break;
20890f9bb03Sxsa case 'R':
209bcf22459Stobias flags |= CR_RECURSE_DIRS;
21090f9bb03Sxsa break;
2110ab67f86Sxsa case 'r':
2120ab67f86Sxsa cvs_specified_tag = optarg;
2130ab67f86Sxsa break;
21490f9bb03Sxsa default:
21590f9bb03Sxsa fatal("%s", cvs_cmd_export.cmd_synopsis);
21690f9bb03Sxsa }
21790f9bb03Sxsa }
21890f9bb03Sxsa
21990f9bb03Sxsa argc -= optind;
22090f9bb03Sxsa argv += optind;
22190f9bb03Sxsa
2225e1effbaStobias if (cvs_specified_tag == NULL)
2235e1effbaStobias fatal("must specify a tag or date");
2245e1effbaStobias
22590f9bb03Sxsa if (argc == 0)
22690f9bb03Sxsa fatal("%s", cvs_cmd_export.cmd_synopsis);
22790f9bb03Sxsa
22890f9bb03Sxsa checkout_check_repository(argc, argv);
22990f9bb03Sxsa
23090f9bb03Sxsa return (0);
23190f9bb03Sxsa }
23290f9bb03Sxsa
23390f9bb03Sxsa static void
checkout_check_repository(int argc,char ** argv)23490f9bb03Sxsa checkout_check_repository(int argc, char **argv)
23590f9bb03Sxsa {
23659b603d6Sxsa int i;
2377fca5395Sjoris char *wdir, *d;
2385219eee5Sjoris struct cvs_recursion cr;
239bf6291b7Sjoris struct module_checkout *mc;
2407fca5395Sjoris struct cvs_ignpat *ip;
2417fca5395Sjoris struct cvs_filelist *fl, *nxt;
242*431378d1Snaddy char repo[PATH_MAX], fpath[PATH_MAX], path[PATH_MAX], *f[1];
2435219eee5Sjoris
24446bff1c6Stobias build_dirs = print_stdout ? 0 : 1;
24546bff1c6Stobias
2464dcde513Sjoris if (cvsroot_is_remote()) {
2475219eee5Sjoris cvs_client_connect_to_server();
2485219eee5Sjoris
2495dd120b0Sjoris if (cvs_specified_tag != NULL)
2505dd120b0Sjoris cvs_client_send_request("Argument -r%s",
2515dd120b0Sjoris cvs_specified_tag);
252a10a48e5Stobias if (Aflag)
2535219eee5Sjoris cvs_client_send_request("Argument -A");
2545219eee5Sjoris
25502d9e5c9Sjoris if (dateflag != NULL)
25602d9e5c9Sjoris cvs_client_send_request("Argument -D%s", dateflag);
25702d9e5c9Sjoris
25837fdff3fStobias if (kflag)
25937fdff3fStobias cvs_client_send_request("Argument -k%s", koptstr);
26037fdff3fStobias
2618dead7d9Sjoris if (dflag != NULL)
2628dead7d9Sjoris cvs_client_send_request("Argument -d%s", dflag);
2638dead7d9Sjoris
26425e75a48Sxsa if (!(flags & CR_RECURSE_DIRS))
26525e75a48Sxsa cvs_client_send_request("Argument -l");
26625e75a48Sxsa
2675219eee5Sjoris if (cvs_cmdop == CVS_OP_CHECKOUT && prune_dirs == 1)
2685219eee5Sjoris cvs_client_send_request("Argument -P");
2695219eee5Sjoris
2707ac78d1dSjoris if (print_stdout == 1)
2717ac78d1dSjoris cvs_client_send_request("Argument -p");
2727ac78d1dSjoris
27344b842f2Sjoris if (nflag == 1)
27444b842f2Sjoris cvs_client_send_request("Argument -n");
27544b842f2Sjoris
2765219eee5Sjoris cr.enterdir = NULL;
2775219eee5Sjoris cr.leavedir = NULL;
278c634bf10Stobias if (print_stdout)
27946bff1c6Stobias cr.fileproc = NULL;
280c634bf10Stobias else
281c634bf10Stobias cr.fileproc = cvs_client_sendfile;
282c634bf10Stobias
283c634bf10Stobias flags &= ~CR_REPO;
2845219eee5Sjoris cr.flags = flags;
2855219eee5Sjoris
2865e1effbaStobias if (cvs_cmdop != CVS_OP_EXPORT)
2875219eee5Sjoris cvs_file_run(argc, argv, &cr);
2885219eee5Sjoris
2895219eee5Sjoris cvs_client_send_files(argv, argc);
2905219eee5Sjoris cvs_client_senddir(".");
2915219eee5Sjoris
2925219eee5Sjoris cvs_client_send_request("%s",
2935219eee5Sjoris (cvs_cmdop == CVS_OP_CHECKOUT) ? "co" : "export");
2945219eee5Sjoris
2955219eee5Sjoris cvs_client_get_responses();
2965219eee5Sjoris
2975219eee5Sjoris return;
2985219eee5Sjoris }
29990f9bb03Sxsa
3003ad3fb45Sjoris for (i = 0; i < argc; i++) {
301bf6291b7Sjoris mc = cvs_module_lookup(argv[i]);
302bf6291b7Sjoris current_module = mc;
303fc22209eSjfb
304f106b389Sjoris RB_FOREACH(fl, cvs_flisthead, &(mc->mc_ignores))
3057fca5395Sjoris cvs_file_ignore(fl->file_path, &checkout_ign_pats);
3067fca5395Sjoris
307f106b389Sjoris RB_FOREACH(fl, cvs_flisthead, &(mc->mc_modules)) {
308fa379effStobias module_repo_root = NULL;
309fa379effStobias
31075bebbccSjoris (void)xsnprintf(repo, sizeof(repo), "%s/%s",
3117fca5395Sjoris current_cvsroot->cr_dir, fl->file_path);
31275bebbccSjoris
313dbeb8705Sjoris if (!(mc->mc_flags & MODULE_ALIAS) || dflag != NULL)
314fa379effStobias module_repo_root = xstrdup(fl->file_path);
315bf6291b7Sjoris
316bf659b2eSjoris if (mc->mc_flags & MODULE_NORECURSE)
317bf659b2eSjoris flags &= ~CR_RECURSE_DIRS;
318bf659b2eSjoris
319dbeb8705Sjoris if (dflag != NULL)
320dbeb8705Sjoris wdir = dflag;
321f748429cSjoris else if (mc->mc_flags & MODULE_ALIAS)
322f748429cSjoris wdir = fl->file_path;
323dbeb8705Sjoris else
324f748429cSjoris wdir = mc->mc_name;
325dbeb8705Sjoris
3267fca5395Sjoris switch (checkout_classify(repo, fl->file_path)) {
327b0d656e9Stobias case CVS_FILE:
3281526ef95Stobias cr.fileproc = cvs_update_local;
3291526ef95Stobias cr.flags = flags;
33046bff1c6Stobias
3317fca5395Sjoris if (!(mc->mc_flags & MODULE_ALIAS)) {
332*431378d1Snaddy if (strlcpy(path, fl->file_path,
333*431378d1Snaddy sizeof(path)) >= sizeof(path))
334*431378d1Snaddy fatal("%s: truncation",
335*431378d1Snaddy __func__);
3367fca5395Sjoris module_repo_root =
337*431378d1Snaddy xstrdup(dirname(path));
3387fca5395Sjoris d = wdir;
339*431378d1Snaddy if (strlcpy(path, fl->file_path,
340*431378d1Snaddy sizeof(path)) >= sizeof(path))
341*431378d1Snaddy fatal("%s: truncation",
342*431378d1Snaddy __func__);
3437fca5395Sjoris (void)xsnprintf(fpath, sizeof(fpath),
344*431378d1Snaddy "%s/%s", d, basename(path));
3457fca5395Sjoris } else {
346*431378d1Snaddy if (strlcpy(path, wdir,
347*431378d1Snaddy sizeof(path)) >= sizeof(path))
348*431378d1Snaddy fatal("%s: truncation",
349*431378d1Snaddy __func__);
350*431378d1Snaddy d = dirname(path);
3517fca5395Sjoris strlcpy(fpath, fl->file_path,
3527fca5395Sjoris sizeof(fpath));
3537fca5395Sjoris }
3547fca5395Sjoris
35546bff1c6Stobias if (build_dirs == 1)
3567fca5395Sjoris cvs_mkpath(d, cvs_specified_tag);
3577fca5395Sjoris
3587fca5395Sjoris f[0] = fpath;
3597fca5395Sjoris cvs_file_run(1, f, &cr);
360b0d656e9Stobias break;
361b0d656e9Stobias case CVS_DIR:
36246bff1c6Stobias if (build_dirs == 1)
363dbeb8705Sjoris cvs_mkpath(wdir, cvs_specified_tag);
364dbeb8705Sjoris checkout_repository(repo, wdir);
365b0d656e9Stobias break;
366b0d656e9Stobias default:
367b0d656e9Stobias break;
3683ad3fb45Sjoris }
369ba6315b4Sjoris
37044b842f2Sjoris if (nflag != 1 && mc->mc_prog != NULL &&
371ea48eba6Sjoris mc->mc_flags & MODULE_RUN_ON_CHECKOUT)
372b034d592Sjoris cvs_exec(mc->mc_prog, NULL, 0);
373fa379effStobias
374397ddb8aSnicm free(module_repo_root);
3757fca5395Sjoris }
3767fca5395Sjoris
3777fca5395Sjoris if (mc->mc_canfree == 1) {
378f106b389Sjoris for (fl = RB_MIN(cvs_flisthead, &(mc->mc_modules));
379f106b389Sjoris fl != NULL; fl = nxt) {
380f106b389Sjoris nxt = RB_NEXT(cvs_flisthead,
381f106b389Sjoris &(mc->mc_modules), fl);
382f106b389Sjoris RB_REMOVE(cvs_flisthead,
383f106b389Sjoris &(mc->mc_modules), fl);
384397ddb8aSnicm free(fl->file_path);
385397ddb8aSnicm free(fl);
3867fca5395Sjoris }
3877fca5395Sjoris }
3887fca5395Sjoris
3897fca5395Sjoris while ((ip = TAILQ_FIRST(&checkout_ign_pats)) != NULL) {
3907fca5395Sjoris TAILQ_REMOVE(&checkout_ign_pats, ip, ip_list);
391397ddb8aSnicm free(ip);
3927fca5395Sjoris }
393bf6291b7Sjoris
394397ddb8aSnicm free(mc);
39562f911b4Sjfb }
396b0d656e9Stobias }
397b0d656e9Stobias
398b0d656e9Stobias static int
checkout_classify(const char * repo,const char * arg)399b0d656e9Stobias checkout_classify(const char *repo, const char *arg)
400b0d656e9Stobias {
401*431378d1Snaddy char *d, dbuf[PATH_MAX], *f, fbuf[PATH_MAX], fpath[PATH_MAX];
402b0d656e9Stobias struct stat sb;
403b0d656e9Stobias
404b0d656e9Stobias if (stat(repo, &sb) == 0) {
4057fca5395Sjoris if (S_ISDIR(sb.st_mode))
406b0d656e9Stobias return CVS_DIR;
407b0d656e9Stobias }
408b0d656e9Stobias
409*431378d1Snaddy if (strlcpy(dbuf, repo, sizeof(dbuf)) >= sizeof(dbuf))
410*431378d1Snaddy fatal("checkout_classify: truncation");
411*431378d1Snaddy d = dirname(dbuf);
412*431378d1Snaddy
413*431378d1Snaddy if (strlcpy(fbuf, repo, sizeof(fbuf)) >= sizeof(fbuf))
414*431378d1Snaddy fatal("checkout_classify: truncation");
415*431378d1Snaddy f = basename(fbuf);
416b0d656e9Stobias
417b0d656e9Stobias (void)xsnprintf(fpath, sizeof(fpath), "%s/%s%s", d, f, RCS_FILE_EXT);
418b0d656e9Stobias if (stat(fpath, &sb) == 0) {
419b0d656e9Stobias if (!S_ISREG(sb.st_mode)) {
420b0d656e9Stobias cvs_log(LP_ERR, "ignoring %s: not a regular file", arg);
421b0d656e9Stobias return 0;
422b0d656e9Stobias }
423b0d656e9Stobias return CVS_FILE;
424b0d656e9Stobias }
425b0d656e9Stobias
426b0d656e9Stobias (void)xsnprintf(fpath, sizeof(fpath), "%s/%s/%s%s",
427b0d656e9Stobias d, CVS_PATH_ATTIC, f, RCS_FILE_EXT);
428b0d656e9Stobias if (stat(fpath, &sb) == 0) {
429b0d656e9Stobias if (!S_ISREG(sb.st_mode)) {
430b0d656e9Stobias cvs_log(LP_ERR, "ignoring %s: not a regular file", arg);
431b0d656e9Stobias return 0;
432b0d656e9Stobias }
433b0d656e9Stobias return CVS_FILE;
434b0d656e9Stobias }
435b0d656e9Stobias
436b0d656e9Stobias cvs_log(LP_ERR, "cannot find module `%s' - ignored", arg);
437b0d656e9Stobias return 0;
438b0d656e9Stobias }
43908f90673Sjfb
4403ad3fb45Sjoris static void
checkout_repository(const char * repobase,const char * wdbase)4413ad3fb45Sjoris checkout_repository(const char *repobase, const char *wdbase)
44216cfc147Sjoris {
4433ad3fb45Sjoris struct cvs_flisthead fl, dl;
4443ad3fb45Sjoris struct cvs_recursion cr;
4455191afffSjoris
446f106b389Sjoris RB_INIT(&fl);
447f106b389Sjoris RB_INIT(&dl);
4485191afffSjoris
449a76e09e0Sxsa cvs_history_add((cvs_cmdop == CVS_OP_CHECKOUT) ?
450a76e09e0Sxsa CVS_HISTORY_CHECKOUT : CVS_HISTORY_EXPORT, NULL, wdbase);
4513901dfa5Sjoris
452f331ff59Stobias if (print_stdout) {
453f331ff59Stobias cr.enterdir = NULL;
454f331ff59Stobias cr.leavedir = NULL;
455f331ff59Stobias } else {
4563ad3fb45Sjoris cr.enterdir = cvs_update_enterdir;
4571b5598b0Sjoris if (cvs_server_active == 1) {
4581b5598b0Sjoris if (disable_fast_checkout != 1)
4591b5598b0Sjoris cr.leavedir = NULL;
4601b5598b0Sjoris else
461878499ddSjoris cr.leavedir = cvs_update_leavedir;
4621b5598b0Sjoris } else {
4631b5598b0Sjoris cr.leavedir = prune_dirs ? cvs_update_leavedir : NULL;
4641b5598b0Sjoris }
465f331ff59Stobias }
466bc5d89feSjoris cr.fileproc = cvs_update_local;
467a2f23aefSxsa cr.flags = flags;
468e4276007Sjfb
469fb3beb6cSjoris cvs_repository_lock(repobase, 0);
47057448823Stobias cvs_repository_getdir(repobase, wdbase, &fl, &dl,
47183389238Sjoris flags & CR_RECURSE_DIRS ? REPOSITORY_DODIRS : 0);
47250b634f9Sjoris
4733ad3fb45Sjoris cvs_file_walklist(&fl, &cr);
4743ad3fb45Sjoris cvs_file_freelist(&fl);
475e4276007Sjfb
4763ad3fb45Sjoris cvs_repository_unlock(repobase);
47750b634f9Sjoris
4783ad3fb45Sjoris cvs_file_walklist(&dl, &cr);
4793ad3fb45Sjoris cvs_file_freelist(&dl);
48050b634f9Sjoris }
48150b634f9Sjoris
482f9872b43Sjoris void
cvs_checkout_file(struct cvs_file * cf,RCSNUM * rnum,char * tag,int co_flags)4835d320860Sjoris cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, char *tag, int co_flags)
4845191afffSjoris {
4851b5598b0Sjoris BUF *bp;
4860d18a67fSjoris mode_t mode;
4877e25e6f9Sjoris int cf_kflag, exists;
4883ad3fb45Sjoris time_t rcstime;
4893ad3fb45Sjoris CVSENTRIES *ent;
4903ad3fb45Sjoris struct timeval tv[2];
4916534056aStobias struct tm datetm;
4921b5598b0Sjoris char *entry, *tosend;
4934967382bSxsa char kbuf[8], sticky[CVS_REV_BUFSZ], rev[CVS_REV_BUFSZ];
4940a7da307Sxsa char timebuf[CVS_TIME_BUFSZ], tbuf[CVS_TIME_BUFSZ];
495b9fc9a72Sderaadt static char lastwd[PATH_MAX];
4965191afffSjoris
497828954c0Sjoris exists = 0;
498828954c0Sjoris tosend = NULL;
49951ef6581Sjoris
50051ef6581Sjoris if (!(co_flags & CO_REMOVE))
5013ad3fb45Sjoris rcsnum_tostr(rnum, rev, sizeof(rev));
502e28eda4eStobias else
503e28eda4eStobias rev[0] = '\0';
5045191afffSjoris
5059fac60a5Sjoris cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d) -> %s",
506a2f23aefSxsa cf->file_path, rev, co_flags,
5079fac60a5Sjoris (cvs_server_active) ? "to client" : "to disk");
5085191afffSjoris
509a2f23aefSxsa if (co_flags & CO_DUMP) {
51087d368dcStobias rcs_rev_write_fd(cf->file_rcs, rnum, STDOUT_FILENO, 0);
511b17342c4Sreyk return;
512b17342c4Sreyk }
513b17342c4Sreyk
5149fac60a5Sjoris if (cvs_server_active == 0) {
515801c91ecSjoris (void)unlink(cf->file_path);
516801c91ecSjoris
517828954c0Sjoris if (!(co_flags & CO_MERGE)) {
5185cf15c45Sjoris if (cf->file_flags & FILE_ON_DISK) {
5193ad3fb45Sjoris exists = 1;
5203ad3fb45Sjoris (void)close(cf->fd);
5213ad3fb45Sjoris }
5223ad3fb45Sjoris
523801c91ecSjoris cf->fd = open(cf->file_path,
52425d391d7Sjoris O_CREAT | O_RDWR | O_TRUNC);
5253ad3fb45Sjoris if (cf->fd == -1)
526828954c0Sjoris fatal("cvs_checkout_file: open: %s",
527828954c0Sjoris strerror(errno));
5283ad3fb45Sjoris
52987d368dcStobias rcs_rev_write_fd(cf->file_rcs, rnum, cf->fd, 0);
5305cf15c45Sjoris cf->file_flags |= FILE_ON_DISK;
531828954c0Sjoris } else {
53225d391d7Sjoris cvs_merge_file(cf, (cvs_join_rev1 == NULL));
533828954c0Sjoris }
5343ad3fb45Sjoris
5350d18a67fSjoris mode = cf->file_rcs->rf_mode;
5360d18a67fSjoris mode |= S_IWUSR;
5370d18a67fSjoris
5380d18a67fSjoris if (fchmod(cf->fd, mode) == -1)
5393ad3fb45Sjoris fatal("cvs_checkout_file: fchmod: %s", strerror(errno));
5403ad3fb45Sjoris
541828954c0Sjoris if ((exists == 0) && (cf->file_ent == NULL) &&
542828954c0Sjoris !(co_flags & CO_MERGE))
5433ad3fb45Sjoris rcstime = rcs_rev_getdate(cf->file_rcs, rnum);
54447e5fe63Sjoris else
5453ad3fb45Sjoris time(&rcstime);
5463ad3fb45Sjoris
5473ad3fb45Sjoris tv[0].tv_sec = rcstime;
5483ad3fb45Sjoris tv[0].tv_usec = 0;
5493ad3fb45Sjoris tv[1] = tv[0];
5503ad3fb45Sjoris if (futimes(cf->fd, tv) == -1)
5519fac60a5Sjoris fatal("cvs_checkout_file: futimes: %s",
5529fac60a5Sjoris strerror(errno));
5539fac60a5Sjoris } else {
5549fac60a5Sjoris time(&rcstime);
5559fac60a5Sjoris }
5563ad3fb45Sjoris
5576534056aStobias gmtime_r(&rcstime, &datetm);
5586534056aStobias asctime_r(&datetm, tbuf);
5592820b891Stobias tbuf[strcspn(tbuf, "\n")] = '\0';
560f9872b43Sjoris
561a2f23aefSxsa if (co_flags & CO_MERGE) {
562c486465dSxsa (void)xsnprintf(timebuf, sizeof(timebuf), "Result of merge+%s",
563f9872b43Sjoris tbuf);
564f9872b43Sjoris } else {
565f9872b43Sjoris strlcpy(timebuf, tbuf, sizeof(timebuf));
566f9872b43Sjoris }
5673ad3fb45Sjoris
568b87788a5Stobias if (reset_tag) {
569b87788a5Stobias sticky[0] = '\0';
570b87788a5Stobias } else if (co_flags & CO_SETSTICKY)
5715d320860Sjoris if (tag != NULL)
5720bf9adf7Sxsa (void)xsnprintf(sticky, sizeof(sticky), "T%s", tag);
5737f0c79fcSjoris else if (cvs_specified_date != -1) {
5746534056aStobias gmtime_r(&cvs_specified_date, &datetm);
57525d9b074Sxsa (void)strftime(sticky, sizeof(sticky),
5766534056aStobias "D"CVS_DATE_FMT, &datetm);
577b87788a5Stobias } else if (cvs_directory_date != -1) {
578b87788a5Stobias gmtime_r(&cvs_directory_date, &datetm);
579b87788a5Stobias (void)strftime(sticky, sizeof(sticky),
580b87788a5Stobias "D"CVS_DATE_FMT, &datetm);
581083eff0dStobias } else
5820bf9adf7Sxsa (void)xsnprintf(sticky, sizeof(sticky), "T%s", rev);
583b87788a5Stobias else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
5840bf9adf7Sxsa (void)xsnprintf(sticky, sizeof(sticky), "T%s",
585ebd8626fStobias cf->file_ent->ce_tag);
586c486465dSxsa else
5870bf9adf7Sxsa sticky[0] = '\0';
5886fba94e7Sjoris
58956f996a2Sxsa kbuf[0] = '\0';
590050d196aSjoris if (cf->file_rcs != NULL && cf->file_rcs->rf_expand != NULL) {
59137fdff3fStobias cf_kflag = rcs_kflag_get(cf->file_rcs->rf_expand);
59237fdff3fStobias if (kflag || cf_kflag != RCS_KWEXP_DEFAULT)
593c486465dSxsa (void)xsnprintf(kbuf, sizeof(kbuf),
59456f996a2Sxsa "-k%s", cf->file_rcs->rf_expand);
595a10a48e5Stobias } else if (!reset_option && cf->file_ent != NULL) {
59637fdff3fStobias if (cf->file_ent->ce_opts != NULL)
59737fdff3fStobias strlcpy(kbuf, cf->file_ent->ce_opts, sizeof(kbuf));
59856f996a2Sxsa }
59956f996a2Sxsa
600ae83823aSxsa entry = xmalloc(CVS_ENT_MAXLINELEN);
601ae83823aSxsa cvs_ent_line_str(cf->file_name, rev, timebuf, kbuf, sticky, 0, 0,
602ae83823aSxsa entry, CVS_ENT_MAXLINELEN);
6033ad3fb45Sjoris
6049fac60a5Sjoris if (cvs_server_active == 0) {
6055e1effbaStobias if (!(co_flags & CO_REMOVE) && cvs_cmdop != CVS_OP_EXPORT) {
6063ad3fb45Sjoris ent = cvs_ent_open(cf->file_wd);
6073ad3fb45Sjoris cvs_ent_add(ent, entry);
60825d391d7Sjoris cf->file_ent = cvs_ent_parse(entry);
60952979273Sjoris }
6109fac60a5Sjoris } else {
611828954c0Sjoris if (co_flags & CO_MERGE) {
612801c91ecSjoris (void)unlink(cf->file_path);
61325d391d7Sjoris cvs_merge_file(cf, (cvs_join_rev1 == NULL));
614828954c0Sjoris tosend = cf->file_path;
615828954c0Sjoris }
616828954c0Sjoris
617a0fe5faaSjoris /*
618a0fe5faaSjoris * If this file has a tag, push out the Directory with the
619d16eec6fSjoris * tag to the client. Except when this file was explicitly
620d16eec6fSjoris * specified on the command line.
621a0fe5faaSjoris */
622d16eec6fSjoris if (tag != NULL && strcmp(cf->file_wd, lastwd) &&
623d16eec6fSjoris !(cf->file_flags & FILE_USER_SUPPLIED)) {
624b9fc9a72Sderaadt strlcpy(lastwd, cf->file_wd, PATH_MAX);
625a0fe5faaSjoris cvs_server_set_sticky(cf->file_wd, sticky);
626a0fe5faaSjoris }
627a0fe5faaSjoris
628a2f23aefSxsa if (co_flags & CO_COMMIT)
6293544a5fcStobias cvs_server_update_entry("Updated", cf);
630828954c0Sjoris else if (co_flags & CO_MERGE)
631828954c0Sjoris cvs_server_update_entry("Merged", cf);
63252979273Sjoris else if (co_flags & CO_REMOVE)
63352979273Sjoris cvs_server_update_entry("Removed", cf);
634408908afSjoris else
635408908afSjoris cvs_server_update_entry("Updated", cf);
6367fffa3ccSjoris
637ae83823aSxsa if (!(co_flags & CO_REMOVE)) {
6389fac60a5Sjoris cvs_remote_output(entry);
6397fffa3ccSjoris
640828954c0Sjoris if (!(co_flags & CO_MERGE)) {
6410d18a67fSjoris mode = cf->file_rcs->rf_mode;
6420d18a67fSjoris mode |= S_IWUSR;
6431b5598b0Sjoris bp = rcs_rev_getbuf(cf->file_rcs, rnum, 0);
6441b5598b0Sjoris cvs_remote_send_file_buf(cf->file_path,
6451b5598b0Sjoris bp, mode);
6461b5598b0Sjoris } else {
6477e25e6f9Sjoris cvs_remote_send_file(tosend, cf->fd);
648828954c0Sjoris }
6497fffa3ccSjoris }
6509fac60a5Sjoris }
651bf3e4586Stobias
652397ddb8aSnicm free(entry);
6533ad3fb45Sjoris }
654