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
32int	prune_dirs = 0;
33int	print_stdout = 0;
34int	build_dirs = 0;
35int	reset_option = 0;
36int	reset_tag = 0;
37int 	backup_local_changes = 0;
38char *cvs_specified_tag = NULL;
39char *cvs_join_rev1 = NULL;
40char *cvs_join_rev2 = NULL;
41
42static char *koptstr;
43static char *dateflag = NULL;
44static int Aflag = 0;
45
46static void update_clear_conflict(struct cvs_file *);
47static void update_join_file(struct cvs_file *);
48
49extern CVSENTRIES *current_list;
50
51struct 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
62int
63cvs_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
193void
194cvs_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
237void
238cvs_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
302prune_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
315void
316cvs_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
496static void
497update_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 */
531int
532update_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
575void
576update_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
708out:
709	free(rev1);
710	free(rev2);
711	free(jrev1);
712	free(jrev2);
713}
714
715void
716cvs_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