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