xref: /openbsd-src/usr.bin/cvs/update.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: update.c,v 1.168 2013/12/13 15:19:41 zhuk 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/stat.h>
19 
20 #include <dirent.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "cvs.h"
27 #include "diff.h"
28 #include "remote.h"
29 
30 int	prune_dirs = 0;
31 int	print_stdout = 0;
32 int	build_dirs = 0;
33 int	reset_option = 0;
34 int	reset_tag = 0;
35 int 	backup_local_changes = 0;
36 char *cvs_specified_tag = NULL;
37 char *cvs_join_rev1 = NULL;
38 char *cvs_join_rev2 = NULL;
39 
40 static char *koptstr;
41 static char *dateflag = NULL;
42 static int Aflag = 0;
43 
44 static void update_clear_conflict(struct cvs_file *);
45 static void update_join_file(struct cvs_file *);
46 
47 extern CVSENTRIES *current_list;
48 
49 struct cvs_cmd cvs_cmd_update = {
50 	CVS_OP_UPDATE, CVS_USE_WDIR, "update",
51 	{ "up", "upd" },
52 	"Bring work tree in sync with repository",
53 	"[-ACdflPpR] [-D date | -r rev] [-I ign] [-j rev] [-k mode] "
54 	"[-t id] ...",
55 	"ACD:dfI:j:k:lPpQqRr:t:u",
56 	NULL,
57 	cvs_update
58 };
59 
60 int
61 cvs_update(int argc, char **argv)
62 {
63 	int ch;
64 	char *arg = ".";
65 	int flags;
66 	struct cvs_recursion cr;
67 
68 	flags = CR_RECURSE_DIRS;
69 
70 	while ((ch = getopt(argc, argv, cvs_cmd_update.cmd_opts)) != -1) {
71 		switch (ch) {
72 		case 'A':
73 			Aflag = 1;
74 			if (koptstr == NULL)
75 				reset_option = 1;
76 			if (cvs_specified_tag == NULL)
77 				reset_tag = 1;
78 			break;
79 		case 'C':
80 			backup_local_changes = 1;
81 			break;
82 		case 'D':
83 			dateflag = optarg;
84 			if ((cvs_specified_date = date_parse(dateflag)) == -1)
85 				fatal("invalid date: %s", dateflag);
86 			reset_tag = 0;
87 			break;
88 		case 'd':
89 			build_dirs = 1;
90 			break;
91 		case 'f':
92 			break;
93 		case 'I':
94 			break;
95 		case 'j':
96 			if (cvs_join_rev1 == NULL)
97 				cvs_join_rev1 = optarg;
98 			else if (cvs_join_rev2 == NULL)
99 				cvs_join_rev2 = optarg;
100 			else
101 				fatal("too many -j options");
102 			break;
103 		case 'k':
104 			reset_option = 0;
105 			koptstr = optarg;
106 			kflag = rcs_kflag_get(koptstr);
107 			if (RCS_KWEXP_INVAL(kflag)) {
108 				cvs_log(LP_ERR,
109 				    "invalid RCS keyword expansion mode");
110 				fatal("%s", cvs_cmd_update.cmd_synopsis);
111 			}
112 			break;
113 		case 'l':
114 			flags &= ~CR_RECURSE_DIRS;
115 			break;
116 		case 'P':
117 			prune_dirs = 1;
118 			break;
119 		case 'p':
120 			print_stdout = 1;
121 			cvs_noexec = 1;
122 			break;
123 		case 'Q':
124 		case 'q':
125 			break;
126 		case 'R':
127 			flags |= CR_RECURSE_DIRS;
128 			break;
129 		case 'r':
130 			reset_tag = 0;
131 			cvs_specified_tag = optarg;
132 			break;
133 		case 'u':
134 			break;
135 		default:
136 			fatal("%s", cvs_cmd_update.cmd_synopsis);
137 		}
138 	}
139 
140 	argc -= optind;
141 	argv += optind;
142 
143 	if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
144 		cr.enterdir = cvs_update_enterdir;
145 		cr.leavedir = prune_dirs ? cvs_update_leavedir : NULL;
146 		cr.fileproc = cvs_update_local;
147 		flags |= CR_REPO;
148 	} else {
149 		cvs_client_connect_to_server();
150 		if (Aflag)
151 			cvs_client_send_request("Argument -A");
152 		if (dateflag != NULL)
153 			cvs_client_send_request("Argument -D%s", dateflag);
154 		if (build_dirs)
155 			cvs_client_send_request("Argument -d");
156 		if (kflag)
157 			cvs_client_send_request("Argument -k%s", koptstr);
158 		if (!(flags & CR_RECURSE_DIRS))
159 			cvs_client_send_request("Argument -l");
160 		if (prune_dirs)
161 			cvs_client_send_request("Argument -P");
162 		if (print_stdout)
163 			cvs_client_send_request("Argument -p");
164 
165 		if (cvs_specified_tag != NULL)
166 			cvs_client_send_request("Argument -r%s",
167 			    cvs_specified_tag);
168 
169 		cr.enterdir = NULL;
170 		cr.leavedir = NULL;
171 		cr.fileproc = cvs_client_sendfile;
172 	}
173 
174 	cr.flags = flags;
175 
176 	if (argc > 0)
177 		cvs_file_run(argc, argv, &cr);
178 	else
179 		cvs_file_run(1, &arg, &cr);
180 
181 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
182 		cvs_client_send_files(argv, argc);
183 		cvs_client_senddir(".");
184 		cvs_client_send_request("update");
185 		cvs_client_get_responses();
186 	}
187 
188 	return (0);
189 }
190 
191 void
192 cvs_update_enterdir(struct cvs_file *cf)
193 {
194 	CVSENTRIES *entlist;
195 	char *dirtag, *entry, fpath[MAXPATHLEN];
196 
197 	cvs_log(LP_TRACE, "cvs_update_enterdir(%s)", cf->file_path);
198 
199 	cvs_file_classify(cf, NULL);
200 
201 	if (cf->file_status == DIR_CREATE && build_dirs == 1) {
202 		cvs_parse_tagfile(cf->file_wd, &dirtag, NULL, NULL);
203 		cvs_mkpath(cf->file_path, cvs_specified_tag != NULL ?
204 		    cvs_specified_tag : dirtag);
205 		if (dirtag != NULL)
206 			xfree(dirtag);
207 
208 		if ((cf->fd = open(cf->file_path, O_RDONLY)) == -1)
209 			fatal("cvs_update_enterdir: `%s': %s",
210 			    cf->file_path, strerror(errno));
211 
212 		if (cvs_server_active == 1 && cvs_cmdop != CVS_OP_CHECKOUT)
213 			cvs_server_clear_sticky(cf->file_path);
214 
215 		if (cvs_cmdop != CVS_OP_EXPORT) {
216 			(void)xasprintf(&entry, "D/%s////", cf->file_name);
217 
218 			entlist = cvs_ent_open(cf->file_wd);
219 			cvs_ent_add(entlist, entry);
220 			xfree(entry);
221 		}
222 	} else if ((cf->file_status == DIR_CREATE && build_dirs == 0) ||
223 		    cf->file_status == FILE_UNKNOWN) {
224 		cf->file_status = FILE_SKIP;
225 	} else if (reset_tag) {
226 		(void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
227 		    cf->file_path, CVS_PATH_TAG);
228 		(void)unlink(fpath);
229 	} else {
230 		if (cvs_specified_tag != NULL || cvs_specified_date != -1)
231 			cvs_write_tagfile(cf->file_path,
232 				    cvs_specified_tag, NULL);
233 	}
234 }
235 
236 void
237 cvs_update_leavedir(struct cvs_file *cf)
238 {
239 	int nbytes;
240 	int isempty;
241 	size_t bufsize;
242 	struct stat st;
243 	struct dirent *dp;
244 	char *buf, *ebuf, *cp;
245 	CVSENTRIES *entlist;
246 
247 	cvs_log(LP_TRACE, "cvs_update_leavedir(%s)", cf->file_path);
248 
249 	if (cvs_server_active == 1 && !strcmp(cf->file_name, "."))
250 		return;
251 
252 	entlist = cvs_ent_open(cf->file_path);
253 	if (!TAILQ_EMPTY(&(entlist->cef_ent))) {
254 		isempty = 0;
255 		goto prune_it;
256 	}
257 
258 	if (fstat(cf->fd, &st) == -1)
259 		fatal("cvs_update_leavedir: %s", strerror(errno));
260 
261 	bufsize = st.st_size;
262 	if (bufsize < st.st_blksize)
263 		bufsize = st.st_blksize;
264 
265 	if (st.st_size > SIZE_MAX)
266 		fatal("cvs_update_leavedir: %s: file size too big",
267 		    cf->file_name);
268 
269 	isempty = 1;
270 	buf = xmalloc(bufsize);
271 
272 	if (lseek(cf->fd, 0, SEEK_SET) == -1)
273 		fatal("cvs_update_leavedir: %s", strerror(errno));
274 
275 	while ((nbytes = getdents(cf->fd, buf, bufsize)) > 0) {
276 		ebuf = buf + nbytes;
277 		cp = buf;
278 
279 		while (cp < ebuf) {
280 			dp = (struct dirent *)cp;
281 			if (!strcmp(dp->d_name, ".") ||
282 			    !strcmp(dp->d_name, "..") ||
283 			    dp->d_fileno == 0) {
284 				cp += dp->d_reclen;
285 				continue;
286 			}
287 
288 			if (strcmp(dp->d_name, CVS_PATH_CVSDIR))
289 				isempty = 0;
290 
291 			if (isempty == 0)
292 				break;
293 
294 			cp += dp->d_reclen;
295 		}
296 	}
297 
298 	if (nbytes == -1)
299 		fatal("cvs_update_leavedir: %s", strerror(errno));
300 
301 	xfree(buf);
302 
303 prune_it:
304 	if ((isempty == 1 && prune_dirs == 1) ||
305 	    (cvs_server_active == 1 && cvs_cmdop == CVS_OP_CHECKOUT)) {
306 		/* XXX */
307 		cvs_rmdir(cf->file_path);
308 
309 		if (cvs_server_active == 0 && cvs_cmdop != CVS_OP_EXPORT) {
310 			entlist = cvs_ent_open(cf->file_wd);
311 			cvs_ent_remove(entlist, cf->file_name);
312 		}
313 	}
314 }
315 
316 void
317 cvs_update_local(struct cvs_file *cf)
318 {
319 	CVSENTRIES *entlist;
320 	int ent_kflag, rcs_kflag, ret, flags;
321 	char *tag, rbuf[CVS_REV_BUFSZ];
322 
323 	cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path);
324 
325 	if (cf->file_type == CVS_DIR) {
326 		if (cf->file_status == FILE_SKIP) {
327 			if (cvs_cmdop == CVS_OP_EXPORT && verbosity > 0)
328 				cvs_printf("? %s\n", cf->file_path);
329 			return;
330 		}
331 
332 		if (cf->file_status != FILE_UNKNOWN &&
333 		    verbosity > 1)
334 			cvs_log(LP_ERR, "Updating %s", cf->file_path);
335 		return;
336 	}
337 
338 	flags = 0;
339 	if (cvs_specified_tag != NULL)
340 		tag = cvs_specified_tag;
341 	else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
342 		tag = cf->file_ent->ce_tag;
343 	else
344 		tag = cvs_directory_tag;
345 
346 	cvs_file_classify(cf, tag);
347 
348 	if (kflag && cf->file_rcs != NULL)
349 		rcs_kwexp_set(cf->file_rcs, kflag);
350 
351 	if ((cf->file_status == FILE_UPTODATE ||
352 	    cf->file_status == FILE_MODIFIED) && cf->file_ent != NULL &&
353 	    cf->file_ent->ce_tag != NULL && reset_tag) {
354 		if (cf->file_status == FILE_MODIFIED)
355 			cf->file_status = FILE_MERGE;
356 		else
357 			cf->file_status = FILE_CHECKOUT;
358 
359 		if ((cf->file_rcsrev = rcs_head_get(cf->file_rcs)) == NULL)
360 			fatal("no head revision in RCS file for %s",
361 			    cf->file_path);
362 
363 		/* might be a bit overkill */
364 		if (cvs_server_active == 1)
365 			cvs_server_clear_sticky(cf->file_wd);
366 	}
367 
368 	if (print_stdout) {
369 		if (cf->file_status != FILE_UNKNOWN && cf->file_rcs != NULL &&
370 		    cf->file_rcsrev != NULL && !cf->file_rcs->rf_dead &&
371 		    (cf->file_flags & FILE_HAS_TAG)) {
372 			rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf));
373 			if (verbosity > 1) {
374 				cvs_log(LP_RCS, RCS_DIFF_DIV);
375 				cvs_log(LP_RCS, "Checking out %s",
376 				    cf->file_path);
377 				cvs_log(LP_RCS, "RCS:  %s", cf->file_rpath);
378 				cvs_log(LP_RCS, "VERS: %s", rbuf);
379 				cvs_log(LP_RCS, "***************");
380 			}
381 			cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_DUMP);
382 		}
383 		return;
384 	}
385 
386 	if (cf->file_ent != NULL) {
387 		if (cf->file_ent->ce_opts == NULL) {
388 			if (kflag)
389 				cf->file_status = FILE_CHECKOUT;
390 		} else if (cf->file_rcs != NULL) {
391 			if (strlen(cf->file_ent->ce_opts) < 3)
392 				fatal("malformed option for file %s",
393 				    cf->file_path);
394 
395 			ent_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2);
396 			rcs_kflag = rcs_kwexp_get(cf->file_rcs);
397 
398 			if ((kflag && (kflag != ent_kflag)) ||
399 			    (reset_option && (ent_kflag != rcs_kflag)))
400 				cf->file_status = FILE_CHECKOUT;
401 		}
402 	}
403 
404 	switch (cf->file_status) {
405 	case FILE_UNKNOWN:
406 		cvs_printf("? %s\n", cf->file_path);
407 		break;
408 	case FILE_MODIFIED:
409 		if (backup_local_changes) {
410 			cvs_backup_file(cf);
411 
412 			cvs_checkout_file(cf, cf->file_rcsrev, NULL, flags);
413 			cvs_printf("U %s\n", cf->file_path);
414 		} else {
415 			ret = update_has_conflict_markers(cf);
416 			if (cf->file_ent->ce_conflict != NULL && ret == 1)
417 				cvs_printf("C %s\n", cf->file_path);
418 			else {
419 				if (cf->file_ent->ce_conflict != NULL && ret == 0)
420 					update_clear_conflict(cf);
421 				cvs_printf("M %s\n", cf->file_path);
422 			}
423 		}
424 		break;
425 	case FILE_ADDED:
426 		cvs_printf("A %s\n", cf->file_path);
427 		break;
428 	case FILE_REMOVED:
429 		cvs_printf("R %s\n", cf->file_path);
430 		break;
431 	case FILE_CONFLICT:
432 		cvs_printf("C %s\n", cf->file_path);
433 		break;
434 	case FILE_LOST:
435 	case FILE_CHECKOUT:
436 	case FILE_PATCH:
437 		if (!reset_tag && (tag != NULL || cvs_specified_date != -1 ||
438 		    cvs_directory_date != -1 || (cf->file_ent != NULL &&
439 		    cf->file_ent->ce_tag != NULL)))
440 			flags = CO_SETSTICKY;
441 
442 		if (cf->file_flags & FILE_ON_DISK && (cf->file_ent == NULL ||
443 		    cf->file_ent->ce_type == CVS_ENT_NONE)) {
444 			cvs_log(LP_ERR, "move away %s; it is in the way",
445 			    cf->file_path);
446 			cvs_printf("C %s\n", cf->file_path);
447 		} else {
448 			cvs_checkout_file(cf, cf->file_rcsrev, tag, flags);
449 			cvs_printf("U %s\n", cf->file_path);
450 			cvs_history_add(CVS_HISTORY_UPDATE_CO, cf, NULL);
451 		}
452 		break;
453 	case FILE_MERGE:
454 		d3rev1 = cf->file_ent->ce_rev;
455 		d3rev2 = cf->file_rcsrev;
456 		cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_MERGE);
457 
458 		if (diff3_conflicts != 0) {
459 			cvs_printf("C %s\n", cf->file_path);
460 			cvs_history_add(CVS_HISTORY_UPDATE_MERGED_ERR,
461 			    cf, NULL);
462 		} else {
463 			update_clear_conflict(cf);
464 			cvs_printf("M %s\n", cf->file_path);
465 			cvs_history_add(CVS_HISTORY_UPDATE_MERGED, cf, NULL);
466 		}
467 		break;
468 	case FILE_UNLINK:
469 		(void)unlink(cf->file_path);
470 	case FILE_REMOVE_ENTRY:
471 		entlist = cvs_ent_open(cf->file_wd);
472 		cvs_ent_remove(entlist, cf->file_name);
473 		cvs_history_add(CVS_HISTORY_UPDATE_REMOVE, cf, NULL);
474 
475 		if (cvs_server_active == 1)
476 			cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_REMOVE);
477 		break;
478 	case FILE_UPTODATE:
479 		if (cvs_cmdop != CVS_OP_UPDATE)
480 			break;
481 
482 		if (reset_tag != 1 && reset_option != 1)
483 			break;
484 
485 		if (cf->file_ent != NULL && cf->file_ent->ce_tag == NULL)
486 			break;
487 
488 		if (cf->file_rcs->rf_dead != 1 &&
489 		    (cf->file_flags & FILE_HAS_TAG))
490 			cvs_checkout_file(cf, cf->file_rcsrev,
491 			    tag, CO_SETSTICKY);
492 		break;
493 	default:
494 		break;
495 	}
496 
497 	if (cvs_join_rev1 != NULL)
498 		update_join_file(cf);
499 }
500 
501 static void
502 update_clear_conflict(struct cvs_file *cf)
503 {
504 	CVSENTRIES *entlist;
505 	char *entry, revbuf[CVS_REV_BUFSZ];
506 	char sticky[CVS_ENT_MAXLINELEN], opt[4];
507 
508 	cvs_log(LP_TRACE, "update_clear_conflict(%s)", cf->file_path);
509 
510 	rcsnum_tostr(cf->file_rcsrev, revbuf, sizeof(revbuf));
511 
512 	sticky[0] = '\0';
513 	if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
514 		(void)xsnprintf(sticky, sizeof(sticky), "T%s",
515 		    cf->file_ent->ce_tag);
516 
517 	opt[0] = '\0';
518 	if (cf->file_ent != NULL && cf->file_ent->ce_opts != NULL)
519 		strlcpy(opt, cf->file_ent->ce_opts, sizeof(opt));
520 
521 	entry = xmalloc(CVS_ENT_MAXLINELEN);
522 	cvs_ent_line_str(cf->file_name, revbuf, "Result of merge",
523 	    opt[0] != '\0' ? opt : "", sticky, 0, 0,
524 	    entry, CVS_ENT_MAXLINELEN);
525 
526 	entlist = cvs_ent_open(cf->file_wd);
527 	cvs_ent_add(entlist, entry);
528 	xfree(entry);
529 }
530 
531 /*
532  * XXX - this is the way GNU cvs checks for outstanding conflicts
533  * in a file after a merge. It is a very very bad approach and
534  * should be looked at once opencvs is working decently.
535  */
536 int
537 update_has_conflict_markers(struct cvs_file *cf)
538 {
539 	BUF *bp;
540 	int conflict;
541 	char *content;
542 	struct rcs_line *lp;
543 	struct rcs_lines *lines;
544 	size_t len;
545 
546 	cvs_log(LP_TRACE, "update_has_conflict_markers(%s)", cf->file_path);
547 
548 	if (!(cf->file_flags & FILE_ON_DISK) || cf->file_ent == NULL)
549 		return (0);
550 
551 	bp = buf_load_fd(cf->fd);
552 
553 	buf_putc(bp, '\0');
554 	len = buf_len(bp);
555 	content = buf_release(bp);
556 	if ((lines = cvs_splitlines(content, len)) == NULL)
557 		fatal("update_has_conflict_markers: failed to split lines");
558 
559 	conflict = 0;
560 	TAILQ_FOREACH(lp, &(lines->l_lines), l_list) {
561 		if (lp->l_line == NULL)
562 			continue;
563 
564 		if (!strncmp(lp->l_line, RCS_CONFLICT_MARKER1,
565 		    sizeof(RCS_CONFLICT_MARKER1) - 1) ||
566 		    !strncmp(lp->l_line, RCS_CONFLICT_MARKER2,
567 		    sizeof(RCS_CONFLICT_MARKER2) - 1) ||
568 		    !strncmp(lp->l_line, RCS_CONFLICT_MARKER3,
569 		    sizeof(RCS_CONFLICT_MARKER3) - 1)) {
570 			conflict = 1;
571 			break;
572 		}
573 	}
574 
575 	cvs_freelines(lines);
576 	xfree(content);
577 	return (conflict);
578 }
579 
580 void
581 update_join_file(struct cvs_file *cf)
582 {
583 	time_t told;
584 	RCSNUM *rev1, *rev2;
585 	const char *state1, *state2;
586 	char rbuf[CVS_REV_BUFSZ], *jrev1, *jrev2, *p;
587 
588 	rev1 = rev2 = NULL;
589 	jrev1 = jrev2 = NULL;
590 
591 	jrev1 = xstrdup(cvs_join_rev1);
592 	if (cvs_join_rev2 != NULL)
593 		jrev2 = xstrdup(cvs_join_rev2);
594 
595 	if (jrev2 == NULL) {
596 		jrev2 = jrev1;
597 		jrev1 = NULL;
598 	}
599 
600 	told = cvs_specified_date;
601 
602 	if ((p = strchr(jrev2, ':')) != NULL) {
603 		(*p++) = '\0';
604 		if ((cvs_specified_date = date_parse(p)) == -1) {
605 			cvs_printf("invalid date: %s", p);
606 			goto out;
607 		}
608 	}
609 
610 	rev2 = rcs_translate_tag(jrev2, cf->file_rcs);
611 	cvs_specified_date = told;
612 
613 	if (jrev1 != NULL) {
614 		if ((p = strchr(jrev1, ':')) != NULL) {
615 			(*p++) = '\0';
616 			if ((cvs_specified_date = date_parse(p)) == -1) {
617 				cvs_printf("invalid date: %s", p);
618 				goto out;
619 			}
620 		}
621 
622 		rev1 = rcs_translate_tag(jrev1, cf->file_rcs);
623 		cvs_specified_date = told;
624 	} else {
625 		if (rev2 == NULL)
626 			goto out;
627 
628 		rev1 = rcsnum_alloc();
629 		rcsnum_cpy(cf->file_rcsrev, rev1, 0);
630 	}
631 
632 	state1 = state2 = RCS_STATE_DEAD;
633 
634 	if (rev1 != NULL)
635 		state1 = rcs_state_get(cf->file_rcs, rev1);
636 	if (rev2 != NULL)
637 		state2 = rcs_state_get(cf->file_rcs, rev2);
638 
639 	if (rev2 == NULL || !strcmp(state2, RCS_STATE_DEAD)) {
640 		if (rev1 == NULL || !strcmp(state1, RCS_STATE_DEAD))
641 			goto out;
642 
643 		if (cf->file_status == FILE_REMOVED ||
644 		    cf->file_rcs->rf_dead == 1)
645 			goto out;
646 
647 		if (cf->file_status == FILE_MODIFIED ||
648 		    cf->file_status == FILE_ADDED)
649 			goto out;
650 
651 		(void)unlink(cf->file_path);
652 		(void)close(cf->fd);
653 		cf->fd = -1;
654 		cvs_remove_local(cf);
655 		goto out;
656 	}
657 
658 	if (cf->file_ent != NULL) {
659 		if (!rcsnum_cmp(cf->file_ent->ce_rev, rev2, 0))
660 			goto out;
661 	}
662 
663 	if (cf->file_rcsrev == NULL) {
664 		cvs_printf("non-mergable file: %s has no head revision!\n",
665 		    cf->file_path);
666 		goto out;
667 	}
668 
669 	if (rev1 == NULL || !strcmp(state1, RCS_STATE_DEAD)) {
670 		if (cf->file_flags & FILE_ON_DISK) {
671 			cvs_printf("%s exists but has been added in %s\n",
672 			    cf->file_path, jrev2);
673 		} else {
674 			cvs_printf("A %s\n", cf->file_path);
675 			cvs_checkout_file(cf, cf->file_rcsrev, NULL, 0);
676 			cvs_add_local(cf);
677 		}
678 		goto out;
679 	}
680 
681 	if (!rcsnum_cmp(rev1, rev2, 0))
682 		goto out;
683 
684 	if (!(cf->file_flags & FILE_ON_DISK)) {
685 		cvs_printf("%s does not exist but is present in %s\n",
686 		    cf->file_path, jrev2);
687 		goto out;
688 	}
689 
690 	if (rcs_kwexp_get(cf->file_rcs) & RCS_KWEXP_NONE) {
691 		cvs_printf("non-mergable file: %s needs merge!\n",
692 		    cf->file_path);
693 		goto out;
694 	}
695 
696 	cvs_printf("joining ");
697 	rcsnum_tostr(rev1, rbuf, sizeof(rbuf));
698 	cvs_printf("%s ", rbuf);
699 
700 	rcsnum_tostr(rev2, rbuf, sizeof(rbuf));
701 	cvs_printf("%s ", rbuf);
702 
703 	rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf));
704 	cvs_printf("into %s (%s)\n", cf->file_path, rbuf);
705 
706 	d3rev1 = rev1;
707 	d3rev2 = rev2;
708 	cvs_checkout_file(cf, cf->file_rcsrev, NULL, CO_MERGE);
709 
710 	if (diff3_conflicts == 0)
711 		update_clear_conflict(cf);
712 
713 out:
714 	if (rev1 != NULL)
715 		rcsnum_free(rev1);
716 	if (rev2 != NULL)
717 		rcsnum_free(rev2);
718 
719 	if (jrev1 != NULL)
720 		xfree(jrev1);
721 	if (jrev2 != NULL)
722 		xfree(jrev2);
723 }
724 
725 void
726 cvs_backup_file(struct cvs_file *cf)
727 {
728 	char	 backup_name[MAXPATHLEN];
729 	char	 revstr[RCSNUM_MAXSTR];
730 
731 	if (cf->file_status == FILE_ADDED)
732 		(void)xsnprintf(revstr, sizeof(revstr), "0");
733 	else
734 		rcsnum_tostr(cf->file_ent->ce_rev, revstr, sizeof(revstr));
735 
736 	(void)xsnprintf(backup_name, MAXPATHLEN, "%s/.#%s.%s",
737 	    cf->file_wd, cf->file_name, revstr);
738 
739 	cvs_file_copy(cf->file_path, backup_name);
740 
741 	(void)xsnprintf(backup_name, MAXPATHLEN, ".#%s.%s",
742 	    cf->file_name, revstr);
743 	cvs_printf("(Locally modified %s moved to %s)\n",
744 		   cf->file_name, backup_name);
745 }
746