xref: /openbsd-src/usr.bin/cvs/checkout.c (revision 37fdff3fa60fdd1baf6bc6082eee5bd7ac3b858c)
1*37fdff3fStobias /*	$OpenBSD: checkout.c,v 1.125 2008/02/04 15:07:32 tobias 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 
181f8531bdSotto #include <sys/param.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>
261f8531bdSotto #include <string.h>
271f8531bdSotto #include <unistd.h>
2808f90673Sjfb 
2908f90673Sjfb #include "cvs.h"
303ad3fb45Sjoris #include "diff.h"
319fac60a5Sjoris #include "remote.h"
3208f90673Sjfb 
3390f9bb03Sxsa static void checkout_check_repository(int, char **);
34b0d656e9Stobias static int checkout_classify(const char *, const char *);
353ad3fb45Sjoris static void checkout_repository(const char *, const char *);
3608f90673Sjfb 
377ac78d1dSjoris extern int print_stdout;
389f820608Sjoris extern int prune_dirs;
39cabc4131Sjoris extern int build_dirs;
409f820608Sjoris 
41a2f23aefSxsa static int flags = CR_REPO | CR_RECURSE_DIRS;
42dbeb8705Sjoris static char *dflag = NULL;
43*37fdff3fStobias static char *koptstr = NULL;
44a2f23aefSxsa 
45e4276007Sjfb struct cvs_cmd cvs_cmd_checkout = {
46f331ff59Stobias 	CVS_OP_CHECKOUT, CVS_USE_WDIR, "checkout",
47e4276007Sjfb 	{ "co", "get" },
483ad3fb45Sjoris 	"Checkout a working copy of a repository",
4938019573Sxsa 	"[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] "
50e4276007Sjfb 	"[-t id] module ...",
51e3095c02Sxsa 	"AcD:d:fj:k:lNnPpRr:st:",
52e4276007Sjfb 	NULL,
533ad3fb45Sjoris 	cvs_checkout
5416cfc147Sjoris };
5516cfc147Sjoris 
5625654a94Sjoris struct cvs_cmd cvs_cmd_export = {
57f331ff59Stobias 	CVS_OP_EXPORT, CVS_USE_WDIR, "export",
5825654a94Sjoris 	{ "exp", "ex" },
5925654a94Sjoris 	"Export sources from CVS, similar to checkout",
6090f9bb03Sxsa 	"[-flNnR] [-d dir] [-k mode] -D date | -r rev module ...",
6190f9bb03Sxsa 	"D:d:k:flNnRr:",
6225654a94Sjoris 	NULL,
6390f9bb03Sxsa 	cvs_export
6425654a94Sjoris };
6525654a94Sjoris 
663ad3fb45Sjoris int
673ad3fb45Sjoris cvs_checkout(int argc, char **argv)
6808f90673Sjfb {
69a2f23aefSxsa 	int ch;
7008f90673Sjfb 
713ad3fb45Sjoris 	while ((ch = getopt(argc, argv, cvs_cmd_checkout.cmd_opts)) != -1) {
7208f90673Sjfb 		switch (ch) {
733734fd29Sxsa 		case 'A':
743734fd29Sxsa 			reset_stickies = 1;
753734fd29Sxsa 			break;
76dbeb8705Sjoris 		case 'd':
77dbeb8705Sjoris 			if (dflag != NULL)
78dbeb8705Sjoris 				fatal("-d specified two or more times");
79dbeb8705Sjoris 			dflag = optarg;
80dbeb8705Sjoris 			break;
81*37fdff3fStobias 		case 'k':
82*37fdff3fStobias 			koptstr = optarg;
83*37fdff3fStobias 			kflag = rcs_kflag_get(koptstr);
84*37fdff3fStobias 			if (RCS_KWEXP_INVAL(kflag)) {
85*37fdff3fStobias 				cvs_log(LP_ERR,
86*37fdff3fStobias 				    "invalid RCS keyword expension mode");
87*37fdff3fStobias 				fatal("%s", cvs_cmd_add.cmd_synopsis);
88*37fdff3fStobias 			}
89*37fdff3fStobias 			break;
90e3095c02Sxsa 		case 'l':
91e3095c02Sxsa 			flags &= ~CR_RECURSE_DIRS;
92e3095c02Sxsa 			break;
9351ef6581Sjoris 		case 'N':
9451ef6581Sjoris 			break;
959f820608Sjoris 		case 'P':
969f820608Sjoris 			prune_dirs = 1;
979f820608Sjoris 			break;
987ac78d1dSjoris 		case 'p':
99f331ff59Stobias 			cmdp->cmd_flags &= ~CVS_USE_WDIR;
1007ac78d1dSjoris 			print_stdout = 1;
1017ac78d1dSjoris 			cvs_noexec = 1;
1027ac78d1dSjoris 			break;
103e3095c02Sxsa 		case 'R':
104bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
105e3095c02Sxsa 			break;
106ca2dc546Sniallo 		case 'r':
1075dd120b0Sjoris 			cvs_specified_tag = optarg;
108ca2dc546Sniallo 			break;
10908f90673Sjfb 		default:
1103ad3fb45Sjoris 			fatal("%s", cvs_cmd_checkout.cmd_synopsis);
11108f90673Sjfb 		}
11208f90673Sjfb 	}
11308f90673Sjfb 
11408f90673Sjfb 	argc -= optind;
11508f90673Sjfb 	argv += optind;
11608f90673Sjfb 
1173ad3fb45Sjoris 	if (argc == 0)
1183ad3fb45Sjoris 		fatal("%s", cvs_cmd_checkout.cmd_synopsis);
119e4276007Sjfb 
12090f9bb03Sxsa 	checkout_check_repository(argc, argv);
12190f9bb03Sxsa 
12290f9bb03Sxsa 	return (0);
12390f9bb03Sxsa }
12490f9bb03Sxsa 
12590f9bb03Sxsa int
12690f9bb03Sxsa cvs_export(int argc, char **argv)
12790f9bb03Sxsa {
128a2f23aefSxsa 	int ch;
12990f9bb03Sxsa 
13090f9bb03Sxsa 	prune_dirs = 1;
13190f9bb03Sxsa 
13290f9bb03Sxsa 	while ((ch = getopt(argc, argv, cvs_cmd_export.cmd_opts)) != -1) {
13390f9bb03Sxsa 		switch (ch) {
134*37fdff3fStobias 		case 'k':
135*37fdff3fStobias 			koptstr = optarg;
136*37fdff3fStobias 			kflag = rcs_kflag_get(koptstr);
137*37fdff3fStobias 			if (RCS_KWEXP_INVAL(kflag)) {
138*37fdff3fStobias 				cvs_log(LP_ERR,
139*37fdff3fStobias 				    "invalid RCS keyword expension mode");
140*37fdff3fStobias 				fatal("%s", cvs_cmd_add.cmd_synopsis);
141*37fdff3fStobias 			}
142*37fdff3fStobias 			break;
14390f9bb03Sxsa 		case 'l':
14490f9bb03Sxsa 			flags &= ~CR_RECURSE_DIRS;
14590f9bb03Sxsa 			break;
14690f9bb03Sxsa 		case 'R':
147bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
14890f9bb03Sxsa 			break;
1490ab67f86Sxsa 		case 'r':
1500ab67f86Sxsa 			cvs_specified_tag = optarg;
1510ab67f86Sxsa 			break;
15290f9bb03Sxsa 		default:
15390f9bb03Sxsa 			fatal("%s", cvs_cmd_export.cmd_synopsis);
15490f9bb03Sxsa 		}
15590f9bb03Sxsa 	}
15690f9bb03Sxsa 
15790f9bb03Sxsa 	argc -= optind;
15890f9bb03Sxsa 	argv += optind;
15990f9bb03Sxsa 
1605e1effbaStobias 	if (cvs_specified_tag == NULL)
1615e1effbaStobias 		fatal("must specify a tag or date");
1625e1effbaStobias 
16390f9bb03Sxsa 	if (argc == 0)
16490f9bb03Sxsa 		fatal("%s", cvs_cmd_export.cmd_synopsis);
16590f9bb03Sxsa 
16690f9bb03Sxsa 	checkout_check_repository(argc, argv);
16790f9bb03Sxsa 
16890f9bb03Sxsa 	return (0);
16990f9bb03Sxsa }
17090f9bb03Sxsa 
17190f9bb03Sxsa static void
17290f9bb03Sxsa checkout_check_repository(int argc, char **argv)
17390f9bb03Sxsa {
17459b603d6Sxsa 	int i;
1757fca5395Sjoris 	char *wdir, *d;
1765219eee5Sjoris 	struct cvs_recursion cr;
177bf6291b7Sjoris 	struct module_checkout *mc;
1787fca5395Sjoris 	struct cvs_ignpat *ip;
1797fca5395Sjoris 	struct cvs_filelist *fl, *nxt;
1807fca5395Sjoris 	char repo[MAXPATHLEN], fpath[MAXPATHLEN], *f[1];
1815219eee5Sjoris 
18246bff1c6Stobias 	build_dirs = print_stdout ? 0 : 1;
18346bff1c6Stobias 
1845219eee5Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1855219eee5Sjoris 		cvs_client_connect_to_server();
1865219eee5Sjoris 
1875dd120b0Sjoris 		if (cvs_specified_tag != NULL)
1885dd120b0Sjoris 			cvs_client_send_request("Argument -r%s",
1895dd120b0Sjoris 			    cvs_specified_tag);
1905219eee5Sjoris 		if (reset_stickies == 1)
1915219eee5Sjoris 			cvs_client_send_request("Argument -A");
1925219eee5Sjoris 
193*37fdff3fStobias 		if (kflag)
194*37fdff3fStobias 			cvs_client_send_request("Argument -k%s", koptstr);
195*37fdff3fStobias 
1968dead7d9Sjoris 		if (dflag != NULL)
1978dead7d9Sjoris 			cvs_client_send_request("Argument -d%s", dflag);
1988dead7d9Sjoris 
19925e75a48Sxsa 		if (!(flags & CR_RECURSE_DIRS))
20025e75a48Sxsa 			cvs_client_send_request("Argument -l");
20125e75a48Sxsa 
2025219eee5Sjoris 		if (cvs_cmdop == CVS_OP_CHECKOUT && prune_dirs == 1)
2035219eee5Sjoris 			cvs_client_send_request("Argument -P");
2045219eee5Sjoris 
2057ac78d1dSjoris 		if (print_stdout == 1)
2067ac78d1dSjoris 			cvs_client_send_request("Argument -p");
2077ac78d1dSjoris 
2085219eee5Sjoris 		cr.enterdir = NULL;
2095219eee5Sjoris 		cr.leavedir = NULL;
210c634bf10Stobias 		if (print_stdout)
21146bff1c6Stobias 			cr.fileproc = NULL;
212c634bf10Stobias 		else
213c634bf10Stobias 			cr.fileproc = cvs_client_sendfile;
214c634bf10Stobias 
215c634bf10Stobias 		flags &= ~CR_REPO;
2165219eee5Sjoris 		cr.flags = flags;
2175219eee5Sjoris 
2185e1effbaStobias 		if (cvs_cmdop != CVS_OP_EXPORT)
2195219eee5Sjoris 			cvs_file_run(argc, argv, &cr);
2205219eee5Sjoris 
2215219eee5Sjoris 		cvs_client_send_files(argv, argc);
2225219eee5Sjoris 		cvs_client_senddir(".");
2235219eee5Sjoris 
2245219eee5Sjoris 		cvs_client_send_request("%s",
2255219eee5Sjoris 		    (cvs_cmdop == CVS_OP_CHECKOUT) ? "co" : "export");
2265219eee5Sjoris 
2275219eee5Sjoris 		cvs_client_get_responses();
2285219eee5Sjoris 
2295219eee5Sjoris 		return;
2305219eee5Sjoris 	}
23190f9bb03Sxsa 
23251ef6581Sjoris 	cvs_directory_tag = cvs_specified_tag;
23351ef6581Sjoris 
2343ad3fb45Sjoris 	for (i = 0; i < argc; i++) {
235bf6291b7Sjoris 		mc = cvs_module_lookup(argv[i]);
236bf6291b7Sjoris 		current_module = mc;
237fc22209eSjfb 
2387fca5395Sjoris 		TAILQ_FOREACH(fl, &(mc->mc_ignores), flist)
2397fca5395Sjoris 			cvs_file_ignore(fl->file_path, &checkout_ign_pats);
2407fca5395Sjoris 
2417fca5395Sjoris 		TAILQ_FOREACH(fl, &(mc->mc_modules), flist) {
24275bebbccSjoris 			(void)xsnprintf(repo, sizeof(repo), "%s/%s",
2437fca5395Sjoris 			    current_cvsroot->cr_dir, fl->file_path);
24475bebbccSjoris 
245dbeb8705Sjoris 			if (!(mc->mc_flags & MODULE_ALIAS) || dflag != NULL)
2467fca5395Sjoris 				module_repo_root = fl->file_path;
247bf6291b7Sjoris 
248bf659b2eSjoris 			if (mc->mc_flags & MODULE_NORECURSE)
249bf659b2eSjoris 				flags &= ~CR_RECURSE_DIRS;
250bf659b2eSjoris 
251dbeb8705Sjoris 			if (dflag != NULL)
252dbeb8705Sjoris 				wdir = dflag;
253f748429cSjoris 			else if (mc->mc_flags & MODULE_ALIAS)
254f748429cSjoris 				wdir = fl->file_path;
255dbeb8705Sjoris 			else
256f748429cSjoris 				wdir = mc->mc_name;
257dbeb8705Sjoris 
2587fca5395Sjoris 			switch (checkout_classify(repo, fl->file_path)) {
259b0d656e9Stobias 			case CVS_FILE:
2601526ef95Stobias 				cr.fileproc = cvs_update_local;
2611526ef95Stobias 				cr.flags = flags;
26246bff1c6Stobias 
2637fca5395Sjoris 				if (!(mc->mc_flags & MODULE_ALIAS)) {
2647fca5395Sjoris 					module_repo_root =
2657fca5395Sjoris 					    dirname(fl->file_path);
2667fca5395Sjoris 					d = wdir;
2677fca5395Sjoris 					(void)xsnprintf(fpath, sizeof(fpath),
2687fca5395Sjoris 					    "%s/%s", d,
2697fca5395Sjoris 					    basename(fl->file_path));
2707fca5395Sjoris 				} else {
2717fca5395Sjoris 					d = dirname(wdir);
2727fca5395Sjoris 					strlcpy(fpath, fl->file_path,
2737fca5395Sjoris 					    sizeof(fpath));
2747fca5395Sjoris 				}
2757fca5395Sjoris 
27646bff1c6Stobias 				if (build_dirs == 1)
2777fca5395Sjoris 					cvs_mkpath(d, cvs_specified_tag);
2787fca5395Sjoris 
2797fca5395Sjoris 				f[0] = fpath;
2807fca5395Sjoris 				cvs_file_run(1, f, &cr);
281b0d656e9Stobias 				break;
282b0d656e9Stobias 			case CVS_DIR:
28346bff1c6Stobias 				if (build_dirs == 1)
284dbeb8705Sjoris 					cvs_mkpath(wdir, cvs_specified_tag);
285dbeb8705Sjoris 				checkout_repository(repo, wdir);
286b0d656e9Stobias 				break;
287b0d656e9Stobias 			default:
288b0d656e9Stobias 				break;
2893ad3fb45Sjoris 			}
2907fca5395Sjoris 		}
2917fca5395Sjoris 
2927fca5395Sjoris 		if (mc->mc_canfree == 1) {
2937fca5395Sjoris 			for (fl = TAILQ_FIRST(&(mc->mc_modules));
2947fca5395Sjoris 			    fl != TAILQ_END(&(mc->mc_modules)); fl = nxt) {
2957fca5395Sjoris 				nxt = TAILQ_NEXT(fl, flist);
2967fca5395Sjoris 				TAILQ_REMOVE(&(mc->mc_modules), fl, flist);
2977fca5395Sjoris 				xfree(fl->file_path);
2987fca5395Sjoris 				xfree(fl);
2997fca5395Sjoris 			}
3007fca5395Sjoris 		}
3017fca5395Sjoris 
3027fca5395Sjoris 		while ((ip = TAILQ_FIRST(&checkout_ign_pats)) != NULL) {
3037fca5395Sjoris 			TAILQ_REMOVE(&checkout_ign_pats, ip, ip_list);
3047fca5395Sjoris 			xfree(ip);
3057fca5395Sjoris 		}
306bf6291b7Sjoris 
307bf6291b7Sjoris 		xfree(mc);
30862f911b4Sjfb 	}
309b0d656e9Stobias }
310b0d656e9Stobias 
311b0d656e9Stobias static int
312b0d656e9Stobias checkout_classify(const char *repo, const char *arg)
313b0d656e9Stobias {
314b0d656e9Stobias 	char *d, *f, fpath[MAXPATHLEN];
315b0d656e9Stobias 	struct stat sb;
316b0d656e9Stobias 
317b0d656e9Stobias 	if (stat(repo, &sb) == 0) {
3187fca5395Sjoris 		if (S_ISDIR(sb.st_mode))
319b0d656e9Stobias 			return CVS_DIR;
320b0d656e9Stobias 	}
321b0d656e9Stobias 
322b0d656e9Stobias 	d = dirname(repo);
323b0d656e9Stobias 	f = basename(repo);
324b0d656e9Stobias 
325b0d656e9Stobias 	(void)xsnprintf(fpath, sizeof(fpath), "%s/%s%s", d, f, RCS_FILE_EXT);
326b0d656e9Stobias 	if (stat(fpath, &sb) == 0) {
327b0d656e9Stobias 		if (!S_ISREG(sb.st_mode)) {
328b0d656e9Stobias 			cvs_log(LP_ERR, "ignoring %s: not a regular file", arg);
329b0d656e9Stobias 			return 0;
330b0d656e9Stobias 		}
331b0d656e9Stobias 		return CVS_FILE;
332b0d656e9Stobias 	}
333b0d656e9Stobias 
334b0d656e9Stobias 	(void)xsnprintf(fpath, sizeof(fpath), "%s/%s/%s%s",
335b0d656e9Stobias 	    d, CVS_PATH_ATTIC, f, RCS_FILE_EXT);
336b0d656e9Stobias 	if (stat(fpath, &sb) == 0) {
337b0d656e9Stobias 		if (!S_ISREG(sb.st_mode)) {
338b0d656e9Stobias 			cvs_log(LP_ERR, "ignoring %s: not a regular file", arg);
339b0d656e9Stobias 			return 0;
340b0d656e9Stobias 		}
341b0d656e9Stobias 		return CVS_FILE;
342b0d656e9Stobias 	}
343b0d656e9Stobias 
344b0d656e9Stobias 	cvs_log(LP_ERR, "cannot find module `%s' - ignored", arg);
345b0d656e9Stobias 	return 0;
346b0d656e9Stobias }
34708f90673Sjfb 
3483ad3fb45Sjoris static void
3493ad3fb45Sjoris checkout_repository(const char *repobase, const char *wdbase)
35016cfc147Sjoris {
3513ad3fb45Sjoris 	struct cvs_flisthead fl, dl;
3523ad3fb45Sjoris 	struct cvs_recursion cr;
3535191afffSjoris 
3543ad3fb45Sjoris 	TAILQ_INIT(&fl);
3553ad3fb45Sjoris 	TAILQ_INIT(&dl);
3565191afffSjoris 
357a76e09e0Sxsa 	cvs_history_add((cvs_cmdop == CVS_OP_CHECKOUT) ?
358a76e09e0Sxsa 	    CVS_HISTORY_CHECKOUT : CVS_HISTORY_EXPORT, NULL, wdbase);
3593901dfa5Sjoris 
360f331ff59Stobias 	if (print_stdout) {
361f331ff59Stobias 		cr.enterdir = NULL;
362f331ff59Stobias 		cr.leavedir = NULL;
363f331ff59Stobias 	} else {
3643ad3fb45Sjoris 		cr.enterdir = cvs_update_enterdir;
365fec3e093Stobias 		cr.leavedir = prune_dirs ? cvs_update_leavedir : NULL;
366f331ff59Stobias 	}
367bc5d89feSjoris 	cr.fileproc = cvs_update_local;
368a2f23aefSxsa 	cr.flags = flags;
369e4276007Sjfb 
3703ad3fb45Sjoris 	cvs_repository_lock(repobase);
37157448823Stobias 	cvs_repository_getdir(repobase, wdbase, &fl, &dl,
37257448823Stobias 	    flags & CR_RECURSE_DIRS ? 1 : 0);
37350b634f9Sjoris 
3743ad3fb45Sjoris 	cvs_file_walklist(&fl, &cr);
3753ad3fb45Sjoris 	cvs_file_freelist(&fl);
376e4276007Sjfb 
3773ad3fb45Sjoris 	cvs_repository_unlock(repobase);
37850b634f9Sjoris 
3793ad3fb45Sjoris 	cvs_file_walklist(&dl, &cr);
3803ad3fb45Sjoris 	cvs_file_freelist(&dl);
38150b634f9Sjoris }
38250b634f9Sjoris 
383f9872b43Sjoris void
3845d320860Sjoris cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, char *tag, int co_flags)
3855191afffSjoris {
386*37fdff3fStobias 	int cf_kflag, oflags, exists;
3873ad3fb45Sjoris 	time_t rcstime;
3883ad3fb45Sjoris 	CVSENTRIES *ent;
3893ad3fb45Sjoris 	struct timeval tv[2];
390828954c0Sjoris 	char *tosend;
3915dd120b0Sjoris 	char template[MAXPATHLEN], entry[CVS_ENT_MAXLINELEN];
3924967382bSxsa 	char kbuf[8], sticky[CVS_REV_BUFSZ], rev[CVS_REV_BUFSZ];
3930a7da307Sxsa 	char timebuf[CVS_TIME_BUFSZ], tbuf[CVS_TIME_BUFSZ];
3945191afffSjoris 
395828954c0Sjoris 	exists = 0;
396828954c0Sjoris 	tosend = NULL;
39751ef6581Sjoris 
39851ef6581Sjoris 	if (!(co_flags & CO_REMOVE))
3993ad3fb45Sjoris 		rcsnum_tostr(rnum, rev, sizeof(rev));
4005191afffSjoris 
4019fac60a5Sjoris 	cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d) -> %s",
402a2f23aefSxsa 	    cf->file_path, rev, co_flags,
4039fac60a5Sjoris 	    (cvs_server_active) ? "to client" : "to disk");
4045191afffSjoris 
405a2f23aefSxsa 	if (co_flags & CO_DUMP) {
40687d368dcStobias 		rcs_rev_write_fd(cf->file_rcs, rnum, STDOUT_FILENO, 0);
407b17342c4Sreyk 		return;
408b17342c4Sreyk 	}
409b17342c4Sreyk 
4109fac60a5Sjoris 	if (cvs_server_active == 0) {
411828954c0Sjoris 		if (!(co_flags & CO_MERGE)) {
4123ad3fb45Sjoris 			oflags = O_WRONLY | O_TRUNC;
4133ad3fb45Sjoris 			if (cf->fd != -1) {
4143ad3fb45Sjoris 				exists = 1;
4153ad3fb45Sjoris 				(void)close(cf->fd);
4163ad3fb45Sjoris 			} else  {
4173ad3fb45Sjoris 				oflags |= O_CREAT;
4183ad3fb45Sjoris 			}
4193ad3fb45Sjoris 
4203ad3fb45Sjoris 			cf->fd = open(cf->file_path, oflags);
4213ad3fb45Sjoris 			if (cf->fd == -1)
422828954c0Sjoris 				fatal("cvs_checkout_file: open: %s",
423828954c0Sjoris 				    strerror(errno));
4243ad3fb45Sjoris 
42587d368dcStobias 			rcs_rev_write_fd(cf->file_rcs, rnum, cf->fd, 0);
426828954c0Sjoris 		} else {
427828954c0Sjoris 			cvs_merge_file(cf, 1);
428828954c0Sjoris 		}
4293ad3fb45Sjoris 
4303ad3fb45Sjoris 		if (fchmod(cf->fd, 0644) == -1)
4313ad3fb45Sjoris 			fatal("cvs_checkout_file: fchmod: %s", strerror(errno));
4323ad3fb45Sjoris 
433828954c0Sjoris 		if ((exists == 0) && (cf->file_ent == NULL) &&
434828954c0Sjoris 		    !(co_flags & CO_MERGE))
4353ad3fb45Sjoris 			rcstime = rcs_rev_getdate(cf->file_rcs, rnum);
43647e5fe63Sjoris 		else
4373ad3fb45Sjoris 			time(&rcstime);
4383ad3fb45Sjoris 
4393ad3fb45Sjoris 		tv[0].tv_sec = rcstime;
4403ad3fb45Sjoris 		tv[0].tv_usec = 0;
4413ad3fb45Sjoris 		tv[1] = tv[0];
4423ad3fb45Sjoris 		if (futimes(cf->fd, tv) == -1)
4439fac60a5Sjoris 			fatal("cvs_checkout_file: futimes: %s",
4449fac60a5Sjoris 			    strerror(errno));
4459fac60a5Sjoris 	} else {
4469fac60a5Sjoris 		time(&rcstime);
4479fac60a5Sjoris 	}
4483ad3fb45Sjoris 
44947e5fe63Sjoris 	asctime_r(gmtime(&rcstime), tbuf);
4502820b891Stobias 	tbuf[strcspn(tbuf, "\n")] = '\0';
451f9872b43Sjoris 
452a2f23aefSxsa 	if (co_flags & CO_MERGE) {
453c486465dSxsa 		(void)xsnprintf(timebuf, sizeof(timebuf), "Result of merge+%s",
454f9872b43Sjoris 		    tbuf);
455f9872b43Sjoris 	} else {
456f9872b43Sjoris 		strlcpy(timebuf, tbuf, sizeof(timebuf));
457f9872b43Sjoris 	}
4583ad3fb45Sjoris 
459c486465dSxsa 	if (co_flags & CO_SETSTICKY)
4605d320860Sjoris 		if (tag != NULL)
4610bf9adf7Sxsa 			(void)xsnprintf(sticky, sizeof(sticky), "T%s", tag);
462ca2dc546Sniallo 		else
4630bf9adf7Sxsa 			(void)xsnprintf(sticky, sizeof(sticky), "T%s", rev);
464ebd8626fStobias 	else if (!reset_stickies && cf->file_ent != NULL &&
465ebd8626fStobias 	    cf->file_ent->ce_tag != NULL)
4660bf9adf7Sxsa 		(void)xsnprintf(sticky, sizeof(sticky), "T%s",
467ebd8626fStobias 		    cf->file_ent->ce_tag);
468c486465dSxsa 	else
4690bf9adf7Sxsa 		sticky[0] = '\0';
4706fba94e7Sjoris 
47156f996a2Sxsa 	kbuf[0] = '\0';
472*37fdff3fStobias 	if (cf->file_rcs->rf_expand != NULL) {
473*37fdff3fStobias 		cf_kflag = rcs_kflag_get(cf->file_rcs->rf_expand);
474*37fdff3fStobias 		if (kflag || cf_kflag != RCS_KWEXP_DEFAULT)
475c486465dSxsa 			(void)xsnprintf(kbuf, sizeof(kbuf),
47656f996a2Sxsa 			    "-k%s", cf->file_rcs->rf_expand);
477*37fdff3fStobias 	} else if (!reset_stickies && cf->file_ent != NULL) {
478*37fdff3fStobias 		if (cf->file_ent->ce_opts != NULL)
479*37fdff3fStobias 			strlcpy(kbuf, cf->file_ent->ce_opts, sizeof(kbuf));
48056f996a2Sxsa 	}
48156f996a2Sxsa 
482c486465dSxsa 	(void)xsnprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s/%s/%s",
4830bf9adf7Sxsa 	    cf->file_name, rev, timebuf, kbuf, sticky);
4843ad3fb45Sjoris 
4859fac60a5Sjoris 	if (cvs_server_active == 0) {
4865e1effbaStobias 		if (!(co_flags & CO_REMOVE) && cvs_cmdop != CVS_OP_EXPORT) {
4873ad3fb45Sjoris 			ent = cvs_ent_open(cf->file_wd);
4883ad3fb45Sjoris 			cvs_ent_add(ent, entry);
4893ad3fb45Sjoris 			cvs_ent_close(ent, ENT_SYNC);
49052979273Sjoris 		}
4919fac60a5Sjoris 	} else {
492828954c0Sjoris 		if (co_flags & CO_MERGE) {
493828954c0Sjoris 			cvs_merge_file(cf, 1);
494828954c0Sjoris 			tosend = cf->file_path;
495828954c0Sjoris 		}
496828954c0Sjoris 
497a2f23aefSxsa 		if (co_flags & CO_COMMIT)
498408908afSjoris 			cvs_server_update_entry("Checked-in", cf);
499828954c0Sjoris 		else if (co_flags & CO_MERGE)
500828954c0Sjoris 			cvs_server_update_entry("Merged", cf);
50152979273Sjoris 		else if (co_flags & CO_REMOVE)
50252979273Sjoris 			cvs_server_update_entry("Removed", cf);
503408908afSjoris 		else
504408908afSjoris 			cvs_server_update_entry("Updated", cf);
5057fffa3ccSjoris 
50652979273Sjoris 		if (!(co_flags & CO_REMOVE))
5079fac60a5Sjoris 			cvs_remote_output(entry);
5087fffa3ccSjoris 
50952979273Sjoris 		if (!(co_flags & CO_COMMIT) && !(co_flags & CO_REMOVE)) {
510828954c0Sjoris 			if (!(co_flags & CO_MERGE)) {
511c486465dSxsa 				(void)xsnprintf(template, MAXPATHLEN,
512bb510330Sjoris 				    "%s/checkout.XXXXXXXXXX", cvs_tmpdir);
513c486465dSxsa 
514828954c0Sjoris 				rcs_rev_write_stmp(cf->file_rcs, rnum,
515828954c0Sjoris 				    template, 0);
516828954c0Sjoris 				tosend = template;
517828954c0Sjoris 			}
5189fac60a5Sjoris 
519828954c0Sjoris 			cvs_remote_send_file(tosend);
520828954c0Sjoris 
521828954c0Sjoris 			if (!(co_flags & CO_MERGE)) {
522828954c0Sjoris 				(void)unlink(template);
523828954c0Sjoris 				cvs_worklist_run(&temp_files,
524828954c0Sjoris 				    cvs_worklist_unlink);
525828954c0Sjoris 			}
5267fffa3ccSjoris 		}
5279fac60a5Sjoris 	}
5283ad3fb45Sjoris }
529