xref: /openbsd-src/usr.bin/cvs/update.c (revision 799f675f6700f14e59124f9825c723e9f2ce19dc)
1 /*	$OpenBSD: update.c,v 1.90 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 int	prune_dirs = 0;
26 int	print = 0;
27 int	build_dirs = 0;
28 int	reset_stickies = 0;
29 static char *tag = NULL;
30 
31 static void update_clear_conflict(struct cvs_file *);
32 
33 struct cvs_cmd cvs_cmd_update = {
34 	CVS_OP_UPDATE, 0, "update",
35 	{ "up", "upd" },
36 	"Bring work tree in sync with repository",
37 	"[-ACdflPpR] [-D date | -r rev] [-I ign] [-j rev] [-k mode] "
38 	"[-t id] ...",
39 	"ACD:dfI:j:k:lPpQqRr:t:",
40 	NULL,
41 	cvs_update
42 };
43 
44 int
45 cvs_update(int argc, char **argv)
46 {
47 	int ch;
48 	char *arg = ".";
49 	int flags;
50 	struct cvs_recursion cr;
51 
52 	flags = CR_RECURSE_DIRS;
53 
54 	while ((ch = getopt(argc, argv, cvs_cmd_update.cmd_opts)) != -1) {
55 		switch (ch) {
56 		case 'A':
57 			reset_stickies = 1;
58 			break;
59 		case 'C':
60 		case 'D':
61 			tag = optarg;
62 			break;
63 		case 'd':
64 			build_dirs = 1;
65 			break;
66 		case 'f':
67 			break;
68 		case 'I':
69 			break;
70 		case 'j':
71 			break;
72 		case 'k':
73 			break;
74 		case 'l':
75 			flags &= ~CR_RECURSE_DIRS;
76 			break;
77 		case 'P':
78 			prune_dirs = 1;
79 			break;
80 		case 'p':
81 			print = 1;
82 			cvs_noexec = 1;
83 			break;
84 		case 'Q':
85 		case 'q':
86 			break;
87 		case 'R':
88 			break;
89 		case 'r':
90 			tag = optarg;
91 			break;
92 		default:
93 			fatal("%s", cvs_cmd_update.cmd_synopsis);
94 		}
95 	}
96 
97 	argc -= optind;
98 	argv += optind;
99 
100 	if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
101 		cr.enterdir = cvs_update_enterdir;
102 		cr.leavedir = cvs_update_leavedir;
103 		cr.fileproc = cvs_update_local;
104 		flags |= CR_REPO;
105 	} else {
106 		cvs_client_connect_to_server();
107 		if (reset_stickies)
108 			cvs_client_send_request("Argument -A");
109 		if (build_dirs)
110 			cvs_client_send_request("Argument -d");
111 		if (!(flags & CR_RECURSE_DIRS))
112 			cvs_client_send_request("Argument -l");
113 		if (prune_dirs)
114 			cvs_client_send_request("Argument -P");
115 		if (print)
116 			cvs_client_send_request("Argument -p");
117 
118 		cr.enterdir = NULL;
119 		cr.leavedir = NULL;
120 		cr.fileproc = cvs_client_sendfile;
121 	}
122 
123 	cr.flags = flags;
124 
125 	if (argc > 0)
126 		cvs_file_run(argc, argv, &cr);
127 	else
128 		cvs_file_run(1, &arg, &cr);
129 
130 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
131 		cvs_client_send_files(argv, argc);
132 		cvs_client_senddir(".");
133 		cvs_client_send_request("update");
134 		cvs_client_get_responses();
135 	}
136 
137 	return (0);
138 }
139 
140 void
141 cvs_update_enterdir(struct cvs_file *cf)
142 {
143 	int l;
144 	char *entry;
145 	CVSENTRIES *entlist;
146 
147 	cvs_log(LP_TRACE, "cvs_update_enterdir(%s)", cf->file_path);
148 
149 	cvs_file_classify(cf, NULL, 0);
150 
151 	if (cf->file_status == DIR_CREATE && build_dirs == 1) {
152 		cvs_mkpath(cf->file_path);
153 		if ((cf->fd = open(cf->file_path, O_RDONLY)) == -1)
154 			fatal("cvs_update_enterdir: `%s': %s",
155 			    cf->file_path, strerror(errno));
156 
157 		entry = xmalloc(CVS_ENT_MAXLINELEN);
158 		l = snprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////",
159 		    cf->file_name);
160 		if (l == -1 || l >= CVS_ENT_MAXLINELEN)
161 			fatal("cvs_update_enterdir: overflow");
162 
163 		entlist = cvs_ent_open(cf->file_wd);
164 		cvs_ent_add(entlist, entry);
165 		cvs_ent_close(entlist, ENT_SYNC);
166 		xfree(entry);
167 	} else if (cf->file_status == DIR_CREATE && build_dirs == 0) {
168 		cf->file_status = FILE_SKIP;
169 	}
170 }
171 
172 void
173 cvs_update_leavedir(struct cvs_file *cf)
174 {
175 	long base;
176 	int nbytes;
177 	int isempty;
178 	size_t bufsize;
179 	struct stat st;
180 	struct dirent *dp;
181 	char *buf, *ebuf, *cp;
182 	struct cvs_ent *ent;
183 	struct cvs_ent_line *line;
184 	CVSENTRIES *entlist;
185 	char export[MAXPATHLEN];
186 
187 	cvs_log(LP_TRACE, "cvs_update_leavedir(%s)", cf->file_path);
188 
189 	if (cvs_cmdop == CVS_OP_EXPORT) {
190 		if (cvs_path_cat(cf->file_path, CVS_PATH_CVSDIR, export,
191 		    MAXPATHLEN) >= MAXPATHLEN)
192 			fatal("cvs_update_leavedir: truncation");
193 
194 		/* XXX */
195 		if (cvs_rmdir(export) == -1)
196 			fatal("cvs_update_leavedir: %s: %s:", export,
197 			    strerror(errno));
198 
199 		return;
200 	}
201 
202 	if (fstat(cf->fd, &st) == -1)
203 		fatal("cvs_update_leavedir: %s", strerror(errno));
204 
205 	bufsize = st.st_size;
206 	if (bufsize < st.st_blksize)
207 		bufsize = st.st_blksize;
208 
209 	isempty = 1;
210 	buf = xmalloc(bufsize);
211 
212 	if (lseek(cf->fd, 0, SEEK_SET) == -1)
213 		fatal("cvs_update_leavedir: %s", strerror(errno));
214 
215 	while ((nbytes = getdirentries(cf->fd, buf, bufsize, &base)) > 0) {
216 		ebuf = buf + nbytes;
217 		cp = buf;
218 
219 		while (cp < ebuf) {
220 			dp = (struct dirent *)cp;
221 			if (!strcmp(dp->d_name, ".") ||
222 			    !strcmp(dp->d_name, "..") ||
223 			    dp->d_reclen == 0) {
224 				cp += dp->d_reclen;
225 				continue;
226 			}
227 
228 			if (!strcmp(dp->d_name, CVS_PATH_CVSDIR)) {
229 				entlist = cvs_ent_open(cf->file_path);
230 				TAILQ_FOREACH(line, &(entlist->cef_ent),
231 				    entries_list) {
232 					ent = cvs_ent_parse(line->buf);
233 
234 					if (ent->ce_status == CVS_ENT_REMOVED) {
235 						isempty = 0;
236 						cvs_ent_free(ent);
237 						break;
238 					}
239 
240 					cvs_ent_free(ent);
241 				}
242 				cvs_ent_close(entlist, ENT_NOSYNC);
243 			} else {
244 				isempty = 0;
245 			}
246 
247 			if (isempty == 0)
248 				break;
249 
250 			cp += dp->d_reclen;
251 		}
252 	}
253 
254 	if (nbytes == -1)
255 		fatal("cvs_update_leavedir: %s", strerror(errno));
256 
257 	xfree(buf);
258 
259 	if (isempty == 1 && prune_dirs == 1) {
260 		/* XXX */
261 		cvs_rmdir(cf->file_path);
262 
263 		if (cvs_server_active == 0) {
264 			entlist = cvs_ent_open(cf->file_wd);
265 			cvs_ent_remove(entlist, cf->file_name);
266 			cvs_ent_close(entlist, ENT_SYNC);
267 		}
268 	}
269 }
270 
271 void
272 cvs_update_local(struct cvs_file *cf)
273 {
274 	int ret, flags;
275 	CVSENTRIES *entlist;
276 	char rbuf[16];
277 
278 	cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path);
279 
280 	if (cf->file_type == CVS_DIR) {
281 		if (cf->file_status == FILE_SKIP)
282 			return;
283 
284 		if (cf->file_status != FILE_UNKNOWN &&
285 		    verbosity > 1)
286 			cvs_log(LP_NOTICE, "Updating %s", cf->file_path);
287 		return;
288 	}
289 
290 	flags = 0;
291 	cvs_file_classify(cf, tag, 1);
292 
293 	if (cf->file_status == FILE_UPTODATE && cf->file_ent != NULL &&
294 	    cf->file_ent->ce_tag != NULL && reset_stickies == 1) {
295 		cf->file_status = FILE_CHECKOUT;
296 		cf->file_rcsrev = rcs_head_get(cf->file_rcs);
297 	}
298 
299 	if (print && cf->file_status != FILE_UNKNOWN) {
300 		rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf));
301 		if (verbosity > 1)
302 			cvs_printf("%s\nChecking out %s\n"
303 			    "RCS:\t%s\nVERS:\t%s\n***************\n",
304 			    RCS_DIFF_DIV, cf->file_path, cf->file_rpath, rbuf);
305 		cvs_checkout_file(cf, cf->file_rcsrev, CO_DUMP);
306 		return;
307 	}
308 
309 	switch (cf->file_status) {
310 	case FILE_UNKNOWN:
311 		cvs_printf("? %s\n", cf->file_path);
312 		break;
313 	case FILE_MODIFIED:
314 		ret = update_has_conflict_markers(cf);
315 		if (cf->file_ent->ce_conflict != NULL && ret == 1) {
316 			cvs_printf("C %s\n", cf->file_path);
317 		} else {
318 			if (cf->file_ent->ce_conflict != NULL && ret == 0)
319 				update_clear_conflict(cf);
320 			cvs_printf("M %s\n", cf->file_path);
321 		}
322 		break;
323 	case FILE_ADDED:
324 		cvs_printf("A %s\n", cf->file_path);
325 		break;
326 	case FILE_REMOVED:
327 		cvs_printf("R %s\n", cf->file_path);
328 		break;
329 	case FILE_CONFLICT:
330 		cvs_printf("C %s\n", cf->file_path);
331 		break;
332 	case FILE_LOST:
333 	case FILE_CHECKOUT:
334 	case FILE_PATCH:
335 		if (tag != NULL)
336 			flags = CO_SETSTICKY;
337 
338 		cvs_checkout_file(cf, cf->file_rcsrev, flags);
339 		cvs_printf("U %s\n", cf->file_path);
340 		break;
341 	case FILE_MERGE:
342 		cvs_checkout_file(cf, cf->file_rcsrev, CO_MERGE);
343 
344 		if (diff3_conflicts != 0) {
345 			cvs_printf("C %s\n", cf->file_path);
346 		} else {
347 			update_clear_conflict(cf);
348 			cvs_printf("M %s\n", cf->file_path);
349 		}
350 		break;
351 	case FILE_UNLINK:
352 		(void)unlink(cf->file_path);
353 	case FILE_REMOVE_ENTRY:
354 		entlist = cvs_ent_open(cf->file_wd);
355 		cvs_ent_remove(entlist, cf->file_name);
356 		cvs_ent_close(entlist, ENT_SYNC);
357 		break;
358 	default:
359 		break;
360 	}
361 }
362 
363 static void
364 update_clear_conflict(struct cvs_file *cf)
365 {
366 	int l;
367 	time_t now;
368 	CVSENTRIES *entlist;
369 	char *entry, revbuf[16], timebuf[32];
370 
371 	cvs_log(LP_TRACE, "update_clear_conflict(%s)", cf->file_path);
372 
373 	time(&now);
374 	ctime_r(&now, timebuf);
375 	if (timebuf[strlen(timebuf) - 1] == '\n')
376 		timebuf[strlen(timebuf) - 1] = '\0';
377 
378 	rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
379 
380 	entry = xmalloc(CVS_ENT_MAXLINELEN);
381 	l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//",
382 	    cf->file_name, revbuf, timebuf);
383 	if (l == -1 || l >= CVS_ENT_MAXLINELEN)
384 		fatal("update_clear_conflict: overflow");
385 
386 	entlist = cvs_ent_open(cf->file_wd);
387 	cvs_ent_add(entlist, entry);
388 	cvs_ent_close(entlist, ENT_SYNC);
389 	xfree(entry);
390 }
391 
392 /*
393  * XXX - this is the way GNU cvs checks for outstanding conflicts
394  * in a file after a merge. It is a very very bad approach and
395  * should be looked at once opencvs is working decently.
396  */
397 int
398 update_has_conflict_markers(struct cvs_file *cf)
399 {
400 	BUF *bp;
401 	int conflict;
402 	char *content;
403 	struct cvs_line *lp;
404 	struct cvs_lines *lines;
405 	size_t len;
406 
407 	cvs_log(LP_TRACE, "update_has_conflict_markers(%s)", cf->file_path);
408 
409 	if (cf->fd == -1)
410 		return (0);
411 
412 	if ((bp = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL)
413 		fatal("update_has_conflict_markers: failed to load %s",
414 		    cf->file_path);
415 
416 	cvs_buf_putc(bp, '\0');
417 	len = cvs_buf_len(bp);
418 	content = cvs_buf_release(bp);
419 	if ((lines = cvs_splitlines(content, len)) == NULL)
420 		fatal("update_has_conflict_markers: failed to split lines");
421 
422 	conflict = 0;
423 	TAILQ_FOREACH(lp, &(lines->l_lines), l_list) {
424 		if (lp->l_line == NULL)
425 			continue;
426 
427 		if (!strncmp(lp->l_line, RCS_CONFLICT_MARKER1,
428 		    strlen(RCS_CONFLICT_MARKER1)) ||
429 		    !strncmp(lp->l_line, RCS_CONFLICT_MARKER2,
430 		    strlen(RCS_CONFLICT_MARKER2)) ||
431 		    !strncmp(lp->l_line, RCS_CONFLICT_MARKER3,
432 		    strlen(RCS_CONFLICT_MARKER3))) {
433 			conflict = 1;
434 			break;
435 		}
436 	}
437 
438 	cvs_freelines(lines);
439 	xfree(content);
440 	return (conflict);
441 }
442