xref: /openbsd-src/usr.bin/cvs/update.c (revision 4dcde51379711c241d4d9b93f9d4e9eae2d51f3d)
1*4dcde513Sjoris /*	$OpenBSD: update.c,v 1.176 2017/06/01 08:08:24 joris Exp $	*/
26c121f58Sjfb /*
33ad3fb45Sjoris  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
46c121f58Sjfb  *
53ad3fb45Sjoris  * Permission to use, copy, modify, and distribute this software for any
63ad3fb45Sjoris  * purpose with or without fee is hereby granted, provided that the above
73ad3fb45Sjoris  * copyright notice and this permission notice appear in all copies.
86c121f58Sjfb  *
93ad3fb45Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103ad3fb45Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113ad3fb45Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123ad3fb45Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133ad3fb45Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143ad3fb45Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153ad3fb45Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166c121f58Sjfb  */
176c121f58Sjfb 
181f8531bdSotto #include <sys/stat.h>
191f8531bdSotto 
2042354d6eSzhuk #include <dirent.h>
211f8531bdSotto #include <errno.h>
221f8531bdSotto #include <fcntl.h>
231357284aSmillert #include <stdint.h>
24397ddb8aSnicm #include <stdlib.h>
251f8531bdSotto #include <string.h>
261f8531bdSotto #include <unistd.h>
276c121f58Sjfb 
286c121f58Sjfb #include "cvs.h"
295191afffSjoris #include "diff.h"
309fac60a5Sjoris #include "remote.h"
316c121f58Sjfb 
329f820608Sjoris int	prune_dirs = 0;
337ac78d1dSjoris int	print_stdout = 0;
34cabc4131Sjoris int	build_dirs = 0;
35a10a48e5Stobias int	reset_option = 0;
36a10a48e5Stobias int	reset_tag = 0;
37daa36f23Snicm int 	backup_local_changes = 0;
385dd120b0Sjoris char *cvs_specified_tag = NULL;
3925d391d7Sjoris char *cvs_join_rev1 = NULL;
4025d391d7Sjoris char *cvs_join_rev2 = NULL;
41cabc4131Sjoris 
4237fdff3fStobias static char *koptstr;
4302d9e5c9Sjoris static char *dateflag = NULL;
44a10a48e5Stobias static int Aflag = 0;
4537fdff3fStobias 
46f9872b43Sjoris static void update_clear_conflict(struct cvs_file *);
4725d391d7Sjoris static void update_join_file(struct cvs_file *);
48f9872b43Sjoris 
497c1a09a6Sjoris extern CVSENTRIES *current_list;
507c1a09a6Sjoris 
51e4276007Sjfb struct cvs_cmd cvs_cmd_update = {
52f331ff59Stobias 	CVS_OP_UPDATE, CVS_USE_WDIR, "update",
53e4276007Sjfb 	{ "up", "upd" },
54e4276007Sjfb 	"Bring work tree in sync with repository",
55e4276007Sjfb 	"[-ACdflPpR] [-D date | -r rev] [-I ign] [-j rev] [-k mode] "
56e4276007Sjfb 	"[-t id] ...",
57b32ec52eSjoris 	"ACD:dfI:j:k:lPpQqRr:t:u",
58e4276007Sjfb 	NULL,
593ad3fb45Sjoris 	cvs_update
6016cfc147Sjoris };
61caf8b85dSjfb 
623ad3fb45Sjoris int
cvs_update(int argc,char ** argv)633ad3fb45Sjoris cvs_update(int argc, char **argv)
646c121f58Sjfb {
6516cfc147Sjoris 	int ch;
663ad3fb45Sjoris 	char *arg = ".";
671890abdaSjoris 	int flags;
683ad3fb45Sjoris 	struct cvs_recursion cr;
69caf8b85dSjfb 
709fac60a5Sjoris 	flags = CR_RECURSE_DIRS;
711890abdaSjoris 
723ad3fb45Sjoris 	while ((ch = getopt(argc, argv, cvs_cmd_update.cmd_opts)) != -1) {
736c121f58Sjfb 		switch (ch) {
746c121f58Sjfb 		case 'A':
75a10a48e5Stobias 			Aflag = 1;
76a10a48e5Stobias 			if (koptstr == NULL)
77a10a48e5Stobias 				reset_option = 1;
78a10a48e5Stobias 			if (cvs_specified_tag == NULL)
79a10a48e5Stobias 				reset_tag = 1;
807893cd61Sjoris 			break;
816c121f58Sjfb 		case 'C':
82daa36f23Snicm 			backup_local_changes = 1;
831e2d2d3bSjoris 			break;
846c121f58Sjfb 		case 'D':
8502d9e5c9Sjoris 			dateflag = optarg;
86092db204Sray 			if ((cvs_specified_date = date_parse(dateflag)) == -1)
87092db204Sray 				fatal("invalid date: %s", dateflag);
88b87788a5Stobias 			reset_tag = 0;
899a9853aeSxsa 			break;
906c121f58Sjfb 		case 'd':
91cabc4131Sjoris 			build_dirs = 1;
927893cd61Sjoris 			break;
936c121f58Sjfb 		case 'f':
94caf8b85dSjfb 			break;
95102e81f9Sxsa 		case 'I':
96102e81f9Sxsa 			break;
97102e81f9Sxsa 		case 'j':
9825d391d7Sjoris 			if (cvs_join_rev1 == NULL)
9925d391d7Sjoris 				cvs_join_rev1 = optarg;
10025d391d7Sjoris 			else if (cvs_join_rev2 == NULL)
10125d391d7Sjoris 				cvs_join_rev2 = optarg;
10225d391d7Sjoris 			else
10325d391d7Sjoris 				fatal("too many -j options");
104102e81f9Sxsa 			break;
105102e81f9Sxsa 		case 'k':
106a10a48e5Stobias 			reset_option = 0;
10737fdff3fStobias 			koptstr = optarg;
10837fdff3fStobias 			kflag = rcs_kflag_get(koptstr);
10937fdff3fStobias 			if (RCS_KWEXP_INVAL(kflag)) {
11037fdff3fStobias 				cvs_log(LP_ERR,
1110bc1d395Stobias 				    "invalid RCS keyword expansion mode");
112aeaaf4a6Stobias 				fatal("%s", cvs_cmd_update.cmd_synopsis);
11337fdff3fStobias 			}
114102e81f9Sxsa 			break;
1156c121f58Sjfb 		case 'l':
1161890abdaSjoris 			flags &= ~CR_RECURSE_DIRS;
117caf8b85dSjfb 			break;
1186c121f58Sjfb 		case 'P':
1199f820608Sjoris 			prune_dirs = 1;
1207893cd61Sjoris 			break;
1216c121f58Sjfb 		case 'p':
1227ac78d1dSjoris 			print_stdout = 1;
123a15077d7Sxsa 			cvs_noexec = 1;
1240bda20e2Sxsa 			break;
1256c121f58Sjfb 		case 'Q':
1266c121f58Sjfb 		case 'q':
127caf8b85dSjfb 			break;
1286c121f58Sjfb 		case 'R':
129bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
130caf8b85dSjfb 			break;
1316c121f58Sjfb 		case 'r':
132a10a48e5Stobias 			reset_tag = 0;
1335dd120b0Sjoris 			cvs_specified_tag = optarg;
1346c121f58Sjfb 			break;
135b32ec52eSjoris 		case 'u':
136b32ec52eSjoris 			break;
1376c121f58Sjfb 		default:
1383ad3fb45Sjoris 			fatal("%s", cvs_cmd_update.cmd_synopsis);
1396c121f58Sjfb 		}
1406c121f58Sjfb 	}
1416c121f58Sjfb 
1423ad3fb45Sjoris 	argc -= optind;
1433ad3fb45Sjoris 	argv += optind;
144caf8b85dSjfb 
145*4dcde513Sjoris 	if (cvsroot_is_local()) {
1463ad3fb45Sjoris 		cr.enterdir = cvs_update_enterdir;
147fec3e093Stobias 		cr.leavedir = prune_dirs ? cvs_update_leavedir : NULL;
148bc5d89feSjoris 		cr.fileproc = cvs_update_local;
1499fac60a5Sjoris 		flags |= CR_REPO;
1509fac60a5Sjoris 	} else {
15180f6ca9bSjoris 		cvs_client_connect_to_server();
152a10a48e5Stobias 		if (Aflag)
1539fac60a5Sjoris 			cvs_client_send_request("Argument -A");
15402d9e5c9Sjoris 		if (dateflag != NULL)
15502d9e5c9Sjoris 			cvs_client_send_request("Argument -D%s", dateflag);
1569fac60a5Sjoris 		if (build_dirs)
1579fac60a5Sjoris 			cvs_client_send_request("Argument -d");
15837fdff3fStobias 		if (kflag)
15937fdff3fStobias 			cvs_client_send_request("Argument -k%s", koptstr);
1609fac60a5Sjoris 		if (!(flags & CR_RECURSE_DIRS))
1619fac60a5Sjoris 			cvs_client_send_request("Argument -l");
1629fac60a5Sjoris 		if (prune_dirs)
1639fac60a5Sjoris 			cvs_client_send_request("Argument -P");
1647ac78d1dSjoris 		if (print_stdout)
1659fac60a5Sjoris 			cvs_client_send_request("Argument -p");
1669fac60a5Sjoris 
16751ef6581Sjoris 		if (cvs_specified_tag != NULL)
16851ef6581Sjoris 			cvs_client_send_request("Argument -r%s",
16951ef6581Sjoris 			    cvs_specified_tag);
17051ef6581Sjoris 
1719fac60a5Sjoris 		cr.enterdir = NULL;
1729fac60a5Sjoris 		cr.leavedir = NULL;
1739fac60a5Sjoris 		cr.fileproc = cvs_client_sendfile;
1749fac60a5Sjoris 	}
1759fac60a5Sjoris 
1761890abdaSjoris 	cr.flags = flags;
177082a1aa7Sxsa 
1783ad3fb45Sjoris 	if (argc > 0)
1793ad3fb45Sjoris 		cvs_file_run(argc, argv, &cr);
180acc85264Sjfb 	else
1813ad3fb45Sjoris 		cvs_file_run(1, &arg, &cr);
182acc85264Sjfb 
183*4dcde513Sjoris 	if (cvsroot_is_remote()) {
1849fac60a5Sjoris 		cvs_client_send_files(argv, argc);
1859fac60a5Sjoris 		cvs_client_senddir(".");
1869fac60a5Sjoris 		cvs_client_send_request("update");
1879fac60a5Sjoris 		cvs_client_get_responses();
1889fac60a5Sjoris 	}
1899fac60a5Sjoris 
1907e393898Sjoris 	return (0);
191acc85264Sjfb }
192acc85264Sjfb 
1933ad3fb45Sjoris void
cvs_update_enterdir(struct cvs_file * cf)1943ad3fb45Sjoris cvs_update_enterdir(struct cvs_file *cf)
195caf8b85dSjfb {
1963ad3fb45Sjoris 	CVSENTRIES *entlist;
197b9fc9a72Sderaadt 	char *dirtag, *entry, fpath[PATH_MAX];
198caf8b85dSjfb 
1993ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_update_enterdir(%s)", cf->file_path);
200caf8b85dSjfb 
201a41c7fa8Stobias 	cvs_file_classify(cf, NULL);
202e4276007Sjfb 
203cabc4131Sjoris 	if (cf->file_status == DIR_CREATE && build_dirs == 1) {
204a41c7fa8Stobias 		cvs_parse_tagfile(cf->file_wd, &dirtag, NULL, NULL);
205a41c7fa8Stobias 		cvs_mkpath(cf->file_path, cvs_specified_tag != NULL ?
206a41c7fa8Stobias 		    cvs_specified_tag : dirtag);
207397ddb8aSnicm 		free(dirtag);
208a41c7fa8Stobias 
2093ad3fb45Sjoris 		if ((cf->fd = open(cf->file_path, O_RDONLY)) == -1)
210d5a38fcaSxsa 			fatal("cvs_update_enterdir: `%s': %s",
211d5a38fcaSxsa 			    cf->file_path, strerror(errno));
2123ad3fb45Sjoris 
213130b5aa8Sjoris 		if (cvs_server_active == 1 && cvs_cmdop != CVS_OP_CHECKOUT)
214130b5aa8Sjoris 			cvs_server_clear_sticky(cf->file_path);
215130b5aa8Sjoris 
2165e1effbaStobias 		if (cvs_cmdop != CVS_OP_EXPORT) {
21793cef22cSray 			(void)xasprintf(&entry, "D/%s////", cf->file_name);
2183ad3fb45Sjoris 
2193ad3fb45Sjoris 			entlist = cvs_ent_open(cf->file_wd);
2203ad3fb45Sjoris 			cvs_ent_add(entlist, entry);
221397ddb8aSnicm 			free(entry);
2225e1effbaStobias 		}
223b974a189Sxsa 	} else if ((cf->file_status == DIR_CREATE && build_dirs == 0) ||
224b974a189Sxsa 		    cf->file_status == FILE_UNKNOWN) {
2256ac6a1c7Sjoris 		cf->file_status = FILE_SKIP;
226a10a48e5Stobias 	} else if (reset_tag) {
227b9fc9a72Sderaadt 		(void)xsnprintf(fpath, PATH_MAX, "%s/%s",
2285dd120b0Sjoris 		    cf->file_path, CVS_PATH_TAG);
2295dd120b0Sjoris 		(void)unlink(fpath);
2305dd120b0Sjoris 	} else {
2317f0c79fcSjoris 		if (cvs_specified_tag != NULL || cvs_specified_date != -1)
2325dd120b0Sjoris 			cvs_write_tagfile(cf->file_path,
2332dec954eStobias 				    cvs_specified_tag, NULL);
2343ad3fb45Sjoris 	}
235caf8b85dSjfb }
236caf8b85dSjfb 
2373ad3fb45Sjoris void
cvs_update_leavedir(struct cvs_file * cf)2389f820608Sjoris cvs_update_leavedir(struct cvs_file *cf)
2399f820608Sjoris {
2409f820608Sjoris 	int nbytes;
2417cf716d8Sxsa 	int isempty;
2429f820608Sjoris 	size_t bufsize;
2439f820608Sjoris 	struct stat st;
2449f820608Sjoris 	struct dirent *dp;
2459f820608Sjoris 	char *buf, *ebuf, *cp;
2469f820608Sjoris 	CVSENTRIES *entlist;
2479f820608Sjoris 
2486ac6a1c7Sjoris 	cvs_log(LP_TRACE, "cvs_update_leavedir(%s)", cf->file_path);
2496ac6a1c7Sjoris 
2502832e534Sjoris 	if (cvs_server_active == 1 && !strcmp(cf->file_name, "."))
2512832e534Sjoris 		return;
2522832e534Sjoris 
253c2bd8c51Sjoris 	entlist = cvs_ent_open(cf->file_path);
254c2bd8c51Sjoris 	if (!TAILQ_EMPTY(&(entlist->cef_ent))) {
255c2bd8c51Sjoris 		isempty = 0;
256c2bd8c51Sjoris 		goto prune_it;
257c2bd8c51Sjoris 	}
258c2bd8c51Sjoris 
2599f820608Sjoris 	if (fstat(cf->fd, &st) == -1)
2609f820608Sjoris 		fatal("cvs_update_leavedir: %s", strerror(errno));
2619f820608Sjoris 
262ae886706Smillert 	if ((uintmax_t)st.st_size > SIZE_MAX)
2631b5598b0Sjoris 		fatal("cvs_update_leavedir: %s: file size too big",
2641b5598b0Sjoris 		    cf->file_name);
265fcb086e6Stobias 
266ae886706Smillert 	bufsize = (st.st_size > st.st_blksize) ? st.st_size : st.st_blksize;
267ae886706Smillert 
2689f820608Sjoris 	isempty = 1;
2699f820608Sjoris 	buf = xmalloc(bufsize);
2709f820608Sjoris 
2710c985af6Sjoris 	if (lseek(cf->fd, 0, SEEK_SET) == -1)
2729f820608Sjoris 		fatal("cvs_update_leavedir: %s", strerror(errno));
2739f820608Sjoris 
27442354d6eSzhuk 	while ((nbytes = getdents(cf->fd, buf, bufsize)) > 0) {
2759f820608Sjoris 		ebuf = buf + nbytes;
2769f820608Sjoris 		cp = buf;
2779f820608Sjoris 
2789f820608Sjoris 		while (cp < ebuf) {
2799f820608Sjoris 			dp = (struct dirent *)cp;
2809f820608Sjoris 			if (!strcmp(dp->d_name, ".") ||
2819f820608Sjoris 			    !strcmp(dp->d_name, "..") ||
282a674b1cbSjoris 			    dp->d_fileno == 0) {
2839f820608Sjoris 				cp += dp->d_reclen;
2849f820608Sjoris 				continue;
2859f820608Sjoris 			}
2869f820608Sjoris 
287c2bd8c51Sjoris 			if (strcmp(dp->d_name, CVS_PATH_CVSDIR))
2889f820608Sjoris 				isempty = 0;
2899f820608Sjoris 
2909f820608Sjoris 			if (isempty == 0)
2919f820608Sjoris 				break;
2929f820608Sjoris 
2939f820608Sjoris 			cp += dp->d_reclen;
2949f820608Sjoris 		}
2959f820608Sjoris 	}
2969f820608Sjoris 
2979f820608Sjoris 	if (nbytes == -1)
2989f820608Sjoris 		fatal("cvs_update_leavedir: %s", strerror(errno));
2999f820608Sjoris 
300397ddb8aSnicm 	free(buf);
3019f820608Sjoris 
302c2bd8c51Sjoris prune_it:
3031b5598b0Sjoris 	if ((isempty == 1 && prune_dirs == 1) ||
3041b5598b0Sjoris 	    (cvs_server_active == 1 && cvs_cmdop == CVS_OP_CHECKOUT)) {
30525654a94Sjoris 		/* XXX */
3069f820608Sjoris 		cvs_rmdir(cf->file_path);
3079f820608Sjoris 
3085e1effbaStobias 		if (cvs_server_active == 0 && cvs_cmdop != CVS_OP_EXPORT) {
3099f820608Sjoris 			entlist = cvs_ent_open(cf->file_wd);
3109f820608Sjoris 			cvs_ent_remove(entlist, cf->file_name);
3119f820608Sjoris 		}
3129f820608Sjoris 	}
3130c7db890Sjoris }
3149f820608Sjoris 
3159f820608Sjoris void
cvs_update_local(struct cvs_file * cf)3163ad3fb45Sjoris cvs_update_local(struct cvs_file *cf)
3173ad3fb45Sjoris {
3183ad3fb45Sjoris 	CVSENTRIES *entlist;
31925d391d7Sjoris 	int ent_kflag, rcs_kflag, ret, flags;
32025d391d7Sjoris 	char *tag, rbuf[CVS_REV_BUFSZ];
3213ad3fb45Sjoris 
3223ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path);
3233ad3fb45Sjoris 
3243ad3fb45Sjoris 	if (cf->file_type == CVS_DIR) {
3255e1effbaStobias 		if (cf->file_status == FILE_SKIP) {
3265e1effbaStobias 			if (cvs_cmdop == CVS_OP_EXPORT && verbosity > 0)
3275e1effbaStobias 				cvs_printf("? %s\n", cf->file_path);
328cabc4131Sjoris 			return;
3295e1effbaStobias 		}
330cabc4131Sjoris 
3313ad3fb45Sjoris 		if (cf->file_status != FILE_UNKNOWN &&
3323ad3fb45Sjoris 		    verbosity > 1)
333177ac2baSjoris 			cvs_log(LP_ERR, "Updating %s", cf->file_path);
3343ad3fb45Sjoris 		return;
335caf8b85dSjfb 	}
336caf8b85dSjfb 
3376fba94e7Sjoris 	flags = 0;
3385d320860Sjoris 	if (cvs_specified_tag != NULL)
3395d320860Sjoris 		tag = cvs_specified_tag;
3405d320860Sjoris 	else
3415d320860Sjoris 		tag = cvs_directory_tag;
3425d320860Sjoris 
3435d320860Sjoris 	cvs_file_classify(cf, tag);
3445d320860Sjoris 
3458cbe204aStobias 	if (kflag && cf->file_rcs != NULL)
34637fdff3fStobias 		rcs_kwexp_set(cf->file_rcs, kflag);
34737fdff3fStobias 
3488c400eebSjoris 	if ((cf->file_status == FILE_UPTODATE ||
3498c400eebSjoris 	    cf->file_status == FILE_MODIFIED) && cf->file_ent != NULL &&
350a10a48e5Stobias 	    cf->file_ent->ce_tag != NULL && reset_tag) {
3518c400eebSjoris 		if (cf->file_status == FILE_MODIFIED)
3528c400eebSjoris 			cf->file_status = FILE_MERGE;
3538c400eebSjoris 		else
3546fba94e7Sjoris 			cf->file_status = FILE_CHECKOUT;
35551ef6581Sjoris 
356e28eda4eStobias 		if ((cf->file_rcsrev = rcs_head_get(cf->file_rcs)) == NULL)
357e28eda4eStobias 			fatal("no head revision in RCS file for %s",
358e28eda4eStobias 			    cf->file_path);
3595dd120b0Sjoris 
3605dd120b0Sjoris 		/* might be a bit overkill */
3615dd120b0Sjoris 		if (cvs_server_active == 1)
3625dd120b0Sjoris 			cvs_server_clear_sticky(cf->file_wd);
3636fba94e7Sjoris 	}
364caf8b85dSjfb 
365e28eda4eStobias 	if (print_stdout) {
366e28eda4eStobias 		if (cf->file_status != FILE_UNKNOWN && cf->file_rcs != NULL &&
367f3b6a477Stobias 		    cf->file_rcsrev != NULL && !cf->file_rcs->rf_dead &&
368f3b6a477Stobias 		    (cf->file_flags & FILE_HAS_TAG)) {
369b17342c4Sreyk 			rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf));
370e8755a22Stobias 			if (verbosity > 1) {
371e8755a22Stobias 				cvs_log(LP_RCS, RCS_DIFF_DIV);
372e28eda4eStobias 				cvs_log(LP_RCS, "Checking out %s",
373e28eda4eStobias 				    cf->file_path);
374e8755a22Stobias 				cvs_log(LP_RCS, "RCS:  %s", cf->file_rpath);
375e8755a22Stobias 				cvs_log(LP_RCS, "VERS: %s", rbuf);
376e8755a22Stobias 				cvs_log(LP_RCS, "***************");
377e8755a22Stobias 			}
3785d320860Sjoris 			cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_DUMP);
379e28eda4eStobias 		}
380b17342c4Sreyk 		return;
381b17342c4Sreyk 	}
382b17342c4Sreyk 
38337fdff3fStobias 	if (cf->file_ent != NULL) {
38437fdff3fStobias 		if (cf->file_ent->ce_opts == NULL) {
38537fdff3fStobias 			if (kflag)
38637fdff3fStobias 				cf->file_status = FILE_CHECKOUT;
387e28eda4eStobias 		} else if (cf->file_rcs != NULL) {
3886a64c583Stobias 			if (strlen(cf->file_ent->ce_opts) < 3)
3896a64c583Stobias 				fatal("malformed option for file %s",
3906a64c583Stobias 				    cf->file_path);
39137fdff3fStobias 
3926a64c583Stobias 			ent_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2);
3936a64c583Stobias 			rcs_kflag = rcs_kwexp_get(cf->file_rcs);
3946a64c583Stobias 
3956a64c583Stobias 			if ((kflag && (kflag != ent_kflag)) ||
3966a64c583Stobias 			    (reset_option && (ent_kflag != rcs_kflag)))
39737fdff3fStobias 				cf->file_status = FILE_CHECKOUT;
39837fdff3fStobias 		}
39937fdff3fStobias 	}
40037fdff3fStobias 
4013ad3fb45Sjoris 	switch (cf->file_status) {
4023ad3fb45Sjoris 	case FILE_UNKNOWN:
4033ad3fb45Sjoris 		cvs_printf("? %s\n", cf->file_path);
4045191afffSjoris 		break;
4053ad3fb45Sjoris 	case FILE_MODIFIED:
406daa36f23Snicm 		if (backup_local_changes) {
407daa36f23Snicm 			cvs_backup_file(cf);
408daa36f23Snicm 
409daa36f23Snicm 			cvs_checkout_file(cf, cf->file_rcsrev, NULL, flags);
410daa36f23Snicm 			cvs_printf("U %s\n", cf->file_path);
411f9872b43Sjoris 		} else {
412daa36f23Snicm 			ret = update_has_conflict_markers(cf);
413daa36f23Snicm 			if (cf->file_ent->ce_conflict != NULL && ret == 1)
414daa36f23Snicm 				cvs_printf("C %s\n", cf->file_path);
415daa36f23Snicm 			else {
416f9872b43Sjoris 				if (cf->file_ent->ce_conflict != NULL && ret == 0)
417f9872b43Sjoris 					update_clear_conflict(cf);
4183ad3fb45Sjoris 				cvs_printf("M %s\n", cf->file_path);
419f9872b43Sjoris 			}
420daa36f23Snicm 		}
4215191afffSjoris 		break;
4223ad3fb45Sjoris 	case FILE_ADDED:
4233ad3fb45Sjoris 		cvs_printf("A %s\n", cf->file_path);
4245191afffSjoris 		break;
4253ad3fb45Sjoris 	case FILE_REMOVED:
4263ad3fb45Sjoris 		cvs_printf("R %s\n", cf->file_path);
4275191afffSjoris 		break;
4283ad3fb45Sjoris 	case FILE_CONFLICT:
4293ad3fb45Sjoris 		cvs_printf("C %s\n", cf->file_path);
4305191afffSjoris 		break;
4313ad3fb45Sjoris 	case FILE_LOST:
4323ad3fb45Sjoris 	case FILE_CHECKOUT:
4333ad3fb45Sjoris 	case FILE_PATCH:
434b87788a5Stobias 		if (!reset_tag && (tag != NULL || cvs_specified_date != -1 ||
435b87788a5Stobias 		    cvs_directory_date != -1 || (cf->file_ent != NULL &&
436b87788a5Stobias 		    cf->file_ent->ce_tag != NULL)))
4376fba94e7Sjoris 			flags = CO_SETSTICKY;
4386fba94e7Sjoris 
439d6f09affSnicm 		if (cf->file_flags & FILE_ON_DISK && (cf->file_ent == NULL ||
440d6f09affSnicm 		    cf->file_ent->ce_type == CVS_ENT_NONE)) {
441d6f09affSnicm 			cvs_log(LP_ERR, "move away %s; it is in the way",
442d6f09affSnicm 			    cf->file_path);
443d6f09affSnicm 			cvs_printf("C %s\n", cf->file_path);
444d6f09affSnicm 		} else {
4455d320860Sjoris 			cvs_checkout_file(cf, cf->file_rcsrev, tag, flags);
4463ad3fb45Sjoris 			cvs_printf("U %s\n", cf->file_path);
4473901dfa5Sjoris 			cvs_history_add(CVS_HISTORY_UPDATE_CO, cf, NULL);
448d6f09affSnicm 		}
4493ad3fb45Sjoris 		break;
4503ad3fb45Sjoris 	case FILE_MERGE:
45125d391d7Sjoris 		d3rev1 = cf->file_ent->ce_rev;
45225d391d7Sjoris 		d3rev2 = cf->file_rcsrev;
4535d320860Sjoris 		cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_MERGE);
454f9872b43Sjoris 
455f9872b43Sjoris 		if (diff3_conflicts != 0) {
456f9872b43Sjoris 			cvs_printf("C %s\n", cf->file_path);
4573901dfa5Sjoris 			cvs_history_add(CVS_HISTORY_UPDATE_MERGED_ERR,
4583901dfa5Sjoris 			    cf, NULL);
459f9872b43Sjoris 		} else {
460f9872b43Sjoris 			update_clear_conflict(cf);
461f9872b43Sjoris 			cvs_printf("M %s\n", cf->file_path);
4623901dfa5Sjoris 			cvs_history_add(CVS_HISTORY_UPDATE_MERGED, cf, NULL);
463f9872b43Sjoris 		}
4643ad3fb45Sjoris 		break;
4653ad3fb45Sjoris 	case FILE_UNLINK:
4663ad3fb45Sjoris 		(void)unlink(cf->file_path);
4673ad3fb45Sjoris 	case FILE_REMOVE_ENTRY:
4683ad3fb45Sjoris 		entlist = cvs_ent_open(cf->file_wd);
4693ad3fb45Sjoris 		cvs_ent_remove(entlist, cf->file_name);
4703901dfa5Sjoris 		cvs_history_add(CVS_HISTORY_UPDATE_REMOVE, cf, NULL);
47117993735Sjoris 
47217993735Sjoris 		if (cvs_server_active == 1)
47317993735Sjoris 			cvs_checkout_file(cf, cf->file_rcsrev, tag, CO_REMOVE);
4745191afffSjoris 		break;
47551ef6581Sjoris 	case FILE_UPTODATE:
47651ef6581Sjoris 		if (cvs_cmdop != CVS_OP_UPDATE)
47751ef6581Sjoris 			break;
47851ef6581Sjoris 
4798357a73fSfcambus 		if (reset_tag != 1 && reset_option != 1 &&
4808357a73fSfcambus 		    cvs_specified_tag == NULL && cvs_specified_date == -1)
4819159bf17Sjoris 			break;
4829159bf17Sjoris 
4839159bf17Sjoris 		if (cf->file_rcs->rf_dead != 1 &&
4849159bf17Sjoris 		    (cf->file_flags & FILE_HAS_TAG))
4855d320860Sjoris 			cvs_checkout_file(cf, cf->file_rcsrev,
4865d320860Sjoris 			    tag, CO_SETSTICKY);
48751ef6581Sjoris 		break;
4885191afffSjoris 	default:
4895191afffSjoris 		break;
4905191afffSjoris 	}
49125d391d7Sjoris 
49225d391d7Sjoris 	if (cvs_join_rev1 != NULL)
49325d391d7Sjoris 		update_join_file(cf);
494caf8b85dSjfb }
495f9872b43Sjoris 
496f9872b43Sjoris static void
update_clear_conflict(struct cvs_file * cf)497f9872b43Sjoris update_clear_conflict(struct cvs_file *cf)
498f9872b43Sjoris {
499f9872b43Sjoris 	CVSENTRIES *entlist;
50009d28507Stobias 	char *entry, revbuf[CVS_REV_BUFSZ];
50125d391d7Sjoris 	char sticky[CVS_ENT_MAXLINELEN], opt[4];
502f9872b43Sjoris 
503f9872b43Sjoris 	cvs_log(LP_TRACE, "update_clear_conflict(%s)", cf->file_path);
504f9872b43Sjoris 
50598f0a3cdSjoris 	rcsnum_tostr(cf->file_rcsrev, revbuf, sizeof(revbuf));
506f9872b43Sjoris 
507b0b4b42cStobias 	sticky[0] = '\0';
50825d391d7Sjoris 	if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
509b0b4b42cStobias 		(void)xsnprintf(sticky, sizeof(sticky), "T%s",
510b0b4b42cStobias 		    cf->file_ent->ce_tag);
511b0b4b42cStobias 
51225d391d7Sjoris 	opt[0] = '\0';
51325d391d7Sjoris 	if (cf->file_ent != NULL && cf->file_ent->ce_opts != NULL)
51425d391d7Sjoris 		strlcpy(opt, cf->file_ent->ce_opts, sizeof(opt));
51525d391d7Sjoris 
516f9872b43Sjoris 	entry = xmalloc(CVS_ENT_MAXLINELEN);
51709d28507Stobias 	cvs_ent_line_str(cf->file_name, revbuf, "Result of merge",
51825d391d7Sjoris 	    opt[0] != '\0' ? opt : "", sticky, 0, 0,
519ae83823aSxsa 	    entry, CVS_ENT_MAXLINELEN);
520f9872b43Sjoris 
521f9872b43Sjoris 	entlist = cvs_ent_open(cf->file_wd);
522f9872b43Sjoris 	cvs_ent_add(entlist, entry);
523397ddb8aSnicm 	free(entry);
524f9872b43Sjoris }
525f9872b43Sjoris 
526f9872b43Sjoris /*
527f9872b43Sjoris  * XXX - this is the way GNU cvs checks for outstanding conflicts
528f9872b43Sjoris  * in a file after a merge. It is a very very bad approach and
529f9872b43Sjoris  * should be looked at once opencvs is working decently.
530f9872b43Sjoris  */
531f9872b43Sjoris int
update_has_conflict_markers(struct cvs_file * cf)532f9872b43Sjoris update_has_conflict_markers(struct cvs_file *cf)
533f9872b43Sjoris {
534f9872b43Sjoris 	BUF *bp;
535f9872b43Sjoris 	int conflict;
536f9872b43Sjoris 	char *content;
5377bb3ddb0Sray 	struct rcs_line *lp;
5387bb3ddb0Sray 	struct rcs_lines *lines;
53931cfd684Sniallo 	size_t len;
540f9872b43Sjoris 
541f9872b43Sjoris 	cvs_log(LP_TRACE, "update_has_conflict_markers(%s)", cf->file_path);
542f9872b43Sjoris 
5435cf15c45Sjoris 	if (!(cf->file_flags & FILE_ON_DISK) || cf->file_ent == NULL)
544846e2b74Sjoris 		return (0);
545846e2b74Sjoris 
5467bb3ddb0Sray 	bp = buf_load_fd(cf->fd);
547f9872b43Sjoris 
5487bb3ddb0Sray 	buf_putc(bp, '\0');
5497bb3ddb0Sray 	len = buf_len(bp);
5507bb3ddb0Sray 	content = buf_release(bp);
55131cfd684Sniallo 	if ((lines = cvs_splitlines(content, len)) == NULL)
552f9872b43Sjoris 		fatal("update_has_conflict_markers: failed to split lines");
553f9872b43Sjoris 
554f9872b43Sjoris 	conflict = 0;
555f9872b43Sjoris 	TAILQ_FOREACH(lp, &(lines->l_lines), l_list) {
556f9872b43Sjoris 		if (lp->l_line == NULL)
557f9872b43Sjoris 			continue;
558f9872b43Sjoris 
559f9872b43Sjoris 		if (!strncmp(lp->l_line, RCS_CONFLICT_MARKER1,
560870158f9Stobias 		    sizeof(RCS_CONFLICT_MARKER1) - 1) ||
561f9872b43Sjoris 		    !strncmp(lp->l_line, RCS_CONFLICT_MARKER2,
562870158f9Stobias 		    sizeof(RCS_CONFLICT_MARKER2) - 1) ||
563f9872b43Sjoris 		    !strncmp(lp->l_line, RCS_CONFLICT_MARKER3,
564870158f9Stobias 		    sizeof(RCS_CONFLICT_MARKER3) - 1)) {
565f9872b43Sjoris 			conflict = 1;
566f9872b43Sjoris 			break;
567f9872b43Sjoris 		}
568f9872b43Sjoris 	}
569f9872b43Sjoris 
570f9872b43Sjoris 	cvs_freelines(lines);
571397ddb8aSnicm 	free(content);
572f9872b43Sjoris 	return (conflict);
573f9872b43Sjoris }
57425d391d7Sjoris 
57525d391d7Sjoris void
update_join_file(struct cvs_file * cf)57625d391d7Sjoris update_join_file(struct cvs_file *cf)
57725d391d7Sjoris {
57825d391d7Sjoris 	time_t told;
57925d391d7Sjoris 	RCSNUM *rev1, *rev2;
58025d391d7Sjoris 	const char *state1, *state2;
58125d391d7Sjoris 	char rbuf[CVS_REV_BUFSZ], *jrev1, *jrev2, *p;
58225d391d7Sjoris 
58325d391d7Sjoris 	rev1 = rev2 = NULL;
58425d391d7Sjoris 	jrev1 = jrev2 = NULL;
58525d391d7Sjoris 
58625d391d7Sjoris 	jrev1 = xstrdup(cvs_join_rev1);
58725d391d7Sjoris 	if (cvs_join_rev2 != NULL)
58825d391d7Sjoris 		jrev2 = xstrdup(cvs_join_rev2);
58925d391d7Sjoris 
59025d391d7Sjoris 	if (jrev2 == NULL) {
59125d391d7Sjoris 		jrev2 = jrev1;
59225d391d7Sjoris 		jrev1 = NULL;
59325d391d7Sjoris 	}
59425d391d7Sjoris 
59525d391d7Sjoris 	told = cvs_specified_date;
59625d391d7Sjoris 
5973834926fSjoris 	if ((p = strchr(jrev2, ':')) != NULL) {
59825d391d7Sjoris 		(*p++) = '\0';
599092db204Sray 		if ((cvs_specified_date = date_parse(p)) == -1) {
600092db204Sray 			cvs_printf("invalid date: %s", p);
601092db204Sray 			goto out;
602092db204Sray 		}
60325d391d7Sjoris 	}
60425d391d7Sjoris 
60525d391d7Sjoris 	rev2 = rcs_translate_tag(jrev2, cf->file_rcs);
60625d391d7Sjoris 	cvs_specified_date = told;
60725d391d7Sjoris 
60825d391d7Sjoris 	if (jrev1 != NULL) {
6093834926fSjoris 		if ((p = strchr(jrev1, ':')) != NULL) {
61025d391d7Sjoris 			(*p++) = '\0';
611092db204Sray 			if ((cvs_specified_date = date_parse(p)) == -1) {
612092db204Sray 				cvs_printf("invalid date: %s", p);
613092db204Sray 				goto out;
614092db204Sray 			}
61525d391d7Sjoris 		}
61625d391d7Sjoris 
61725d391d7Sjoris 		rev1 = rcs_translate_tag(jrev1, cf->file_rcs);
61825d391d7Sjoris 		cvs_specified_date = told;
61925d391d7Sjoris 	} else {
62025d391d7Sjoris 		if (rev2 == NULL)
62125d391d7Sjoris 			goto out;
62225d391d7Sjoris 
62325d391d7Sjoris 		rev1 = rcsnum_alloc();
62425d391d7Sjoris 		rcsnum_cpy(cf->file_rcsrev, rev1, 0);
62525d391d7Sjoris 	}
62625d391d7Sjoris 
62725d391d7Sjoris 	state1 = state2 = RCS_STATE_DEAD;
62825d391d7Sjoris 
62925d391d7Sjoris 	if (rev1 != NULL)
63025d391d7Sjoris 		state1 = rcs_state_get(cf->file_rcs, rev1);
63125d391d7Sjoris 	if (rev2 != NULL)
63225d391d7Sjoris 		state2 = rcs_state_get(cf->file_rcs, rev2);
63325d391d7Sjoris 
63425d391d7Sjoris 	if (rev2 == NULL || !strcmp(state2, RCS_STATE_DEAD)) {
63525d391d7Sjoris 		if (rev1 == NULL || !strcmp(state1, RCS_STATE_DEAD))
63625d391d7Sjoris 			goto out;
63725d391d7Sjoris 
63825d391d7Sjoris 		if (cf->file_status == FILE_REMOVED ||
63925d391d7Sjoris 		    cf->file_rcs->rf_dead == 1)
64025d391d7Sjoris 			goto out;
64125d391d7Sjoris 
64225d391d7Sjoris 		if (cf->file_status == FILE_MODIFIED ||
64325d391d7Sjoris 		    cf->file_status == FILE_ADDED)
64425d391d7Sjoris 			goto out;
64525d391d7Sjoris 
64625d391d7Sjoris 		(void)unlink(cf->file_path);
64725d391d7Sjoris 		(void)close(cf->fd);
64825d391d7Sjoris 		cf->fd = -1;
64925d391d7Sjoris 		cvs_remove_local(cf);
65025d391d7Sjoris 		goto out;
65125d391d7Sjoris 	}
65225d391d7Sjoris 
65325d391d7Sjoris 	if (cf->file_ent != NULL) {
65425d391d7Sjoris 		if (!rcsnum_cmp(cf->file_ent->ce_rev, rev2, 0))
65525d391d7Sjoris 			goto out;
65625d391d7Sjoris 	}
65725d391d7Sjoris 
658e28eda4eStobias 	if (cf->file_rcsrev == NULL) {
659e28eda4eStobias 		cvs_printf("non-mergable file: %s has no head revision!\n",
660e28eda4eStobias 		    cf->file_path);
661e28eda4eStobias 		goto out;
662e28eda4eStobias 	}
663e28eda4eStobias 
66425d391d7Sjoris 	if (rev1 == NULL || !strcmp(state1, RCS_STATE_DEAD)) {
6655cf15c45Sjoris 		if (cf->file_flags & FILE_ON_DISK) {
66625d391d7Sjoris 			cvs_printf("%s exists but has been added in %s\n",
6677cee39ecSjoris 			    cf->file_path, jrev2);
66825d391d7Sjoris 		} else {
6697cee39ecSjoris 			cvs_printf("A %s\n", cf->file_path);
67025d391d7Sjoris 			cvs_checkout_file(cf, cf->file_rcsrev, NULL, 0);
67125d391d7Sjoris 			cvs_add_local(cf);
67225d391d7Sjoris 		}
67325d391d7Sjoris 		goto out;
67425d391d7Sjoris 	}
67525d391d7Sjoris 
6767cee39ecSjoris 	if (!rcsnum_cmp(rev1, rev2, 0))
67725d391d7Sjoris 		goto out;
67825d391d7Sjoris 
6795cf15c45Sjoris 	if (!(cf->file_flags & FILE_ON_DISK)) {
6807cee39ecSjoris 		cvs_printf("%s does not exist but is present in %s\n",
6817cee39ecSjoris 		    cf->file_path, jrev2);
6827cee39ecSjoris 		goto out;
6837cee39ecSjoris 	}
6847cee39ecSjoris 
685f2a06cf4Stobias 	if (rcs_kwexp_get(cf->file_rcs) & RCS_KWEXP_NONE) {
68625d391d7Sjoris 		cvs_printf("non-mergable file: %s needs merge!\n",
68725d391d7Sjoris 		    cf->file_path);
68825d391d7Sjoris 		goto out;
68925d391d7Sjoris 	}
69025d391d7Sjoris 
69125d391d7Sjoris 	cvs_printf("joining ");
69225d391d7Sjoris 	rcsnum_tostr(rev1, rbuf, sizeof(rbuf));
69325d391d7Sjoris 	cvs_printf("%s ", rbuf);
69425d391d7Sjoris 
69525d391d7Sjoris 	rcsnum_tostr(rev2, rbuf, sizeof(rbuf));
69625d391d7Sjoris 	cvs_printf("%s ", rbuf);
69725d391d7Sjoris 
69825d391d7Sjoris 	rcsnum_tostr(cf->file_rcsrev, rbuf, sizeof(rbuf));
69925d391d7Sjoris 	cvs_printf("into %s (%s)\n", cf->file_path, rbuf);
70025d391d7Sjoris 
70125d391d7Sjoris 	d3rev1 = rev1;
70225d391d7Sjoris 	d3rev2 = rev2;
70325d391d7Sjoris 	cvs_checkout_file(cf, cf->file_rcsrev, NULL, CO_MERGE);
70425d391d7Sjoris 
705fea2e6e6Sjoris 	if (diff3_conflicts == 0)
70625d391d7Sjoris 		update_clear_conflict(cf);
70725d391d7Sjoris 
70825d391d7Sjoris out:
70953ce2177Sfcambus 	free(rev1);
71053ce2177Sfcambus 	free(rev2);
711397ddb8aSnicm 	free(jrev1);
712397ddb8aSnicm 	free(jrev2);
71325d391d7Sjoris }
714daa36f23Snicm 
715daa36f23Snicm void
cvs_backup_file(struct cvs_file * cf)716daa36f23Snicm cvs_backup_file(struct cvs_file *cf)
717daa36f23Snicm {
718b9fc9a72Sderaadt 	char	 backup_name[PATH_MAX];
7198fe0da22Stedu 	char	 revstr[RCSNUM_MAXSTR];
720daa36f23Snicm 
721daa36f23Snicm 	if (cf->file_status == FILE_ADDED)
7228fe0da22Stedu 		(void)xsnprintf(revstr, sizeof(revstr), "0");
723daa36f23Snicm 	else
7248fe0da22Stedu 		rcsnum_tostr(cf->file_ent->ce_rev, revstr, sizeof(revstr));
725daa36f23Snicm 
726b9fc9a72Sderaadt 	(void)xsnprintf(backup_name, PATH_MAX, "%s/.#%s.%s",
727daa36f23Snicm 	    cf->file_wd, cf->file_name, revstr);
728daa36f23Snicm 
729daa36f23Snicm 	cvs_file_copy(cf->file_path, backup_name);
730daa36f23Snicm 
731b9fc9a72Sderaadt 	(void)xsnprintf(backup_name, PATH_MAX, ".#%s.%s",
732daa36f23Snicm 	    cf->file_name, revstr);
733daa36f23Snicm 	cvs_printf("(Locally modified %s moved to %s)\n",
734daa36f23Snicm 		   cf->file_name, backup_name);
735daa36f23Snicm }
736