xref: /openbsd-src/usr.bin/cvs/checkout.c (revision 94fd4554194a14f126fba33b837cc68a1df42468)
1 /*	$OpenBSD: checkout.c,v 1.92 2007/02/22 06:42:09 otto Exp $	*/
2 /*
3  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/dirent.h>
20 #include <sys/stat.h>
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "cvs.h"
28 #include "diff.h"
29 #include "remote.h"
30 
31 static void checkout_check_repository(int, char **);
32 static void checkout_repository(const char *, const char *);
33 
34 extern int prune_dirs;
35 extern int build_dirs;
36 extern int reset_stickies;
37 
38 static int flags = CR_REPO | CR_RECURSE_DIRS;
39 
40 struct cvs_cmd cvs_cmd_checkout = {
41 	CVS_OP_CHECKOUT, 0, "checkout",
42 	{ "co", "get" },
43 	"Checkout a working copy of a repository",
44 	"[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] "
45 	"[-t id] module ...",
46 	"AcD:d:fj:k:lNnPpRr:st:",
47 	NULL,
48 	cvs_checkout
49 };
50 
51 struct cvs_cmd cvs_cmd_export = {
52 	CVS_OP_EXPORT, 0, "export",
53 	{ "exp", "ex" },
54 	"Export sources from CVS, similar to checkout",
55 	"[-flNnR] [-d dir] [-k mode] -D date | -r rev module ...",
56 	"D:d:k:flNnRr:",
57 	NULL,
58 	cvs_export
59 };
60 
61 int
62 cvs_checkout(int argc, char **argv)
63 {
64 	int ch;
65 
66 	while ((ch = getopt(argc, argv, cvs_cmd_checkout.cmd_opts)) != -1) {
67 		switch (ch) {
68 		case 'A':
69 			reset_stickies = 1;
70 			break;
71 		case 'l':
72 			flags &= ~CR_RECURSE_DIRS;
73 			break;
74 		case 'P':
75 			prune_dirs = 1;
76 			break;
77 		case 'R':
78 			break;
79 		default:
80 			fatal("%s", cvs_cmd_checkout.cmd_synopsis);
81 		}
82 	}
83 
84 	argc -= optind;
85 	argv += optind;
86 
87 	if (argc == 0)
88 		fatal("%s", cvs_cmd_checkout.cmd_synopsis);
89 
90 	checkout_check_repository(argc, argv);
91 
92 	return (0);
93 }
94 
95 int
96 cvs_export(int argc, char **argv)
97 {
98 	int ch;
99 
100 	prune_dirs = 1;
101 
102 	while ((ch = getopt(argc, argv, cvs_cmd_export.cmd_opts)) != -1) {
103 		switch (ch) {
104 		case 'l':
105 			flags &= ~CR_RECURSE_DIRS;
106 			break;
107 		case 'R':
108 			break;
109 		default:
110 			fatal("%s", cvs_cmd_export.cmd_synopsis);
111 		}
112 	}
113 
114 	argc -= optind;
115 	argv += optind;
116 
117 	if (argc == 0)
118 		fatal("%s", cvs_cmd_export.cmd_synopsis);
119 
120 	checkout_check_repository(argc, argv);
121 
122 	return (0);
123 }
124 
125 static void
126 checkout_check_repository(int argc, char **argv)
127 {
128 	int i;
129 	char repo[MAXPATHLEN];
130 	struct stat st;
131 	struct cvs_recursion cr;
132 
133 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
134 		cvs_client_connect_to_server();
135 
136 		if (reset_stickies == 1)
137 			cvs_client_send_request("Argument -A");
138 
139 		if (!(flags & CR_RECURSE_DIRS))
140 			cvs_client_send_request("Argument -l");
141 
142 		if (cvs_cmdop == CVS_OP_CHECKOUT && prune_dirs == 1)
143 			cvs_client_send_request("Argument -P");
144 
145 		cr.enterdir = NULL;
146 		cr.leavedir = NULL;
147 		cr.fileproc = cvs_client_sendfile;
148 		cr.flags = flags;
149 
150 		cvs_file_run(argc, argv, &cr);
151 
152 		cvs_client_send_files(argv, argc);
153 		cvs_client_senddir(".");
154 
155 		cvs_client_send_request("%s",
156 		    (cvs_cmdop == CVS_OP_CHECKOUT) ? "co" : "export");
157 
158 		cvs_client_get_responses();
159 
160 		return;
161 	}
162 
163 	for (i = 0; i < argc; i++) {
164 		(void)xsnprintf(repo, sizeof(repo), "%s/%s",
165 		    current_cvsroot->cr_dir, argv[i]);
166 
167 		if (stat(repo, &st) == -1) {
168 			cvs_log(LP_ERR, "cannot find module `%s' - ignored",
169 			    argv[i]);
170 			continue;
171 		}
172 		cvs_mkpath(argv[i]);
173 		checkout_repository(repo, argv[i]);
174 	}
175 }
176 
177 static void
178 checkout_repository(const char *repobase, const char *wdbase)
179 {
180 	struct cvs_flisthead fl, dl;
181 	struct cvs_recursion cr;
182 
183 	TAILQ_INIT(&fl);
184 	TAILQ_INIT(&dl);
185 
186 	build_dirs = 1;
187 	cr.enterdir = cvs_update_enterdir;
188 	cr.leavedir = cvs_update_leavedir;
189 	cr.fileproc = cvs_update_local;
190 	cr.flags = flags;
191 
192 	cvs_repository_lock(repobase);
193 	cvs_repository_getdir(repobase, wdbase, &fl, &dl, 1);
194 
195 	cvs_file_walklist(&fl, &cr);
196 	cvs_file_freelist(&fl);
197 
198 	cvs_repository_unlock(repobase);
199 
200 	cvs_file_walklist(&dl, &cr);
201 	cvs_file_freelist(&dl);
202 }
203 
204 void
205 cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int co_flags)
206 {
207 	int kflag, oflags, exists;
208 	time_t rcstime;
209 	CVSENTRIES *ent;
210 	struct timeval tv[2];
211 	char *tosend;
212 	char template[MAXPATHLEN], *p, entry[CVS_ENT_MAXLINELEN], rev[16];
213 	char timebuf[64], kbuf[8], tbuf[32], stickytag[32];
214 
215 	exists = 0;
216 	tosend = NULL;
217 	rcsnum_tostr(rnum, rev, sizeof(rev));
218 
219 	cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d) -> %s",
220 	    cf->file_path, rev, co_flags,
221 	    (cvs_server_active) ? "to client" : "to disk");
222 
223 	if (co_flags & CO_DUMP) {
224 		if (cvs_server_active) {
225 			cvs_printf("dump file %s to client\n", cf->file_path);
226 		} else {
227 			rcs_rev_write_fd(cf->file_rcs, rnum,
228 			    STDOUT_FILENO, 1);
229 		}
230 
231 		return;
232 	}
233 
234 	if (cvs_server_active == 0) {
235 		if (!(co_flags & CO_MERGE)) {
236 			oflags = O_WRONLY | O_TRUNC;
237 			if (cf->fd != -1) {
238 				exists = 1;
239 				(void)close(cf->fd);
240 			} else  {
241 				oflags |= O_CREAT;
242 			}
243 
244 			cf->fd = open(cf->file_path, oflags);
245 			if (cf->fd == -1)
246 				fatal("cvs_checkout_file: open: %s",
247 				    strerror(errno));
248 
249 			rcs_rev_write_fd(cf->file_rcs, rnum, cf->fd, 1);
250 		} else {
251 			cvs_merge_file(cf, 1);
252 		}
253 
254 		if (fchmod(cf->fd, 0644) == -1)
255 			fatal("cvs_checkout_file: fchmod: %s", strerror(errno));
256 
257 		if ((exists == 0) && (cf->file_ent == NULL) &&
258 		    !(co_flags & CO_MERGE))
259 			rcstime = rcs_rev_getdate(cf->file_rcs, rnum);
260 		else
261 			time(&rcstime);
262 
263 		tv[0].tv_sec = rcstime;
264 		tv[0].tv_usec = 0;
265 		tv[1] = tv[0];
266 		if (futimes(cf->fd, tv) == -1)
267 			fatal("cvs_checkout_file: futimes: %s",
268 			    strerror(errno));
269 	} else {
270 		time(&rcstime);
271 	}
272 
273 	asctime_r(gmtime(&rcstime), tbuf);
274 	if (tbuf[strlen(tbuf) - 1] == '\n')
275 		tbuf[strlen(tbuf) - 1] = '\0';
276 
277 	if (co_flags & CO_MERGE) {
278 		(void)xsnprintf(timebuf, sizeof(timebuf), "Result of merge+%s",
279 		    tbuf);
280 	} else {
281 		strlcpy(timebuf, tbuf, sizeof(timebuf));
282 	}
283 
284 	if (co_flags & CO_SETSTICKY)
285 		(void)xsnprintf(stickytag, sizeof(stickytag), "T%s", rev);
286 	else
287 		stickytag[0] = '\0';
288 
289 	kbuf[0] = '\0';
290 	if (cf->file_ent != NULL) {
291 		if (cf->file_ent->ce_opts != NULL)
292 			strlcpy(kbuf, cf->file_ent->ce_opts, sizeof(kbuf));
293 	} else if (cf->file_rcs->rf_expand != NULL) {
294 		kflag = rcs_kflag_get(cf->file_rcs->rf_expand);
295 		if (!(kflag & RCS_KWEXP_DEFAULT))
296 			(void)xsnprintf(kbuf, sizeof(kbuf),
297 			    "-k%s", cf->file_rcs->rf_expand);
298 	}
299 
300 	(void)xsnprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s/%s/%s",
301 	    cf->file_name, rev, timebuf, kbuf, stickytag);
302 
303 	if (cvs_server_active == 0) {
304 		ent = cvs_ent_open(cf->file_wd);
305 		cvs_ent_add(ent, entry);
306 		cvs_ent_close(ent, ENT_SYNC);
307 	} else {
308 		if ((p = strrchr(cf->file_rpath, ',')) != NULL)
309 			*p = '\0';
310 
311 		if (co_flags & CO_MERGE) {
312 			cvs_merge_file(cf, 1);
313 			tosend = cf->file_path;
314 		}
315 
316 		if (co_flags & CO_COMMIT)
317 			cvs_server_update_entry("Checked-in", cf);
318 		else if (co_flags & CO_MERGE)
319 			cvs_server_update_entry("Merged", cf);
320 		else
321 			cvs_server_update_entry("Updated", cf);
322 
323 		cvs_remote_output(entry);
324 
325 		if (!(co_flags & CO_COMMIT)) {
326 			if (!(co_flags & CO_MERGE)) {
327 				(void)xsnprintf(template, MAXPATHLEN,
328 				    "%s/checkout.XXXXXXXXXX", cvs_tmpdir);
329 
330 				rcs_rev_write_stmp(cf->file_rcs, rnum,
331 				    template, 0);
332 				tosend = template;
333 			}
334 
335 			cvs_remote_send_file(tosend);
336 
337 			if (!(co_flags & CO_MERGE)) {
338 				(void)unlink(template);
339 				cvs_worklist_run(&temp_files,
340 				    cvs_worklist_unlink);
341 			}
342 		}
343 
344 		if (p != NULL)
345 			*p = ',';
346 	}
347 }
348