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