1 |
|
|
/* $OpenBSD: rcsprog.c,v 1.161 2016/07/04 01:39:12 millert Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2005 Jean-Francois Brousseau <jfb@openbsd.org> |
4 |
|
|
* All rights reserved. |
5 |
|
|
* |
6 |
|
|
* Redistribution and use in source and binary forms, with or without |
7 |
|
|
* modification, are permitted provided that the following conditions |
8 |
|
|
* are met: |
9 |
|
|
* |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. The name of the author may not be used to endorse or promote products |
13 |
|
|
* derived from this software without specific prior written permission. |
14 |
|
|
* |
15 |
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
16 |
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
17 |
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
18 |
|
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 |
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 |
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
21 |
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
22 |
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
23 |
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
24 |
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 |
|
|
*/ |
26 |
|
|
|
27 |
|
|
#include <sys/stat.h> |
28 |
|
|
|
29 |
|
|
#include <err.h> |
30 |
|
|
#include <signal.h> |
31 |
|
|
#include <stdio.h> |
32 |
|
|
#include <stdlib.h> |
33 |
|
|
#include <string.h> |
34 |
|
|
#include <unistd.h> |
35 |
|
|
|
36 |
|
|
#include "rcsprog.h" |
37 |
|
|
|
38 |
|
|
#define RCSPROG_OPTSTRING "A:a:b::c:e::Iik:Ll::m:Mn:N:o:qt::TUu::Vx::z::" |
39 |
|
|
|
40 |
|
|
const char rcs_version[] = "OpenRCS 4.5"; |
41 |
|
|
|
42 |
|
|
int rcsflags; |
43 |
|
|
int rcs_optind; |
44 |
|
|
char *rcs_optarg; |
45 |
|
|
char *rcs_suffixes = RCS_DEFAULT_SUFFIX; |
46 |
|
|
char *rcs_tmpdir = RCS_TMPDIR_DEFAULT; |
47 |
|
|
|
48 |
|
|
struct rcs_prog { |
49 |
|
|
char *prog_name; |
50 |
|
|
int (*prog_hdlr)(int, char **); |
51 |
|
|
void (*prog_usage)(void); |
52 |
|
|
} programs[] = { |
53 |
|
|
{ "rcs", rcs_main, rcs_usage }, |
54 |
|
|
{ "ci", checkin_main, checkin_usage }, |
55 |
|
|
{ "co", checkout_main, checkout_usage }, |
56 |
|
|
{ "rcsclean", rcsclean_main, rcsclean_usage }, |
57 |
|
|
{ "rcsdiff", rcsdiff_main, rcsdiff_usage }, |
58 |
|
|
{ "rcsmerge", rcsmerge_main, rcsmerge_usage }, |
59 |
|
|
{ "rlog", rlog_main, rlog_usage }, |
60 |
|
|
{ "ident", ident_main, ident_usage }, |
61 |
|
|
{ "merge", merge_main, merge_usage }, |
62 |
|
|
}; |
63 |
|
|
|
64 |
|
|
struct wklhead temp_files; |
65 |
|
|
|
66 |
|
|
void sighdlr(int); |
67 |
|
|
static void rcs_attach_symbol(RCSFILE *, const char *); |
68 |
|
|
|
69 |
|
|
/* ARGSUSED */ |
70 |
|
|
void |
71 |
|
|
sighdlr(int sig) |
72 |
|
|
{ |
73 |
|
|
worklist_clean(&temp_files, worklist_unlink); |
74 |
|
|
_exit(1); |
75 |
|
|
} |
76 |
|
|
|
77 |
|
|
int |
78 |
|
|
build_cmd(char ***cmd_argv, char **argv, int argc) |
79 |
|
|
{ |
80 |
|
|
int cmd_argc, i, cur; |
81 |
|
10 |
char *cp, *rcsinit, *linebuf, *lp; |
82 |
|
|
|
83 |
✓✗ |
5 |
if ((rcsinit = getenv("RCSINIT")) == NULL) { |
84 |
|
5 |
*cmd_argv = argv; |
85 |
|
5 |
return argc; |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
cur = argc + 2; |
89 |
|
|
cmd_argc = 0; |
90 |
|
|
*cmd_argv = xcalloc(cur, sizeof(char *)); |
91 |
|
|
(*cmd_argv)[cmd_argc++] = argv[0]; |
92 |
|
|
|
93 |
|
|
linebuf = xstrdup(rcsinit); |
94 |
|
|
for (lp = linebuf; lp != NULL;) { |
95 |
|
|
cp = strsep(&lp, " \t\b\f\n\r\t\v"); |
96 |
|
|
if (cp == NULL) |
97 |
|
|
break; |
98 |
|
|
if (*cp == '\0') |
99 |
|
|
continue; |
100 |
|
|
|
101 |
|
|
if (cmd_argc == cur) { |
102 |
|
|
cur += 8; |
103 |
|
|
*cmd_argv = xreallocarray(*cmd_argv, cur, |
104 |
|
|
sizeof(char *)); |
105 |
|
|
} |
106 |
|
|
|
107 |
|
|
(*cmd_argv)[cmd_argc++] = cp; |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
if (cmd_argc + argc > cur) { |
111 |
|
|
cur = cmd_argc + argc + 1; |
112 |
|
|
*cmd_argv = xreallocarray(*cmd_argv, cur, |
113 |
|
|
sizeof(char *)); |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
for (i = 1; i < argc; i++) |
117 |
|
|
(*cmd_argv)[cmd_argc++] = argv[i]; |
118 |
|
|
|
119 |
|
|
(*cmd_argv)[cmd_argc] = NULL; |
120 |
|
|
|
121 |
|
|
return cmd_argc; |
122 |
|
5 |
} |
123 |
|
|
|
124 |
|
|
int |
125 |
|
|
main(int argc, char **argv) |
126 |
|
|
{ |
127 |
|
|
u_int i; |
128 |
|
10 |
char **cmd_argv; |
129 |
|
|
int ret, cmd_argc; |
130 |
|
|
|
131 |
✗✓ |
5 |
if (pledge("stdio rpath wpath cpath fattr flock getpw", NULL) == -1) |
132 |
|
|
err(2, "pledge"); |
133 |
|
|
|
134 |
|
|
ret = -1; |
135 |
|
5 |
rcs_optind = 1; |
136 |
|
5 |
SLIST_INIT(&temp_files); |
137 |
|
|
|
138 |
|
5 |
cmd_argc = build_cmd(&cmd_argv, argv, argc); |
139 |
|
|
|
140 |
|
5 |
if ((rcs_tmpdir = getenv("TMPDIR")) == NULL) |
141 |
|
|
rcs_tmpdir = RCS_TMPDIR_DEFAULT; |
142 |
|
|
|
143 |
|
5 |
signal(SIGHUP, sighdlr); |
144 |
|
5 |
signal(SIGINT, sighdlr); |
145 |
|
5 |
signal(SIGQUIT, sighdlr); |
146 |
|
5 |
signal(SIGABRT, sighdlr); |
147 |
|
5 |
signal(SIGALRM, sighdlr); |
148 |
|
5 |
signal(SIGTERM, sighdlr); |
149 |
|
|
|
150 |
✓✗ |
90 |
for (i = 0; i < (sizeof(programs)/sizeof(programs[0])); i++) |
151 |
✓✓ |
45 |
if (strcmp(__progname, programs[i].prog_name) == 0) { |
152 |
|
5 |
usage = programs[i].prog_usage; |
153 |
|
5 |
ret = programs[i].prog_hdlr(cmd_argc, cmd_argv); |
154 |
|
5 |
break; |
155 |
|
|
} |
156 |
|
|
|
157 |
|
|
/* clean up temporary files */ |
158 |
|
|
worklist_run(&temp_files, worklist_unlink); |
159 |
|
|
|
160 |
|
|
exit(ret); |
161 |
|
|
} |
162 |
|
|
|
163 |
|
|
|
164 |
|
|
__dead void |
165 |
|
|
rcs_usage(void) |
166 |
|
|
{ |
167 |
|
|
fprintf(stderr, |
168 |
|
|
"usage: rcs [-IiLqTUV] [-Aoldfile] [-ausers] [-b[rev]]\n" |
169 |
|
|
" [-cstring] [-e[users]] [-kmode] [-l[rev]] [-mrev:msg]\n" |
170 |
|
|
" [-orev] [-t[str]] [-u[rev]] [-xsuffixes] file ...\n"); |
171 |
|
|
|
172 |
|
|
exit(1); |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
/* |
176 |
|
|
* rcs_main() |
177 |
|
|
* |
178 |
|
|
* Handler for the `rcs' program. |
179 |
|
|
* Returns 0 on success, or >0 on error. |
180 |
|
|
*/ |
181 |
|
|
int |
182 |
|
|
rcs_main(int argc, char **argv) |
183 |
|
|
{ |
184 |
|
|
int fd; |
185 |
|
|
int i, j, ch, flags, kflag, lkmode; |
186 |
|
|
const char *nflag, *oldfilename, *orange; |
187 |
|
|
char fpath[PATH_MAX]; |
188 |
|
|
char *logstr, *logmsg, *descfile; |
189 |
|
|
char *alist, *comment, *elist, *lrev, *urev; |
190 |
|
|
mode_t fmode; |
191 |
|
|
RCSFILE *file; |
192 |
|
|
RCSNUM *logrev; |
193 |
|
|
struct rcs_access *acp; |
194 |
|
|
time_t rcs_mtime = -1; |
195 |
|
|
|
196 |
|
|
kflag = RCS_KWEXP_ERR; |
197 |
|
|
lkmode = RCS_LOCK_INVAL; |
198 |
|
|
fmode = S_IRUSR|S_IRGRP|S_IROTH; |
199 |
|
|
flags = RCS_RDWR|RCS_PARSE_FULLY; |
200 |
|
|
lrev = urev = descfile = NULL; |
201 |
|
|
logstr = alist = comment = elist = NULL; |
202 |
|
|
nflag = oldfilename = orange = NULL; |
203 |
|
|
|
204 |
|
|
/* match GNU */ |
205 |
|
|
if (1 < argc && argv[1][0] != '-') |
206 |
|
|
warnx("warning: No options were given; " |
207 |
|
|
"this usage is obsolescent."); |
208 |
|
|
|
209 |
|
|
while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) { |
210 |
|
|
switch (ch) { |
211 |
|
|
case 'A': |
212 |
|
|
oldfilename = rcs_optarg; |
213 |
|
|
rcsflags |= CO_ACLAPPEND; |
214 |
|
|
break; |
215 |
|
|
case 'a': |
216 |
|
|
alist = rcs_optarg; |
217 |
|
|
break; |
218 |
|
|
case 'c': |
219 |
|
|
comment = rcs_optarg; |
220 |
|
|
break; |
221 |
|
|
case 'e': |
222 |
|
|
elist = rcs_optarg; |
223 |
|
|
rcsflags |= RCSPROG_EFLAG; |
224 |
|
|
break; |
225 |
|
|
case 'I': |
226 |
|
|
rcsflags |= INTERACTIVE; |
227 |
|
|
break; |
228 |
|
|
case 'i': |
229 |
|
|
flags |= RCS_CREATE; |
230 |
|
|
break; |
231 |
|
|
case 'k': |
232 |
|
|
kflag = rcs_kflag_get(rcs_optarg); |
233 |
|
|
if (RCS_KWEXP_INVAL(kflag)) { |
234 |
|
|
warnx("invalid RCS keyword substitution mode"); |
235 |
|
|
(usage)(); |
236 |
|
|
} |
237 |
|
|
break; |
238 |
|
|
case 'L': |
239 |
|
|
if (lkmode == RCS_LOCK_LOOSE) |
240 |
|
|
warnx("-U overridden by -L"); |
241 |
|
|
lkmode = RCS_LOCK_STRICT; |
242 |
|
|
break; |
243 |
|
|
case 'l': |
244 |
|
|
if (rcsflags & RCSPROG_UFLAG) |
245 |
|
|
warnx("-u overridden by -l"); |
246 |
|
|
lrev = rcs_optarg; |
247 |
|
|
rcsflags &= ~RCSPROG_UFLAG; |
248 |
|
|
rcsflags |= RCSPROG_LFLAG; |
249 |
|
|
break; |
250 |
|
|
case 'm': |
251 |
|
|
free(logstr); |
252 |
|
|
logstr = xstrdup(rcs_optarg); |
253 |
|
|
break; |
254 |
|
|
case 'M': |
255 |
|
|
/* ignore for the moment */ |
256 |
|
|
break; |
257 |
|
|
case 'n': |
258 |
|
|
nflag = rcs_optarg; |
259 |
|
|
break; |
260 |
|
|
case 'N': |
261 |
|
|
nflag = rcs_optarg; |
262 |
|
|
rcsflags |= RCSPROG_NFLAG; |
263 |
|
|
break; |
264 |
|
|
case 'o': |
265 |
|
|
orange = rcs_optarg; |
266 |
|
|
break; |
267 |
|
|
case 'q': |
268 |
|
|
rcsflags |= QUIET; |
269 |
|
|
break; |
270 |
|
|
case 't': |
271 |
|
|
descfile = rcs_optarg; |
272 |
|
|
rcsflags |= DESCRIPTION; |
273 |
|
|
break; |
274 |
|
|
case 'T': |
275 |
|
|
rcsflags |= PRESERVETIME; |
276 |
|
|
break; |
277 |
|
|
case 'U': |
278 |
|
|
if (lkmode == RCS_LOCK_STRICT) |
279 |
|
|
warnx("-L overridden by -U"); |
280 |
|
|
lkmode = RCS_LOCK_LOOSE; |
281 |
|
|
break; |
282 |
|
|
case 'u': |
283 |
|
|
if (rcsflags & RCSPROG_LFLAG) |
284 |
|
|
warnx("-l overridden by -u"); |
285 |
|
|
urev = rcs_optarg; |
286 |
|
|
rcsflags &= ~RCSPROG_LFLAG; |
287 |
|
|
rcsflags |= RCSPROG_UFLAG; |
288 |
|
|
break; |
289 |
|
|
case 'V': |
290 |
|
|
printf("%s\n", rcs_version); |
291 |
|
|
exit(0); |
292 |
|
|
case 'x': |
293 |
|
|
/* Use blank extension if none given. */ |
294 |
|
|
rcs_suffixes = rcs_optarg ? rcs_optarg : ""; |
295 |
|
|
break; |
296 |
|
|
case 'z': |
297 |
|
|
/* |
298 |
|
|
* kept for compatibility |
299 |
|
|
*/ |
300 |
|
|
break; |
301 |
|
|
default: |
302 |
|
|
(usage)(); |
303 |
|
|
} |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
argc -= rcs_optind; |
307 |
|
|
argv += rcs_optind; |
308 |
|
|
|
309 |
|
|
if (argc == 0) { |
310 |
|
|
warnx("no input file"); |
311 |
|
|
(usage)(); |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
for (i = 0; i < argc; i++) { |
315 |
|
|
fd = rcs_choosefile(argv[i], fpath, sizeof(fpath)); |
316 |
|
|
if (fd < 0 && !(flags & RCS_CREATE)) { |
317 |
|
|
warn("%s", fpath); |
318 |
|
|
continue; |
319 |
|
|
} |
320 |
|
|
|
321 |
|
|
if (!(rcsflags & QUIET)) |
322 |
|
|
(void)fprintf(stderr, "RCS file: %s\n", fpath); |
323 |
|
|
|
324 |
|
|
if ((file = rcs_open(fpath, fd, flags, fmode)) == NULL) { |
325 |
|
|
close(fd); |
326 |
|
|
continue; |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
if (rcsflags & DESCRIPTION) { |
330 |
|
|
if (rcs_set_description(file, descfile, rcsflags) == -1) { |
331 |
|
|
warn("%s", descfile); |
332 |
|
|
rcs_close(file); |
333 |
|
|
continue; |
334 |
|
|
} |
335 |
|
|
} |
336 |
|
|
else if (flags & RCS_CREATE) { |
337 |
|
|
if (rcs_set_description(file, NULL, rcsflags) == -1) { |
338 |
|
|
warn("stdin"); |
339 |
|
|
rcs_close(file); |
340 |
|
|
continue; |
341 |
|
|
} |
342 |
|
|
} |
343 |
|
|
|
344 |
|
|
if (rcsflags & PRESERVETIME) |
345 |
|
|
rcs_mtime = rcs_get_mtime(file); |
346 |
|
|
|
347 |
|
|
if (nflag != NULL) |
348 |
|
|
rcs_attach_symbol(file, nflag); |
349 |
|
|
|
350 |
|
|
if (logstr != NULL) { |
351 |
|
|
if ((logmsg = strchr(logstr, ':')) == NULL) { |
352 |
|
|
warnx("missing log message"); |
353 |
|
|
rcs_close(file); |
354 |
|
|
continue; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
*logmsg++ = '\0'; |
358 |
|
|
if ((logrev = rcsnum_parse(logstr)) == NULL) { |
359 |
|
|
warnx("`%s' bad revision number", logstr); |
360 |
|
|
rcs_close(file); |
361 |
|
|
continue; |
362 |
|
|
} |
363 |
|
|
|
364 |
|
|
if (rcs_rev_setlog(file, logrev, logmsg) < 0) { |
365 |
|
|
warnx("failed to set logmsg for `%s' to `%s'", |
366 |
|
|
logstr, logmsg); |
367 |
|
|
rcs_close(file); |
368 |
|
|
rcsnum_free(logrev); |
369 |
|
|
continue; |
370 |
|
|
} |
371 |
|
|
|
372 |
|
|
rcsnum_free(logrev); |
373 |
|
|
} |
374 |
|
|
|
375 |
|
|
/* entries to add from <oldfile> */ |
376 |
|
|
if (rcsflags & CO_ACLAPPEND) { |
377 |
|
|
RCSFILE *oldfile; |
378 |
|
|
int ofd; |
379 |
|
|
char ofpath[PATH_MAX]; |
380 |
|
|
|
381 |
|
|
ofd = rcs_choosefile(oldfilename, ofpath, sizeof(ofpath)); |
382 |
|
|
if (ofd < 0) { |
383 |
|
|
if (!(flags & RCS_CREATE)) |
384 |
|
|
warn("%s", ofpath); |
385 |
|
|
exit(1); |
386 |
|
|
} |
387 |
|
|
if ((oldfile = rcs_open(ofpath, ofd, RCS_READ)) == NULL) |
388 |
|
|
exit(1); |
389 |
|
|
|
390 |
|
|
TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list) |
391 |
|
|
rcs_access_add(file, acp->ra_name); |
392 |
|
|
|
393 |
|
|
rcs_close(oldfile); |
394 |
|
|
(void)close(ofd); |
395 |
|
|
} |
396 |
|
|
|
397 |
|
|
/* entries to add to the access list */ |
398 |
|
|
if (alist != NULL) { |
399 |
|
|
struct rcs_argvector *aargv; |
400 |
|
|
|
401 |
|
|
aargv = rcs_strsplit(alist, ","); |
402 |
|
|
for (j = 0; aargv->argv[j] != NULL; j++) |
403 |
|
|
rcs_access_add(file, aargv->argv[j]); |
404 |
|
|
|
405 |
|
|
rcs_argv_destroy(aargv); |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
if (comment != NULL) |
409 |
|
|
rcs_comment_set(file, comment); |
410 |
|
|
|
411 |
|
|
if (elist != NULL) { |
412 |
|
|
struct rcs_argvector *eargv; |
413 |
|
|
|
414 |
|
|
eargv = rcs_strsplit(elist, ","); |
415 |
|
|
for (j = 0; eargv->argv[j] != NULL; j++) |
416 |
|
|
rcs_access_remove(file, eargv->argv[j]); |
417 |
|
|
|
418 |
|
|
rcs_argv_destroy(eargv); |
419 |
|
|
} else if (rcsflags & RCSPROG_EFLAG) { |
420 |
|
|
struct rcs_access *rap; |
421 |
|
|
|
422 |
|
|
/* XXX rcs_access_remove(file, NULL); ?? */ |
423 |
|
|
while (!TAILQ_EMPTY(&(file->rf_access))) { |
424 |
|
|
rap = TAILQ_FIRST(&(file->rf_access)); |
425 |
|
|
TAILQ_REMOVE(&(file->rf_access), rap, ra_list); |
426 |
|
|
free(rap->ra_name); |
427 |
|
|
free(rap); |
428 |
|
|
} |
429 |
|
|
/* not synced anymore */ |
430 |
|
|
file->rf_flags &= ~RCS_SYNCED; |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
rcs_kwexp_set(file, kflag); |
434 |
|
|
|
435 |
|
|
if (lkmode != RCS_LOCK_INVAL) |
436 |
|
|
(void)rcs_lock_setmode(file, lkmode); |
437 |
|
|
|
438 |
|
|
if (rcsflags & RCSPROG_LFLAG) { |
439 |
|
|
RCSNUM *rev; |
440 |
|
|
const char *username; |
441 |
|
|
char rev_str[RCS_REV_BUFSZ]; |
442 |
|
|
|
443 |
|
|
if (file->rf_head == NULL) { |
444 |
|
|
warnx("%s contains no revisions", fpath); |
445 |
|
|
rcs_close(file); |
446 |
|
|
continue; |
447 |
|
|
} |
448 |
|
|
|
449 |
|
|
if ((username = getlogin()) == NULL) |
450 |
|
|
err(1, "getlogin"); |
451 |
|
|
if (lrev == NULL) { |
452 |
|
|
rev = rcsnum_alloc(); |
453 |
|
|
rcsnum_cpy(file->rf_head, rev, 0); |
454 |
|
|
} else if ((rev = rcsnum_parse(lrev)) == NULL) { |
455 |
|
|
warnx("unable to unlock file"); |
456 |
|
|
rcs_close(file); |
457 |
|
|
continue; |
458 |
|
|
} |
459 |
|
|
rcsnum_tostr(rev, rev_str, sizeof(rev_str)); |
460 |
|
|
/* Make sure revision exists. */ |
461 |
|
|
if (rcs_findrev(file, rev) == NULL) |
462 |
|
|
errx(1, "%s: cannot lock nonexisting " |
463 |
|
|
"revision %s", fpath, rev_str); |
464 |
|
|
if (rcs_lock_add(file, username, rev) != -1 && |
465 |
|
|
!(rcsflags & QUIET)) |
466 |
|
|
(void)fprintf(stderr, "%s locked\n", rev_str); |
467 |
|
|
rcsnum_free(rev); |
468 |
|
|
} |
469 |
|
|
|
470 |
|
|
if (rcsflags & RCSPROG_UFLAG) { |
471 |
|
|
RCSNUM *rev; |
472 |
|
|
const char *username; |
473 |
|
|
char rev_str[RCS_REV_BUFSZ]; |
474 |
|
|
|
475 |
|
|
if (file->rf_head == NULL) { |
476 |
|
|
warnx("%s contains no revisions", fpath); |
477 |
|
|
rcs_close(file); |
478 |
|
|
continue; |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
if ((username = getlogin()) == NULL) |
482 |
|
|
err(1, "getlogin"); |
483 |
|
|
if (urev == NULL) { |
484 |
|
|
rev = rcsnum_alloc(); |
485 |
|
|
rcsnum_cpy(file->rf_head, rev, 0); |
486 |
|
|
} else if ((rev = rcsnum_parse(urev)) == NULL) { |
487 |
|
|
warnx("unable to unlock file"); |
488 |
|
|
rcs_close(file); |
489 |
|
|
continue; |
490 |
|
|
} |
491 |
|
|
rcsnum_tostr(rev, rev_str, sizeof(rev_str)); |
492 |
|
|
/* Make sure revision exists. */ |
493 |
|
|
if (rcs_findrev(file, rev) == NULL) |
494 |
|
|
errx(1, "%s: cannot unlock nonexisting " |
495 |
|
|
"revision %s", fpath, rev_str); |
496 |
|
|
if (rcs_lock_remove(file, username, rev) == -1 && |
497 |
|
|
!(rcsflags & QUIET)) |
498 |
|
|
warnx("%s: warning: No locks are set.", fpath); |
499 |
|
|
else { |
500 |
|
|
if (!(rcsflags & QUIET)) |
501 |
|
|
(void)fprintf(stderr, |
502 |
|
|
"%s unlocked\n", rev_str); |
503 |
|
|
} |
504 |
|
|
rcsnum_free(rev); |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
if (orange != NULL) { |
508 |
|
|
struct rcs_delta *rdp, *nrdp; |
509 |
|
|
char b[RCS_REV_BUFSZ]; |
510 |
|
|
|
511 |
|
|
rcs_rev_select(file, orange); |
512 |
|
|
for (rdp = TAILQ_FIRST(&(file->rf_delta)); |
513 |
|
|
rdp != NULL; rdp = nrdp) { |
514 |
|
|
nrdp = TAILQ_NEXT(rdp, rd_list); |
515 |
|
|
|
516 |
|
|
/* |
517 |
|
|
* Delete selected revisions. |
518 |
|
|
*/ |
519 |
|
|
if (rdp->rd_flags & RCS_RD_SELECT) { |
520 |
|
|
rcsnum_tostr(rdp->rd_num, b, sizeof(b)); |
521 |
|
|
if (!(rcsflags & QUIET)) { |
522 |
|
|
(void)fprintf(stderr, "deleting" |
523 |
|
|
" revision %s\n", b); |
524 |
|
|
} |
525 |
|
|
(void)rcs_rev_remove(file, rdp->rd_num); |
526 |
|
|
} |
527 |
|
|
} |
528 |
|
|
} |
529 |
|
|
|
530 |
|
|
rcs_write(file); |
531 |
|
|
|
532 |
|
|
if (rcsflags & PRESERVETIME) |
533 |
|
|
rcs_set_mtime(file, rcs_mtime); |
534 |
|
|
|
535 |
|
|
rcs_close(file); |
536 |
|
|
|
537 |
|
|
if (!(rcsflags & QUIET)) |
538 |
|
|
(void)fprintf(stderr, "done\n"); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
return (0); |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
static void |
545 |
|
|
rcs_attach_symbol(RCSFILE *file, const char *symname) |
546 |
|
|
{ |
547 |
|
|
char *rnum; |
548 |
|
|
RCSNUM *rev; |
549 |
|
|
char rbuf[RCS_REV_BUFSZ]; |
550 |
|
|
int rm; |
551 |
|
|
|
552 |
|
|
rm = 0; |
553 |
|
|
rev = NULL; |
554 |
|
|
if ((rnum = strrchr(symname, ':')) != NULL) { |
555 |
|
|
if (rnum[1] == '\0') |
556 |
|
|
rev = file->rf_head; |
557 |
|
|
*(rnum++) = '\0'; |
558 |
|
|
} else { |
559 |
|
|
rm = 1; |
560 |
|
|
} |
561 |
|
|
|
562 |
|
|
if (rev == NULL && rm != 1) { |
563 |
|
|
if ((rev = rcsnum_parse(rnum)) == NULL) |
564 |
|
|
errx(1, "bad revision %s", rnum); |
565 |
|
|
} |
566 |
|
|
|
567 |
|
|
if (rcsflags & RCSPROG_NFLAG) |
568 |
|
|
rm = 1; |
569 |
|
|
|
570 |
|
|
if (rm == 1) { |
571 |
|
|
if (rcs_sym_remove(file, symname) < 0) { |
572 |
|
|
if (rcs_errno == RCS_ERR_NOENT && |
573 |
|
|
!(rcsflags & RCSPROG_NFLAG)) |
574 |
|
|
warnx("cannot delete nonexisting symbol %s", |
575 |
|
|
symname); |
576 |
|
|
} else { |
577 |
|
|
if (rcsflags & RCSPROG_NFLAG) |
578 |
|
|
rm = 0; |
579 |
|
|
} |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
if (rm == 0) { |
583 |
|
|
if (rcs_sym_add(file, symname, rev) < 0 && |
584 |
|
|
rcs_errno == RCS_ERR_DUPENT) { |
585 |
|
|
rcsnum_tostr(rcs_sym_getrev(file, symname), |
586 |
|
|
rbuf, sizeof(rbuf)); |
587 |
|
|
errx(1, "symbolic name %s already bound to %s", |
588 |
|
|
symname, rbuf); |
589 |
|
|
} |
590 |
|
|
} |
591 |
|
|
} |