xref: /openbsd/usr.bin/cvs/checkout.c (revision 431378d1)
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