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