xref: /openbsd-src/usr.bin/rcs/rcsprog.c (revision 6541b77c94127ecd11c0a5f7b2cdfe2db3a4a356)
1*6541b77cSguenther /*	$OpenBSD: rcsprog.c,v 1.165 2023/08/11 05:02:21 guenther Exp $	*/
27235aeccSderaadt /*
37235aeccSderaadt  * Copyright (c) 2005 Jean-Francois Brousseau <jfb@openbsd.org>
47235aeccSderaadt  * All rights reserved.
57235aeccSderaadt  *
67235aeccSderaadt  * Redistribution and use in source and binary forms, with or without
77235aeccSderaadt  * modification, are permitted provided that the following conditions
87235aeccSderaadt  * are met:
97235aeccSderaadt  *
107235aeccSderaadt  * 1. Redistributions of source code must retain the above copyright
117235aeccSderaadt  *    notice, this list of conditions and the following disclaimer.
127235aeccSderaadt  * 2. The name of the author may not be used to endorse or promote products
137235aeccSderaadt  *    derived from this software without specific prior written permission.
147235aeccSderaadt  *
157235aeccSderaadt  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
167235aeccSderaadt  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
177235aeccSderaadt  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
187235aeccSderaadt  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
197235aeccSderaadt  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
207235aeccSderaadt  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
217235aeccSderaadt  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
227235aeccSderaadt  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
237235aeccSderaadt  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
247235aeccSderaadt  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
257235aeccSderaadt  */
267235aeccSderaadt 
274781e2faSxsa #include <sys/stat.h>
284781e2faSxsa 
294781e2faSxsa #include <err.h>
303442ef86Schl #include <signal.h>
314781e2faSxsa #include <stdio.h>
324781e2faSxsa #include <stdlib.h>
334781e2faSxsa #include <string.h>
344781e2faSxsa #include <unistd.h>
357235aeccSderaadt 
3600f786efSjoris #include "rcsprog.h"
377235aeccSderaadt 
3800f4e67cSmillert #define RCSPROG_OPTSTRING	"A:a:b::c:e::Iik:Ll::m:Mn:N:o:qt::TUu::Vx::z::"
395983b570Sjoris 
4081ba9f81Sjoris const char rcs_version[] = "OpenRCS 4.5";
417235aeccSderaadt 
42713e08f8Sray int	 rcsflags;
43a2b34663Sjoris int	 rcs_optind;
44a2b34663Sjoris char	*rcs_optarg;
451d44d920Stobias char	*rcs_suffixes = RCS_DEFAULT_SUFFIX;
461dc71a1bSxsa char	*rcs_tmpdir = RCS_TMPDIR_DEFAULT;
47a2b34663Sjoris 
487235aeccSderaadt struct rcs_prog {
497235aeccSderaadt 	char	*prog_name;
507235aeccSderaadt 	int	(*prog_hdlr)(int, char **);
517b640abcSderaadt 	void	(*prog_usage)(void);
527235aeccSderaadt } programs[] = {
537b640abcSderaadt 	{ "rcs",	rcs_main,	rcs_usage	},
5450deab1aSjoris 	{ "ci",		checkin_main,	checkin_usage   },
55a8c7a9c7Sjoris 	{ "co",		checkout_main,	checkout_usage  },
5622447615Sjoris 	{ "rcsclean",	rcsclean_main,	rcsclean_usage	},
5797449b47Sjoris 	{ "rcsdiff",	rcsdiff_main,	rcsdiff_usage	},
5893055963Sxsa 	{ "rcsmerge",	rcsmerge_main,	rcsmerge_usage	},
5967970ff5Sjoris 	{ "rlog",	rlog_main,	rlog_usage	},
60c6e1ac21Sjoris 	{ "ident",	ident_main,	ident_usage	},
611bed6b5dSxsa 	{ "merge",	merge_main,	merge_usage	},
627235aeccSderaadt };
632c2b8515Smortimer void   (*usage)(void);
647235aeccSderaadt 
657a9e6d11Sray struct wklhead temp_files;
66f2af11b8Sjoris 
67f2af11b8Sjoris void sighdlr(int);
685983b570Sjoris static void  rcs_attach_symbol(RCSFILE *, const char *);
695983b570Sjoris 
7025f9d6ccSjoris void
sighdlr(int sig)71f2af11b8Sjoris sighdlr(int sig)
72f2af11b8Sjoris {
737a9e6d11Sray 	worklist_clean(&temp_files, worklist_unlink);
74f2af11b8Sjoris 	_exit(1);
75f2af11b8Sjoris }
76f2af11b8Sjoris 
7700f786efSjoris int
build_cmd(char *** cmd_argv,char ** argv,int argc)78a742a464Stobias build_cmd(char ***cmd_argv, char **argv, int argc)
7952686e4aSjoris {
80a742a464Stobias 	int cmd_argc, i, cur;
81a742a464Stobias 	char *cp, *rcsinit, *linebuf, *lp;
8252686e4aSjoris 
83a742a464Stobias 	if ((rcsinit = getenv("RCSINIT")) == NULL) {
84a742a464Stobias 		*cmd_argv = argv;
85a742a464Stobias 		return argc;
86a742a464Stobias 	}
8752686e4aSjoris 
88a742a464Stobias 	cur = argc + 2;
89a742a464Stobias 	cmd_argc = 0;
90a742a464Stobias 	*cmd_argv = xcalloc(cur, sizeof(char *));
91a742a464Stobias 	(*cmd_argv)[cmd_argc++] = argv[0];
92a742a464Stobias 
93a742a464Stobias 	linebuf = xstrdup(rcsinit);
9452686e4aSjoris 	for (lp = linebuf; lp != NULL;) {
95326daabbSxsa 		cp = strsep(&lp, " \t\b\f\n\r\t\v");
9652686e4aSjoris 		if (cp == NULL)
9752686e4aSjoris 			break;
98a742a464Stobias 		if (*cp == '\0')
9952686e4aSjoris 			continue;
10052686e4aSjoris 
101a742a464Stobias 		if (cmd_argc == cur) {
102a742a464Stobias 			cur += 8;
103caa2ffb0Sderaadt 			*cmd_argv = xreallocarray(*cmd_argv, cur,
104a742a464Stobias 			    sizeof(char *));
10552686e4aSjoris 		}
10652686e4aSjoris 
107a742a464Stobias 		(*cmd_argv)[cmd_argc++] = cp;
10852686e4aSjoris 	}
10952686e4aSjoris 
110a742a464Stobias 	if (cmd_argc + argc > cur) {
111a742a464Stobias 		cur = cmd_argc + argc + 1;
112caa2ffb0Sderaadt 		*cmd_argv = xreallocarray(*cmd_argv, cur,
113a742a464Stobias 		    sizeof(char *));
11452686e4aSjoris 	}
11552686e4aSjoris 
116a742a464Stobias 	for (i = 1; i < argc; i++)
117a742a464Stobias 		(*cmd_argv)[cmd_argc++] = argv[i];
118a742a464Stobias 
119a742a464Stobias 	(*cmd_argv)[cmd_argc] = NULL;
120a742a464Stobias 
121a742a464Stobias 	return cmd_argc;
12252686e4aSjoris }
12352686e4aSjoris 
12452686e4aSjoris int
main(int argc,char ** argv)1257235aeccSderaadt main(int argc, char **argv)
1267235aeccSderaadt {
1277235aeccSderaadt 	u_int i;
128a742a464Stobias 	char **cmd_argv;
12952686e4aSjoris 	int ret, cmd_argc;
1307235aeccSderaadt 
131cbbcd319Smillert 	if (pledge("stdio rpath wpath cpath fattr flock getpw", NULL) == -1)
1326e3a368bSgsoares 		err(2, "pledge");
133b808fb87Sderaadt 
1347235aeccSderaadt 	ret = -1;
135a2b34663Sjoris 	rcs_optind = 1;
1367a9e6d11Sray 	SLIST_INIT(&temp_files);
1377235aeccSderaadt 
138a742a464Stobias 	cmd_argc = build_cmd(&cmd_argv, argv, argc);
13952686e4aSjoris 
1401dc71a1bSxsa 	if ((rcs_tmpdir = getenv("TMPDIR")) == NULL)
1411dc71a1bSxsa 		rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1421dc71a1bSxsa 
143f2af11b8Sjoris 	signal(SIGHUP, sighdlr);
144f2af11b8Sjoris 	signal(SIGINT, sighdlr);
145f2af11b8Sjoris 	signal(SIGQUIT, sighdlr);
146f2af11b8Sjoris 	signal(SIGABRT, sighdlr);
147f2af11b8Sjoris 	signal(SIGALRM, sighdlr);
148f2af11b8Sjoris 	signal(SIGTERM, sighdlr);
149f2af11b8Sjoris 
1507235aeccSderaadt 	for (i = 0; i < (sizeof(programs)/sizeof(programs[0])); i++)
1517b640abcSderaadt 		if (strcmp(__progname, programs[i].prog_name) == 0) {
1527b640abcSderaadt 			usage = programs[i].prog_usage;
15352686e4aSjoris 			ret = programs[i].prog_hdlr(cmd_argc, cmd_argv);
1547b640abcSderaadt 			break;
1557b640abcSderaadt 		}
1567235aeccSderaadt 
15716134806Sjoris 	/* clean up temporary files */
1587a9e6d11Sray 	worklist_run(&temp_files, worklist_unlink);
15916134806Sjoris 
160b3677838Sniallo 	exit(ret);
1617235aeccSderaadt }
1627235aeccSderaadt 
1637235aeccSderaadt 
164960e00beSotto __dead void
rcs_usage(void)1657235aeccSderaadt rcs_usage(void)
1667235aeccSderaadt {
1677235aeccSderaadt 	fprintf(stderr,
168fe2e2d22Sniallo 	    "usage: rcs [-IiLqTUV] [-Aoldfile] [-ausers] [-b[rev]]\n"
169d660bd8fSray 	    "           [-cstring] [-e[users]] [-kmode] [-l[rev]] [-mrev:msg]\n"
17030663f63Ssobrado 	    "           [-orev] [-t[str]] [-u[rev]] [-xsuffixes] file ...\n");
171960e00beSotto 
172960e00beSotto 	exit(1);
1737235aeccSderaadt }
1747235aeccSderaadt 
1757235aeccSderaadt /*
1767235aeccSderaadt  * rcs_main()
1777235aeccSderaadt  *
1787235aeccSderaadt  * Handler for the `rcs' program.
1797235aeccSderaadt  * Returns 0 on success, or >0 on error.
1807235aeccSderaadt  */
1817235aeccSderaadt int
rcs_main(int argc,char ** argv)1827235aeccSderaadt rcs_main(int argc, char **argv)
1837235aeccSderaadt {
1849f3d16a8Sray 	int fd;
1859349b09eSxsa 	int i, j, ch, flags, kflag, lkmode;
1866e2f6b91Sray 	const char *nflag, *oldfilename, *orange;
187b9fc9a72Sderaadt 	char fpath[PATH_MAX];
1886e2f6b91Sray 	char *logstr, *logmsg, *descfile;
1896e2f6b91Sray 	char *alist, *comment, *elist, *lrev, *urev;
1907235aeccSderaadt 	mode_t fmode;
1919f3d16a8Sray 	RCSFILE *file;
19220bbe70aSjoris 	RCSNUM *logrev;
193c2d70614Sxsa 	struct rcs_access *acp;
194*6541b77cSguenther 	struct timespec rcs_mtime = { .tv_sec = 0, .tv_nsec = UTIME_OMIT };
1957235aeccSderaadt 
19686ca4362Sxsa 	kflag = RCS_KWEXP_ERR;
19712f4f457Sxsa 	lkmode = RCS_LOCK_INVAL;
198c51cb395Sniallo 	fmode =  S_IRUSR|S_IRGRP|S_IROTH;
199b71c7dfeSniallo 	flags = RCS_RDWR|RCS_PARSE_FULLY;
2006e2f6b91Sray 	lrev = urev = descfile = NULL;
2016e2f6b91Sray 	logstr = alist = comment = elist = NULL;
2026e2f6b91Sray 	nflag = oldfilename = orange = NULL;
2037235aeccSderaadt 
2047aea8f3dSxsa 	/* match GNU */
2057aea8f3dSxsa 	if (1 < argc && argv[1][0] != '-')
2067aea8f3dSxsa 		warnx("warning: No options were given; "
2077aea8f3dSxsa 		    "this usage is obsolescent.");
2087aea8f3dSxsa 
209271479caSxsa 	while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) {
2107235aeccSderaadt 		switch (ch) {
2117235aeccSderaadt 		case 'A':
2129f3d16a8Sray 			oldfilename = rcs_optarg;
2135983b570Sjoris 			rcsflags |= CO_ACLAPPEND;
2147235aeccSderaadt 			break;
2157235aeccSderaadt 		case 'a':
216a2b34663Sjoris 			alist = rcs_optarg;
2177235aeccSderaadt 			break;
2187235aeccSderaadt 		case 'c':
219a2b34663Sjoris 			comment = rcs_optarg;
2207235aeccSderaadt 			break;
2217235aeccSderaadt 		case 'e':
222a2b34663Sjoris 			elist = rcs_optarg;
223ecfbf341Sray 			rcsflags |= RCSPROG_EFLAG;
2247235aeccSderaadt 			break;
22500f4e67cSmillert 		case 'I':
22600f4e67cSmillert 			rcsflags |= INTERACTIVE;
22700f4e67cSmillert 			break;
2287235aeccSderaadt 		case 'i':
2297235aeccSderaadt 			flags |= RCS_CREATE;
2307235aeccSderaadt 			break;
2317235aeccSderaadt 		case 'k':
232a2b34663Sjoris 			kflag = rcs_kflag_get(rcs_optarg);
2337235aeccSderaadt 			if (RCS_KWEXP_INVAL(kflag)) {
23425a1df5bSxsa 				warnx("invalid RCS keyword substitution mode");
23525a1df5bSxsa 				(usage)();
2367235aeccSderaadt 			}
2377235aeccSderaadt 			break;
2387235aeccSderaadt 		case 'L':
2397235aeccSderaadt 			if (lkmode == RCS_LOCK_LOOSE)
2407c635cc4Stobias 				warnx("-U overridden by -L");
2417235aeccSderaadt 			lkmode = RCS_LOCK_STRICT;
2427235aeccSderaadt 			break;
243d660bd8fSray 		case 'l':
24487e61ef9Sotto 			if (rcsflags & RCSPROG_UFLAG)
24587e61ef9Sotto 				warnx("-u overridden by -l");
246d660bd8fSray 			lrev = rcs_optarg;
24787e61ef9Sotto 			rcsflags &= ~RCSPROG_UFLAG;
248d660bd8fSray 			rcsflags |= RCSPROG_LFLAG;
249d660bd8fSray 			break;
25020bbe70aSjoris 		case 'm':
2518ac837e5Snicm 			free(logstr);
2520450b43bSjoris 			logstr = xstrdup(rcs_optarg);
25320bbe70aSjoris 			break;
2547235aeccSderaadt 		case 'M':
2557235aeccSderaadt 			/* ignore for the moment */
2567235aeccSderaadt 			break;
2575983b570Sjoris 		case 'n':
2586e2f6b91Sray 			nflag = rcs_optarg;
2595983b570Sjoris 			break;
2605983b570Sjoris 		case 'N':
2616e2f6b91Sray 			nflag = rcs_optarg;
262ecfbf341Sray 			rcsflags |= RCSPROG_NFLAG;
2635983b570Sjoris 			break;
264ea1ee718Sjoris 		case 'o':
2656e2f6b91Sray 			orange = rcs_optarg;
266ea1ee718Sjoris 			break;
26721d0e704Sjoris 		case 'q':
268913586deSxsa 			rcsflags |= QUIET;
26921d0e704Sjoris 			break;
2705983b570Sjoris 		case 't':
2715983b570Sjoris 			descfile = rcs_optarg;
272d491b5edSray 			rcsflags |= DESCRIPTION;
2735983b570Sjoris 			break;
2744ecf1ebbSxsa 		case 'T':
2755983b570Sjoris 			rcsflags |= PRESERVETIME;
2764ecf1ebbSxsa 			break;
2777235aeccSderaadt 		case 'U':
2787235aeccSderaadt 			if (lkmode == RCS_LOCK_STRICT)
2797c635cc4Stobias 				warnx("-L overridden by -U");
2807235aeccSderaadt 			lkmode = RCS_LOCK_LOOSE;
2817235aeccSderaadt 			break;
282d660bd8fSray 		case 'u':
28387e61ef9Sotto 			if (rcsflags & RCSPROG_LFLAG)
28487e61ef9Sotto 				warnx("-l overridden by -u");
285d660bd8fSray 			urev = rcs_optarg;
28687e61ef9Sotto 			rcsflags &= ~RCSPROG_LFLAG;
287d660bd8fSray 			rcsflags |= RCSPROG_UFLAG;
288d660bd8fSray 			break;
2897235aeccSderaadt 		case 'V':
2907235aeccSderaadt 			printf("%s\n", rcs_version);
2917235aeccSderaadt 			exit(0);
2927518c1e9Sxsa 		case 'x':
2934365263eSray 			/* Use blank extension if none given. */
2944365263eSray 			rcs_suffixes = rcs_optarg ? rcs_optarg : "";
2957518c1e9Sxsa 			break;
296271479caSxsa 		case 'z':
297271479caSxsa 			/*
298271479caSxsa 			 * kept for compatibility
299271479caSxsa 			 */
300271479caSxsa 			break;
3017235aeccSderaadt 		default:
3027b640abcSderaadt 			(usage)();
3037235aeccSderaadt 		}
3047235aeccSderaadt 	}
3057235aeccSderaadt 
306a2b34663Sjoris 	argc -= rcs_optind;
307a2b34663Sjoris 	argv += rcs_optind;
3080dce961eSjoris 
3097235aeccSderaadt 	if (argc == 0) {
31082ca7eaaSxsa 		warnx("no input file");
31160b18b15Sjoris 		(usage)();
3127235aeccSderaadt 	}
3137235aeccSderaadt 
3147235aeccSderaadt 	for (i = 0; i < argc; i++) {
315f3876e23Sray 		fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
316f3876e23Sray 		if (fd < 0 && !(flags & RCS_CREATE)) {
31799e66255Sniallo 			warn("%s", fpath);
3181013ab0fSjoris 			continue;
319f3876e23Sray 		}
3209ca97391Sjoris 
321913586deSxsa 		if (!(rcsflags & QUIET))
3224a1cfd68Sxsa 			(void)fprintf(stderr, "RCS file: %s\n", fpath);
32366d00473Sxsa 
3244b1a1f86Sray 		if ((file = rcs_open(fpath, fd, flags, fmode)) == NULL) {
3254b1a1f86Sray 			close(fd);
3269ca97391Sjoris 			continue;
3274b1a1f86Sray 		}
3287235aeccSderaadt 
3294b1a1f86Sray 		if (rcsflags & DESCRIPTION) {
33000f4e67cSmillert 			if (rcs_set_description(file, descfile, rcsflags) == -1) {
3314b1a1f86Sray 				warn("%s", descfile);
3324b1a1f86Sray 				rcs_close(file);
3334b1a1f86Sray 				continue;
3344b1a1f86Sray 			}
3354b1a1f86Sray 		}
3364b1a1f86Sray 		else if (flags & RCS_CREATE) {
33700f4e67cSmillert 			if (rcs_set_description(file, NULL, rcsflags) == -1) {
3384b1a1f86Sray 				warn("stdin");
3394b1a1f86Sray 				rcs_close(file);
3404b1a1f86Sray 				continue;
3414b1a1f86Sray 			}
3424b1a1f86Sray 		}
3435983b570Sjoris 
3445983b570Sjoris 		if (rcsflags & PRESERVETIME)
345bc8d37c3Sjoris 			rcs_mtime = rcs_get_mtime(file);
34666d00473Sxsa 
3475983b570Sjoris 		if (nflag != NULL)
3485983b570Sjoris 			rcs_attach_symbol(file, nflag);
3495983b570Sjoris 
35020bbe70aSjoris 		if (logstr != NULL) {
35120bbe70aSjoris 			if ((logmsg = strchr(logstr, ':')) == NULL) {
35282ca7eaaSxsa 				warnx("missing log message");
35320bbe70aSjoris 				rcs_close(file);
35420bbe70aSjoris 				continue;
35520bbe70aSjoris 			}
35620bbe70aSjoris 
35720bbe70aSjoris 			*logmsg++ = '\0';
35820bbe70aSjoris 			if ((logrev = rcsnum_parse(logstr)) == NULL) {
359671d72e5Sxsa 				warnx("`%s' bad revision number", logstr);
36020bbe70aSjoris 				rcs_close(file);
36120bbe70aSjoris 				continue;
36220bbe70aSjoris 			}
36320bbe70aSjoris 
36420bbe70aSjoris 			if (rcs_rev_setlog(file, logrev, logmsg) < 0) {
365671d72e5Sxsa 				warnx("failed to set logmsg for `%s' to `%s'",
36620bbe70aSjoris 				    logstr, logmsg);
36720bbe70aSjoris 				rcs_close(file);
368e3d0b03dSjoris 				rcsnum_free(logrev);
36920bbe70aSjoris 				continue;
37020bbe70aSjoris 			}
37120bbe70aSjoris 
37220bbe70aSjoris 			rcsnum_free(logrev);
37320bbe70aSjoris 		}
37420bbe70aSjoris 
375c2d70614Sxsa 		/* entries to add from <oldfile> */
3765983b570Sjoris 		if (rcsflags & CO_ACLAPPEND) {
3779f3d16a8Sray 			RCSFILE *oldfile;
3789f3d16a8Sray 			int ofd;
379b9fc9a72Sderaadt 			char ofpath[PATH_MAX];
3809f3d16a8Sray 
3819f3d16a8Sray 			ofd = rcs_choosefile(oldfilename, ofpath, sizeof(ofpath));
3829f3d16a8Sray 			if (ofd < 0) {
3839f3d16a8Sray 				if (!(flags & RCS_CREATE))
38499e66255Sniallo 					warn("%s", ofpath);
3859f3d16a8Sray 				exit(1);
3869f3d16a8Sray 			}
387bc8d37c3Sjoris 			if ((oldfile = rcs_open(ofpath, ofd, RCS_READ)) == NULL)
388c2d70614Sxsa 				exit(1);
389c2d70614Sxsa 
390c2d70614Sxsa 			TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list)
391c2d70614Sxsa 				rcs_access_add(file, acp->ra_name);
392c2d70614Sxsa 
393c2d70614Sxsa 			rcs_close(oldfile);
3949f3d16a8Sray 			(void)close(ofd);
395c2d70614Sxsa 		}
396c2d70614Sxsa 
3977235aeccSderaadt 		/* entries to add to the access list */
3987235aeccSderaadt 		if (alist != NULL) {
3992dc36bedSjoris 			struct rcs_argvector *aargv;
4007235aeccSderaadt 
4012dc36bedSjoris 			aargv = rcs_strsplit(alist, ",");
402948d9c85Spat 			for (j = 0; aargv->argv[j] != NULL; j++)
403948d9c85Spat 				rcs_access_add(file, aargv->argv[j]);
4047235aeccSderaadt 
4052dc36bedSjoris 			rcs_argv_destroy(aargv);
4067235aeccSderaadt 		}
4077235aeccSderaadt 
4087235aeccSderaadt 		if (comment != NULL)
4097235aeccSderaadt 			rcs_comment_set(file, comment);
4107235aeccSderaadt 
41197fee963Sxsa 		if (elist != NULL) {
4122dc36bedSjoris 			struct rcs_argvector *eargv;
41397fee963Sxsa 
4142dc36bedSjoris 			eargv = rcs_strsplit(elist, ",");
415948d9c85Spat 			for (j = 0; eargv->argv[j] != NULL; j++)
416948d9c85Spat 				rcs_access_remove(file, eargv->argv[j]);
41797fee963Sxsa 
4182dc36bedSjoris 			rcs_argv_destroy(eargv);
419ecfbf341Sray 		} else if (rcsflags & RCSPROG_EFLAG) {
42097fee963Sxsa 			struct rcs_access *rap;
42197fee963Sxsa 
42297fee963Sxsa 			/* XXX rcs_access_remove(file, NULL); ?? */
42397fee963Sxsa 			while (!TAILQ_EMPTY(&(file->rf_access))) {
42497fee963Sxsa 				rap = TAILQ_FIRST(&(file->rf_access));
42597fee963Sxsa 				TAILQ_REMOVE(&(file->rf_access), rap, ra_list);
4268ac837e5Snicm 				free(rap->ra_name);
4278ac837e5Snicm 				free(rap);
42897fee963Sxsa 			}
42997fee963Sxsa 			/* not synced anymore */
43097fee963Sxsa 			file->rf_flags &= ~RCS_SYNCED;
43197fee963Sxsa 		}
43297fee963Sxsa 
4337235aeccSderaadt 		rcs_kwexp_set(file, kflag);
4347235aeccSderaadt 
43512f4f457Sxsa 		if (lkmode != RCS_LOCK_INVAL)
436291869e5Sxsa 			(void)rcs_lock_setmode(file, lkmode);
4377235aeccSderaadt 
438d660bd8fSray 		if (rcsflags & RCSPROG_LFLAG) {
439d660bd8fSray 			RCSNUM *rev;
440d660bd8fSray 			const char *username;
441950e09dcSxsa 			char rev_str[RCS_REV_BUFSZ];
442d660bd8fSray 
4434bd9a95aSjoris 			if (file->rf_head == NULL) {
4444bd9a95aSjoris 				warnx("%s contains no revisions", fpath);
4454bd9a95aSjoris 				rcs_close(file);
4464bd9a95aSjoris 				continue;
4474bd9a95aSjoris 			}
4484bd9a95aSjoris 
449d660bd8fSray 			if ((username = getlogin()) == NULL)
450a3660ae3Sxsa 				err(1, "getlogin");
451d660bd8fSray 			if (lrev == NULL) {
452d660bd8fSray 				rev = rcsnum_alloc();
453d660bd8fSray 				rcsnum_cpy(file->rf_head, rev, 0);
454d660bd8fSray 			} else if ((rev = rcsnum_parse(lrev)) == NULL) {
45582ca7eaaSxsa 				warnx("unable to unlock file");
456d660bd8fSray 				rcs_close(file);
457d660bd8fSray 				continue;
458d660bd8fSray 			}
4590e8341b7Sray 			rcsnum_tostr(rev, rev_str, sizeof(rev_str));
460d660bd8fSray 			/* Make sure revision exists. */
461d660bd8fSray 			if (rcs_findrev(file, rev) == NULL)
462a3660ae3Sxsa 				errx(1, "%s: cannot lock nonexisting "
463a3660ae3Sxsa 				    "revision %s", fpath, rev_str);
4640e8341b7Sray 			if (rcs_lock_add(file, username, rev) != -1 &&
465913586deSxsa 			    !(rcsflags & QUIET))
46647cc040dSxsa 				(void)fprintf(stderr, "%s locked\n", rev_str);
467d660bd8fSray 			rcsnum_free(rev);
468d660bd8fSray 		}
469d660bd8fSray 
470d660bd8fSray 		if (rcsflags & RCSPROG_UFLAG) {
471d660bd8fSray 			RCSNUM *rev;
472d660bd8fSray 			const char *username;
473950e09dcSxsa 			char rev_str[RCS_REV_BUFSZ];
474d660bd8fSray 
4754bd9a95aSjoris 			if (file->rf_head == NULL) {
4764bd9a95aSjoris 				warnx("%s contains no revisions", fpath);
4774bd9a95aSjoris 				rcs_close(file);
4784bd9a95aSjoris 				continue;
4794bd9a95aSjoris 			}
4804bd9a95aSjoris 
481d660bd8fSray 			if ((username = getlogin()) == NULL)
482a3660ae3Sxsa 				err(1, "getlogin");
483d660bd8fSray 			if (urev == NULL) {
484d660bd8fSray 				rev = rcsnum_alloc();
485d660bd8fSray 				rcsnum_cpy(file->rf_head, rev, 0);
486d660bd8fSray 			} else if ((rev = rcsnum_parse(urev)) == NULL) {
48782ca7eaaSxsa 				warnx("unable to unlock file");
488d660bd8fSray 				rcs_close(file);
489d660bd8fSray 				continue;
490d660bd8fSray 			}
4910e8341b7Sray 			rcsnum_tostr(rev, rev_str, sizeof(rev_str));
492d660bd8fSray 			/* Make sure revision exists. */
493d660bd8fSray 			if (rcs_findrev(file, rev) == NULL)
494a3660ae3Sxsa 				errx(1, "%s: cannot unlock nonexisting "
495a3660ae3Sxsa 				    "revision %s", fpath, rev_str);
4960e8341b7Sray 			if (rcs_lock_remove(file, username, rev) == -1 &&
497913586deSxsa 			    !(rcsflags & QUIET))
49882ca7eaaSxsa 				warnx("%s: warning: No locks are set.", fpath);
49947cc040dSxsa 			else {
50047cc040dSxsa 				if (!(rcsflags & QUIET))
50147cc040dSxsa 					(void)fprintf(stderr,
50247cc040dSxsa 					    "%s unlocked\n", rev_str);
50347cc040dSxsa 			}
504d660bd8fSray 			rcsnum_free(rev);
505d660bd8fSray 		}
506d660bd8fSray 
507ea1ee718Sjoris 		if (orange != NULL) {
508e7010264Sjoris 			struct rcs_delta *rdp, *nrdp;
509950e09dcSxsa 			char b[RCS_REV_BUFSZ];
510ea1ee718Sjoris 
511ea1ee718Sjoris 			rcs_rev_select(file, orange);
512e7010264Sjoris 			for (rdp = TAILQ_FIRST(&(file->rf_delta));
513e7010264Sjoris 			    rdp != NULL; rdp = nrdp) {
514e7010264Sjoris 				nrdp = TAILQ_NEXT(rdp, rd_list);
515e7010264Sjoris 
516ea1ee718Sjoris 				/*
517ea1ee718Sjoris 				 * Delete selected revisions.
518ea1ee718Sjoris 				 */
519ea1ee718Sjoris 				if (rdp->rd_flags & RCS_RD_SELECT) {
520ea1ee718Sjoris 					rcsnum_tostr(rdp->rd_num, b, sizeof(b));
52114470f30Sjoris 
52214470f30Sjoris 					if (rdp->rd_locker != NULL) {
52314470f30Sjoris 						errx(1, "%s: can't remove "
52414470f30Sjoris 						    "locked revision %s",
52514470f30Sjoris 						    fpath, b);
52614470f30Sjoris 						continue;
52714470f30Sjoris 					}
52814470f30Sjoris 
5294a1cfd68Sxsa 					if (!(rcsflags & QUIET)) {
5304a1cfd68Sxsa 						(void)fprintf(stderr, "deleting"
5314a1cfd68Sxsa 						    " revision %s\n", b);
5324a1cfd68Sxsa 					}
533ea1ee718Sjoris 					(void)rcs_rev_remove(file, rdp->rd_num);
534ea1ee718Sjoris 				}
535ea1ee718Sjoris 			}
536ea1ee718Sjoris 		}
537ea1ee718Sjoris 
538bc8d37c3Sjoris 		rcs_write(file);
53900f786efSjoris 
5405983b570Sjoris 		if (rcsflags & PRESERVETIME)
541bc8d37c3Sjoris 			rcs_set_mtime(file, rcs_mtime);
542bc8d37c3Sjoris 
543bc8d37c3Sjoris 		rcs_close(file);
54466d00473Sxsa 
545913586deSxsa 		if (!(rcsflags & QUIET))
5464a1cfd68Sxsa 			(void)fprintf(stderr, "done\n");
5477235aeccSderaadt 	}
5487235aeccSderaadt 
5497235aeccSderaadt 	return (0);
5507235aeccSderaadt }
5515983b570Sjoris 
5525983b570Sjoris static void
rcs_attach_symbol(RCSFILE * file,const char * symname)5535983b570Sjoris rcs_attach_symbol(RCSFILE *file, const char *symname)
5545983b570Sjoris {
5555983b570Sjoris 	char *rnum;
5565983b570Sjoris 	RCSNUM *rev;
557950e09dcSxsa 	char rbuf[RCS_REV_BUFSZ];
5585983b570Sjoris 	int rm;
5595983b570Sjoris 
5605983b570Sjoris 	rm = 0;
5615983b570Sjoris 	rev = NULL;
5625983b570Sjoris 	if ((rnum = strrchr(symname, ':')) != NULL) {
5635983b570Sjoris 		if (rnum[1] == '\0')
5645983b570Sjoris 			rev = file->rf_head;
5655983b570Sjoris 		*(rnum++) = '\0';
5665983b570Sjoris 	} else {
5675983b570Sjoris 		rm = 1;
5685983b570Sjoris 	}
5695983b570Sjoris 
5705983b570Sjoris 	if (rev == NULL && rm != 1) {
5715983b570Sjoris 		if ((rev = rcsnum_parse(rnum)) == NULL)
572a3660ae3Sxsa 			errx(1, "bad revision %s", rnum);
5735983b570Sjoris 	}
5745983b570Sjoris 
575ecfbf341Sray 	if (rcsflags & RCSPROG_NFLAG)
5765983b570Sjoris 		rm = 1;
5775983b570Sjoris 
5785983b570Sjoris 	if (rm == 1) {
5795983b570Sjoris 		if (rcs_sym_remove(file, symname) < 0) {
5807249ec9bSderaadt 			if (rcs_errno == RCS_ERR_NOENT &&
581ecfbf341Sray 			    !(rcsflags & RCSPROG_NFLAG))
582a3660ae3Sxsa 				warnx("cannot delete nonexisting symbol %s",
58382ca7eaaSxsa 				    symname);
5845983b570Sjoris 		} else {
585ecfbf341Sray 			if (rcsflags & RCSPROG_NFLAG)
5865983b570Sjoris 				rm = 0;
5875983b570Sjoris 		}
5885983b570Sjoris 	}
5895983b570Sjoris 
5905983b570Sjoris 	if (rm == 0) {
5917249ec9bSderaadt 		if (rcs_sym_add(file, symname, rev) < 0 &&
5927249ec9bSderaadt 		    rcs_errno == RCS_ERR_DUPENT) {
5935983b570Sjoris 			rcsnum_tostr(rcs_sym_getrev(file, symname),
5945983b570Sjoris 			    rbuf, sizeof(rbuf));
595a3660ae3Sxsa 			errx(1, "symbolic name %s already bound to %s",
5965983b570Sjoris 			    symname, rbuf);
5975983b570Sjoris 		}
5985983b570Sjoris 	}
5995983b570Sjoris }
600