xref: /openbsd-src/usr.bin/cvs/edit.c (revision 4dcde51379711c241d4d9b93f9d4e9eae2d51f3d)
1*4dcde513Sjoris /*	$OpenBSD: edit.c,v 1.53 2017/06/01 08:08:24 joris Exp $	*/
25a31a4e8Sxsa /*
34aab05f0Sxsa  * Copyright (c) 2006, 2007 Xavier Santolaria <xsa@openbsd.org>
45a31a4e8Sxsa  *
55a31a4e8Sxsa  * Permission to use, copy, modify, and distribute this software for any
65a31a4e8Sxsa  * purpose with or without fee is hereby granted, provided that the above
75a31a4e8Sxsa  * copyright notice and this permission notice appear in all copies.
85a31a4e8Sxsa  *
95a31a4e8Sxsa  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
105a31a4e8Sxsa  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
115a31a4e8Sxsa  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
125a31a4e8Sxsa  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
135a31a4e8Sxsa  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
145a31a4e8Sxsa  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
155a31a4e8Sxsa  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
165a31a4e8Sxsa  */
175a31a4e8Sxsa 
181f8531bdSotto #include <sys/stat.h>
191f8531bdSotto 
201f8531bdSotto #include <errno.h>
21397ddb8aSnicm #include <stdlib.h>
221f8531bdSotto #include <string.h>
236534056aStobias #include <time.h>
241f8531bdSotto #include <unistd.h>
255a31a4e8Sxsa 
265a31a4e8Sxsa #include "cvs.h"
275a31a4e8Sxsa #include "remote.h"
285a31a4e8Sxsa 
2933c4ed80Sxsa #define E_COMMIT	0x01
3033c4ed80Sxsa #define E_EDIT		0x02
3133c4ed80Sxsa #define E_UNEDIT	0x04
3233c4ed80Sxsa #define E_ALL		(E_EDIT|E_COMMIT|E_UNEDIT)
3333c4ed80Sxsa 
34de08069bSxsa #define BASE_ADD	0x01
35de08069bSxsa #define BASE_GET	0x02
36de08069bSxsa #define BASE_REMOVE	0x04
37de08069bSxsa 
3833c4ed80Sxsa static void	cvs_edit_local(struct cvs_file *);
395a31a4e8Sxsa static void	cvs_editors_local(struct cvs_file *);
404aab05f0Sxsa static void	cvs_unedit_local(struct cvs_file *);
415a31a4e8Sxsa 
42de08069bSxsa static RCSNUM	*cvs_base_handle(struct cvs_file *, int);
43de08069bSxsa 
4433c4ed80Sxsa static int	edit_aflags = 0;
4533c4ed80Sxsa 
4633c4ed80Sxsa struct cvs_cmd cvs_cmd_edit = {
47f331ff59Stobias 	CVS_OP_EDIT, CVS_USE_WDIR, "edit",
487eeb4908Sragge 	{ { 0 }, { 0 } },
4933c4ed80Sxsa 	"Get ready to edit a watched file",
5033c4ed80Sxsa 	"[-lR] [-a action] [file ...]",
5133c4ed80Sxsa 	"a:lR",
5233c4ed80Sxsa 	NULL,
5333c4ed80Sxsa 	cvs_edit
5433c4ed80Sxsa };
5533c4ed80Sxsa 
565a31a4e8Sxsa struct cvs_cmd cvs_cmd_editors = {
57f331ff59Stobias 	CVS_OP_EDITORS, CVS_USE_WDIR, "editors",
587eeb4908Sragge 	{ { 0 }, { 0 } },
595a31a4e8Sxsa 	"See who is editing a watched file",
605a31a4e8Sxsa 	"[-lR] [file ...]",
615a31a4e8Sxsa 	"lR",
625a31a4e8Sxsa 	NULL,
635a31a4e8Sxsa 	cvs_editors
645a31a4e8Sxsa };
655a31a4e8Sxsa 
664aab05f0Sxsa struct cvs_cmd cvs_cmd_unedit = {
67f331ff59Stobias 	CVS_OP_UNEDIT, CVS_USE_WDIR, "unedit",
687eeb4908Sragge 	{ { 0 }, { 0 } },
694aab05f0Sxsa 	"Undo an edit command",
704aab05f0Sxsa 	"[-lR] [file ...]",
714aab05f0Sxsa 	"lR",
724aab05f0Sxsa 	NULL,
734aab05f0Sxsa 	cvs_unedit
744aab05f0Sxsa };
754aab05f0Sxsa 
765a31a4e8Sxsa int
cvs_edit(int argc,char ** argv)7733c4ed80Sxsa cvs_edit(int argc, char **argv)
7833c4ed80Sxsa {
7933c4ed80Sxsa 	int ch;
8033c4ed80Sxsa 	int flags;
8133c4ed80Sxsa 	struct cvs_recursion cr;
8233c4ed80Sxsa 
8333c4ed80Sxsa 	flags = CR_RECURSE_DIRS;
8433c4ed80Sxsa 
8533c4ed80Sxsa 	while ((ch = getopt(argc, argv, cvs_cmd_edit.cmd_opts)) != -1) {
8633c4ed80Sxsa 		switch (ch) {
8733c4ed80Sxsa 		case 'a':
8833c4ed80Sxsa 			if (strcmp(optarg, "edit") == 0)
8933c4ed80Sxsa 				edit_aflags |= E_EDIT;
9033c4ed80Sxsa 			else if (strcmp(optarg, "unedit") == 0)
9133c4ed80Sxsa 				edit_aflags |= E_UNEDIT;
9233c4ed80Sxsa 			else if (strcmp(optarg, "commit") == 0)
9333c4ed80Sxsa 				edit_aflags |= E_COMMIT;
9433c4ed80Sxsa 			else if (strcmp(optarg, "all") == 0)
9533c4ed80Sxsa 				edit_aflags |= E_ALL;
9633c4ed80Sxsa 			else if (strcmp(optarg, "none") == 0)
9733c4ed80Sxsa 				edit_aflags &= ~E_ALL;
9833c4ed80Sxsa 			else
9933c4ed80Sxsa 				fatal("%s", cvs_cmd_edit.cmd_synopsis);
10033c4ed80Sxsa 			break;
10133c4ed80Sxsa 		case 'l':
10233c4ed80Sxsa 			flags &= ~CR_RECURSE_DIRS;
10333c4ed80Sxsa 			break;
10433c4ed80Sxsa 		case 'R':
105bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
10633c4ed80Sxsa 			break;
10733c4ed80Sxsa 		default:
10833c4ed80Sxsa 			fatal("%s", cvs_cmd_edit.cmd_synopsis);
10933c4ed80Sxsa 		}
11033c4ed80Sxsa 	}
11133c4ed80Sxsa 
11233c4ed80Sxsa 	argc -= optind;
11333c4ed80Sxsa 	argv += optind;
11433c4ed80Sxsa 
11533c4ed80Sxsa 	if (argc == 0)
11633c4ed80Sxsa 		fatal("%s", cvs_cmd_edit.cmd_synopsis);
11733c4ed80Sxsa 
11833c4ed80Sxsa 	if (edit_aflags == 0)
11933c4ed80Sxsa 		edit_aflags |= E_ALL;
12033c4ed80Sxsa 
12133c4ed80Sxsa 	cr.enterdir = NULL;
12233c4ed80Sxsa 	cr.leavedir = NULL;
12333c4ed80Sxsa 
124*4dcde513Sjoris 	if (cvsroot_is_remote()) {
12580f6ca9bSjoris 		cvs_client_connect_to_server();
12633c4ed80Sxsa 		cr.fileproc = cvs_client_sendfile;
12733c4ed80Sxsa 
12833c4ed80Sxsa 		if (!(flags & CR_RECURSE_DIRS))
12933c4ed80Sxsa 			cvs_client_send_request("Argument -l");
13033c4ed80Sxsa 	} else {
13133c4ed80Sxsa 		cr.fileproc = cvs_edit_local;
13233c4ed80Sxsa 	}
13333c4ed80Sxsa 
13433c4ed80Sxsa 	cr.flags = flags;
13533c4ed80Sxsa 
13633c4ed80Sxsa 	cvs_file_run(argc, argv, &cr);
13733c4ed80Sxsa 
138*4dcde513Sjoris 	if (cvsroot_is_remote()) {
13933c4ed80Sxsa 		cvs_client_send_files(argv, argc);
14033c4ed80Sxsa 		cvs_client_senddir(".");
14133c4ed80Sxsa 		cvs_client_send_request("edit");
14233c4ed80Sxsa 		cvs_client_get_responses();
14333c4ed80Sxsa 	}
14433c4ed80Sxsa 
14533c4ed80Sxsa 	return (0);
14633c4ed80Sxsa }
14733c4ed80Sxsa 
14833c4ed80Sxsa int
cvs_editors(int argc,char ** argv)1495a31a4e8Sxsa cvs_editors(int argc, char **argv)
1505a31a4e8Sxsa {
1515a31a4e8Sxsa 	int ch;
1525a31a4e8Sxsa 	int flags;
1535a31a4e8Sxsa 	struct cvs_recursion cr;
1545a31a4e8Sxsa 
1555a31a4e8Sxsa 	flags = CR_RECURSE_DIRS;
1565a31a4e8Sxsa 
1575a31a4e8Sxsa 	while ((ch = getopt(argc, argv, cvs_cmd_editors.cmd_opts)) != -1) {
1585a31a4e8Sxsa 		switch (ch) {
1595a31a4e8Sxsa 		case 'l':
1605a31a4e8Sxsa 			flags &= ~CR_RECURSE_DIRS;
1615a31a4e8Sxsa 			break;
1625a31a4e8Sxsa 		case 'R':
163bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
1645a31a4e8Sxsa 			break;
1655a31a4e8Sxsa 		default:
1665a31a4e8Sxsa 			fatal("%s", cvs_cmd_editors.cmd_synopsis);
1675a31a4e8Sxsa 		}
1685a31a4e8Sxsa 	}
1695a31a4e8Sxsa 
1705a31a4e8Sxsa 	argc -= optind;
1715a31a4e8Sxsa 	argv += optind;
1725a31a4e8Sxsa 
1735a31a4e8Sxsa 	if (argc == 0)
1745a31a4e8Sxsa 		fatal("%s", cvs_cmd_editors.cmd_synopsis);
1755a31a4e8Sxsa 
1765a31a4e8Sxsa 	cr.enterdir = NULL;
1775a31a4e8Sxsa 	cr.leavedir = NULL;
1785a31a4e8Sxsa 
179*4dcde513Sjoris 	if (cvsroot_is_remote()) {
18080f6ca9bSjoris 		cvs_client_connect_to_server();
1815a31a4e8Sxsa 		cr.fileproc = cvs_client_sendfile;
1825a31a4e8Sxsa 
1835a31a4e8Sxsa 		if (!(flags & CR_RECURSE_DIRS))
1845a31a4e8Sxsa 			cvs_client_send_request("Argument -l");
1855a31a4e8Sxsa 	} else {
1865a31a4e8Sxsa 		cr.fileproc = cvs_editors_local;
1875a31a4e8Sxsa 	}
1885a31a4e8Sxsa 
1895a31a4e8Sxsa 	cr.flags = flags;
1905a31a4e8Sxsa 
1915a31a4e8Sxsa 	cvs_file_run(argc, argv, &cr);
1925a31a4e8Sxsa 
193*4dcde513Sjoris 	if (cvsroot_is_remote()) {
1945a31a4e8Sxsa 		cvs_client_send_files(argv, argc);
1955a31a4e8Sxsa 		cvs_client_senddir(".");
1965a31a4e8Sxsa 		cvs_client_send_request("editors");
1975a31a4e8Sxsa 		cvs_client_get_responses();
1985a31a4e8Sxsa 	}
1995a31a4e8Sxsa 
2005a31a4e8Sxsa 	return (0);
2015a31a4e8Sxsa }
2025a31a4e8Sxsa 
2034aab05f0Sxsa int
cvs_unedit(int argc,char ** argv)2044aab05f0Sxsa cvs_unedit(int argc, char **argv)
2054aab05f0Sxsa {
2064aab05f0Sxsa 	int ch;
2074aab05f0Sxsa 	int flags;
2084aab05f0Sxsa 	struct cvs_recursion cr;
2094aab05f0Sxsa 
2104aab05f0Sxsa 	flags = CR_RECURSE_DIRS;
2114aab05f0Sxsa 
2124aab05f0Sxsa 	while ((ch = getopt(argc, argv, cvs_cmd_unedit.cmd_opts)) != -1) {
2134aab05f0Sxsa 		switch (ch) {
2144aab05f0Sxsa 		case 'l':
2154aab05f0Sxsa 			flags &= ~CR_RECURSE_DIRS;
2164aab05f0Sxsa 			break;
2174aab05f0Sxsa 		case 'R':
218bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
2194aab05f0Sxsa 			break;
2204aab05f0Sxsa 		default:
2214aab05f0Sxsa 			fatal("%s", cvs_cmd_unedit.cmd_synopsis);
2224aab05f0Sxsa 		}
2234aab05f0Sxsa 	}
2244aab05f0Sxsa 
2254aab05f0Sxsa 	argc -= optind;
2264aab05f0Sxsa 	argv += optind;
2274aab05f0Sxsa 
2284aab05f0Sxsa 	if (argc == 0)
2294aab05f0Sxsa 		fatal("%s", cvs_cmd_unedit.cmd_synopsis);
2304aab05f0Sxsa 
2314aab05f0Sxsa 	cr.enterdir = NULL;
2324aab05f0Sxsa 	cr.leavedir = NULL;
2334aab05f0Sxsa 
234*4dcde513Sjoris 	if (cvsroot_is_remote()) {
23575010b66Sxsa 		cvs_client_connect_to_server();
2364aab05f0Sxsa 		cr.fileproc = cvs_client_sendfile;
2374aab05f0Sxsa 
2384aab05f0Sxsa 		if (!(flags & CR_RECURSE_DIRS))
2394aab05f0Sxsa 			cvs_client_send_request("Argument -l");
2404aab05f0Sxsa 	} else {
2414aab05f0Sxsa 		cr.fileproc = cvs_unedit_local;
2424aab05f0Sxsa 	}
2434aab05f0Sxsa 
2444aab05f0Sxsa 	cr.flags = flags;
2454aab05f0Sxsa 
2464aab05f0Sxsa 	cvs_file_run(argc, argv, &cr);
2474aab05f0Sxsa 
248*4dcde513Sjoris 	if (cvsroot_is_remote()) {
2494aab05f0Sxsa 		cvs_client_send_files(argv, argc);
2504aab05f0Sxsa 		cvs_client_senddir(".");
2514aab05f0Sxsa 		cvs_client_send_request("unedit");
2524aab05f0Sxsa 		cvs_client_get_responses();
2534aab05f0Sxsa 	}
2544aab05f0Sxsa 
2554aab05f0Sxsa 	return (0);
2564aab05f0Sxsa }
2574aab05f0Sxsa 
2585a31a4e8Sxsa static void
cvs_edit_local(struct cvs_file * cf)25933c4ed80Sxsa cvs_edit_local(struct cvs_file *cf)
26033c4ed80Sxsa {
26133c4ed80Sxsa 	FILE *fp;
2626534056aStobias 	struct tm t;
26333c4ed80Sxsa 	time_t now;
264b9fc9a72Sderaadt 	char timebuf[CVS_TIME_BUFSZ], thishost[HOST_NAME_MAX+1];
265b9fc9a72Sderaadt 	char bfpath[PATH_MAX], wdir[PATH_MAX];
26633c4ed80Sxsa 
26733c4ed80Sxsa 	if (cvs_noexec == 1)
26833c4ed80Sxsa 		return;
26933c4ed80Sxsa 
270b7b86dcdSxsa 	cvs_log(LP_TRACE, "cvs_edit_local(%s)", cf->file_path);
271b7b86dcdSxsa 
27251ef6581Sjoris 	cvs_file_classify(cf, cvs_directory_tag);
273de08069bSxsa 
27433c4ed80Sxsa 	if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL)
27533c4ed80Sxsa 		fatal("cvs_edit_local: fopen: `%s': %s",
27633c4ed80Sxsa 		    CVS_PATH_NOTIFY, strerror(errno));
27733c4ed80Sxsa 
27833c4ed80Sxsa 	(void)time(&now);
2796534056aStobias 	gmtime_r(&now, &t);
2806534056aStobias 	asctime_r(&t, timebuf);
2812820b891Stobias 	timebuf[strcspn(timebuf, "\n")] = '\0';
28233c4ed80Sxsa 
28302b07ef5Sxsa 	if (gethostname(thishost, sizeof(thishost)) == -1)
28402b07ef5Sxsa 		fatal("gethostname failed");
28502b07ef5Sxsa 
286b6798006Sxsa 	if (getcwd(wdir, sizeof(wdir)) == NULL)
287b6798006Sxsa 		fatal("getcwd failed");
288b6798006Sxsa 
28957e637ffSxsa 	(void)fprintf(fp, "E%s\t%s GMT\t%s\t%s\t",
290b6798006Sxsa 	    cf->file_name, timebuf, thishost, wdir);
29133c4ed80Sxsa 
29233c4ed80Sxsa 	if (edit_aflags & E_EDIT)
29333c4ed80Sxsa 		(void)fprintf(fp, "E");
29433c4ed80Sxsa 	if (edit_aflags & E_UNEDIT)
29533c4ed80Sxsa 		(void)fprintf(fp, "U");
29633c4ed80Sxsa 	if (edit_aflags & E_COMMIT)
29733c4ed80Sxsa 		(void)fprintf(fp, "C");
29833c4ed80Sxsa 
29933c4ed80Sxsa 	(void)fprintf(fp, "\n");
30033c4ed80Sxsa 
30133c4ed80Sxsa 	(void)fclose(fp);
30233c4ed80Sxsa 
30333c4ed80Sxsa 	if (fchmod(cf->fd, 0644) == -1)
30433c4ed80Sxsa 		fatal("cvs_edit_local: fchmod %s", strerror(errno));
30533c4ed80Sxsa 
306b9fc9a72Sderaadt 	(void)xsnprintf(bfpath, PATH_MAX, "%s/%s",
307e40de241Sxsa 	    CVS_PATH_BASEDIR, cf->file_name);
30833c4ed80Sxsa 
309158dfd40Sxsa 	if (mkdir(CVS_PATH_BASEDIR, 0755) == -1 && errno != EEXIST)
310158dfd40Sxsa 		fatal("cvs_edit_local: `%s': %s", CVS_PATH_BASEDIR,
311158dfd40Sxsa 		    strerror(errno));
312158dfd40Sxsa 
3130b10fb85Sxsa 	if (cvs_file_copy(cf->file_path, bfpath) == -1)
3140b10fb85Sxsa 		fatal("cvs_edit_local: cvs_file_copy failed");
31533c4ed80Sxsa 
316de08069bSxsa 	(void)cvs_base_handle(cf, BASE_ADD);
31733c4ed80Sxsa }
31833c4ed80Sxsa 
31933c4ed80Sxsa static void
cvs_editors_local(struct cvs_file * cf)3205a31a4e8Sxsa cvs_editors_local(struct cvs_file *cf)
3215a31a4e8Sxsa {
3225a31a4e8Sxsa }
3234aab05f0Sxsa 
3244aab05f0Sxsa static void
cvs_unedit_local(struct cvs_file * cf)3254aab05f0Sxsa cvs_unedit_local(struct cvs_file *cf)
3264aab05f0Sxsa {
3274aab05f0Sxsa 	FILE *fp;
3284aab05f0Sxsa 	struct stat st;
3296534056aStobias 	struct tm t;
3304aab05f0Sxsa 	time_t now;
331b9fc9a72Sderaadt 	char bfpath[PATH_MAX], timebuf[64], thishost[HOST_NAME_MAX+1];
332b9fc9a72Sderaadt 	char wdir[PATH_MAX], sticky[CVS_ENT_MAXLINELEN];
333b7b86dcdSxsa 	RCSNUM *ba_rev;
3344aab05f0Sxsa 
33578cab6d5Szinovik 	cvs_log(LP_TRACE, "cvs_unedit_local(%s)", cf->file_path);
33678cab6d5Szinovik 
3374aab05f0Sxsa 	if (cvs_noexec == 1)
3384aab05f0Sxsa 		return;
3394aab05f0Sxsa 
3405a1806edSjoris 	cvs_file_classify(cf, cvs_directory_tag);
341de08069bSxsa 
342b9fc9a72Sderaadt 	(void)xsnprintf(bfpath, PATH_MAX, "%s/%s",
343e40de241Sxsa 	    CVS_PATH_BASEDIR, cf->file_name);
3444aab05f0Sxsa 
345ba7b4b60Sotto 	if (stat(bfpath, &st) == -1)
3464aab05f0Sxsa 		return;
3474aab05f0Sxsa 
34893c88394Sxsa 	if (cvs_file_cmp(cf->file_path, bfpath) != 0) {
34993c88394Sxsa 		cvs_printf("%s has been modified; revert changes? ",
35093c88394Sxsa 		    cf->file_name);
35193c88394Sxsa 
352ba7b4b60Sotto 		if (cvs_yesno() == -1)
35393c88394Sxsa 			return;
35493c88394Sxsa 	}
3554aab05f0Sxsa 
3564aab05f0Sxsa 	cvs_rename(bfpath, cf->file_path);
3574aab05f0Sxsa 
3584aab05f0Sxsa 	if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL)
3594aab05f0Sxsa 		fatal("cvs_unedit_local: fopen: `%s': %s",
3604aab05f0Sxsa 		    CVS_PATH_NOTIFY, strerror(errno));
3614aab05f0Sxsa 
3624aab05f0Sxsa 	(void)time(&now);
3636534056aStobias 	gmtime_r(&now, &t);
3646534056aStobias 	asctime_r(&t, timebuf);
3652820b891Stobias 	timebuf[strcspn(timebuf, "\n")] = '\0';
3664aab05f0Sxsa 
36702b07ef5Sxsa 	if (gethostname(thishost, sizeof(thishost)) == -1)
36802b07ef5Sxsa 		fatal("gethostname failed");
36902b07ef5Sxsa 
370b6798006Sxsa 	if (getcwd(wdir, sizeof(wdir)) == NULL)
371b6798006Sxsa 		fatal("getcwd failed");
372de08069bSxsa 
3734aab05f0Sxsa 	(void)fprintf(fp, "U%s\t%s GMT\t%s\t%s\t\n",
374b6798006Sxsa 	    cf->file_name, timebuf, thishost, wdir);
3754aab05f0Sxsa 
3764aab05f0Sxsa 	(void)fclose(fp);
3774aab05f0Sxsa 
378b7b86dcdSxsa 	if ((ba_rev = cvs_base_handle(cf, BASE_GET)) == NULL) {
379b7b86dcdSxsa 		cvs_log(LP_ERR, "%s not mentioned in %s",
380b7b86dcdSxsa 		    cf->file_name, CVS_PATH_BASEREV);
381b7b86dcdSxsa 		return;
382b7b86dcdSxsa 	}
383a0486c11Sxsa 
384a0486c11Sxsa 	if (cf->file_ent != NULL) {
385a0486c11Sxsa 		CVSENTRIES *entlist;
386a0486c11Sxsa 		struct cvs_ent *ent;
3870a7da307Sxsa 		char *entry, rbuf[CVS_REV_BUFSZ];
388a0486c11Sxsa 
389a0486c11Sxsa 		entlist = cvs_ent_open(cf->file_wd);
390a0486c11Sxsa 
391a0486c11Sxsa 		if ((ent = cvs_ent_get(entlist, cf->file_name)) == NULL)
3928a8a62cbSxsa 			fatal("cvs_unedit_local: cvs_ent_get failed");
393a0486c11Sxsa 
394a0486c11Sxsa 		(void)rcsnum_tostr(ba_rev, rbuf, sizeof(rbuf));
395a0486c11Sxsa 
396a0486c11Sxsa 		memset(timebuf, 0, sizeof(timebuf));
397a0486c11Sxsa 		ctime_r(&cf->file_ent->ce_mtime, timebuf);
3982820b891Stobias 		timebuf[strcspn(timebuf, "\n")] = '\0';
399a0486c11Sxsa 
400b0b4b42cStobias 		sticky[0] = '\0';
401b0b4b42cStobias 		if (cf->file_ent->ce_tag != NULL)
402b0b4b42cStobias 			(void)xsnprintf(sticky, sizeof(sticky), "T%s",
403b0b4b42cStobias 			    cf->file_ent->ce_tag);
404b0b4b42cStobias 
405a0486c11Sxsa 		(void)xasprintf(&entry, "/%s/%s/%s/%s/%s",
4067eeb4908Sragge 		    cf->file_name, rbuf, timebuf, cf->file_ent->ce_opts ?
4077eeb4908Sragge 		    cf->file_ent->ce_opts : "", sticky);
408a0486c11Sxsa 
409a0486c11Sxsa 		cvs_ent_add(entlist, entry);
410a0486c11Sxsa 
411a0486c11Sxsa 		cvs_ent_free(ent);
412a0486c11Sxsa 
413397ddb8aSnicm 		free(entry);
414b7b86dcdSxsa 	}
415b7b86dcdSxsa 
41653ce2177Sfcambus 	free(ba_rev);
417a0486c11Sxsa 
418b7b86dcdSxsa 	(void)cvs_base_handle(cf, BASE_REMOVE);
4192b484273Sxsa 
4202b484273Sxsa 	if (fchmod(cf->fd, 0644) == -1)
4212b484273Sxsa 		fatal("cvs_unedit_local: fchmod %s", strerror(errno));
4224aab05f0Sxsa }
423de08069bSxsa 
424de08069bSxsa static RCSNUM *
cvs_base_handle(struct cvs_file * cf,int flags)425de08069bSxsa cvs_base_handle(struct cvs_file *cf, int flags)
426de08069bSxsa {
427de08069bSxsa 	FILE *fp, *tfp;
428de08069bSxsa 	RCSNUM *ba_rev;
429b7b86dcdSxsa 	int i;
430b7b86dcdSxsa 	char *dp, *sp;
431b9fc9a72Sderaadt 	char buf[PATH_MAX], *fields[2], rbuf[CVS_REV_BUFSZ];
432de08069bSxsa 
433de08069bSxsa 	cvs_log(LP_TRACE, "cvs_base_handle(%s)", cf->file_path);
434de08069bSxsa 
435de08069bSxsa 	tfp = NULL;
436de08069bSxsa 	ba_rev = NULL;
437de08069bSxsa 
438b7b86dcdSxsa 	if (((fp = fopen(CVS_PATH_BASEREV, "r")) == NULL) && errno != ENOENT) {
439de08069bSxsa 		cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREV);
440de08069bSxsa 		goto out;
441de08069bSxsa 	}
442de08069bSxsa 
443de08069bSxsa 	if (flags & (BASE_ADD|BASE_REMOVE)) {
444de08069bSxsa 		if ((tfp = fopen(CVS_PATH_BASEREVTMP, "w")) == NULL) {
445de08069bSxsa 			cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREVTMP);
446de08069bSxsa 			goto out;
447de08069bSxsa 		}
448de08069bSxsa 	}
449de08069bSxsa 
450b7b86dcdSxsa 	if (fp != NULL) {
451de08069bSxsa 		while (fgets(buf, sizeof(buf), fp)) {
452b625fa02Sgilles 			buf[strcspn(buf, "\n")] = '\0';
453de08069bSxsa 
454de08069bSxsa 			if (buf[0] != 'B')
455de08069bSxsa 				continue;
456de08069bSxsa 
457b7b86dcdSxsa 			sp = buf + 1;
458b7b86dcdSxsa 			i = 0;
459b7b86dcdSxsa 			do {
460b7b86dcdSxsa 				if ((dp = strchr(sp, '/')) != NULL)
461b7b86dcdSxsa 					*(dp++) = '\0';
462b7b86dcdSxsa 				fields[i++] = sp;
463b7b86dcdSxsa 				sp = dp;
464b7b86dcdSxsa 			} while (dp != NULL && i < 2);
465de08069bSxsa 
466b7b86dcdSxsa 			if (cvs_file_cmpname(fields[0], cf->file_path) == 0) {
467de08069bSxsa 				if (flags & BASE_GET) {
468b7b86dcdSxsa 					ba_rev = rcsnum_parse(fields[1]);
469b7b86dcdSxsa 					if (ba_rev == NULL)
470b7b86dcdSxsa 						fatal("cvs_base_handle: "
471b7b86dcdSxsa 						    "rcsnum_parse");
472de08069bSxsa 					goto got_rev;
473de08069bSxsa 				}
474de08069bSxsa 			} else {
475de08069bSxsa 				if (flags & (BASE_ADD|BASE_REMOVE))
476de08069bSxsa 					(void)fprintf(tfp, "%s\n", buf);
477de08069bSxsa 			}
478de08069bSxsa 		}
479b7b86dcdSxsa 	}
480de08069bSxsa 
481de08069bSxsa got_rev:
482de08069bSxsa 	if (flags & (BASE_ADD)) {
483de08069bSxsa 		(void)rcsnum_tostr(cf->file_ent->ce_rev, rbuf, sizeof(rbuf));
484de08069bSxsa 		(void)fprintf(tfp, "B%s/%s/\n", cf->file_path, rbuf);
485de08069bSxsa 	}
486de08069bSxsa 
487de08069bSxsa out:
488de08069bSxsa 	if (fp != NULL)
489de08069bSxsa 		(void)fclose(fp);
490de08069bSxsa 
491de08069bSxsa 	if (tfp != NULL) {
492de08069bSxsa 		(void)fclose(tfp);
493de08069bSxsa 		(void)cvs_rename(CVS_PATH_BASEREVTMP, CVS_PATH_BASEREV);
494de08069bSxsa 	}
495de08069bSxsa 
496de08069bSxsa 	return (ba_rev);
497de08069bSxsa }
498