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