Bug Summary

File:src/usr.bin/rcs/rcs.c
Warning:line 891, column 16
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name rcs.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/rcs/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/rcs -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/rcs/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/rcs/rcs.c
1/* $OpenBSD: rcs.c,v 1.89 2021/11/28 19:28:42 deraadt Exp $ */
2/*
3 * Copyright (c) 2004 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/types.h>
28#include <sys/stat.h>
29
30#include <ctype.h>
31#include <err.h>
32#include <errno(*__errno()).h>
33#include <pwd.h>
34#include <stdarg.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39
40#include "diff.h"
41#include "rcs.h"
42#include "rcsparse.h"
43#include "rcsprog.h"
44#include "rcsutil.h"
45#include "xmalloc.h"
46
47#define _MAXBSIZE(64 * 1024) (64 * 1024)
48
49#define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
50
51/* invalid characters in RCS states */
52static const char rcs_state_invch[] = RCS_STATE_INVALCHAR"$,:;@";
53
54/* invalid characters in RCS symbol names */
55static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR"$,.:;@";
56
57struct rcs_kw rcs_expkw[] = {
58 { "Author", RCS_KW_AUTHOR0x1000 },
59 { "Date", RCS_KW_DATE0x2000 },
60 { "Locker", RCS_KW_LOCKER0x10000 },
61 { "Header", RCS_KW_HEADER((0x0100 | 0x0200 | 0x2000 | 0x1000 | 0x0800 | 0x10000) | 0x0010
)
},
62 { "Id", RCS_KW_ID(0x0100 | 0x0200 | 0x2000 | 0x1000 | 0x0800 | 0x10000) },
63 { "OpenBSD", RCS_KW_ID(0x0100 | 0x0200 | 0x2000 | 0x1000 | 0x0800 | 0x10000) },
64 { "Log", RCS_KW_LOG0x4000 },
65 { "Name", RCS_KW_NAME0x8000 },
66 { "RCSfile", RCS_KW_RCSFILE0x0100 },
67 { "Revision", RCS_KW_REVISION0x0200 },
68 { "Source", RCS_KW_SOURCE0x0400 },
69 { "State", RCS_KW_STATE0x0800 },
70 { "Mdocdate", RCS_KW_MDOCDATE0x0020 },
71};
72
73int rcs_errno = RCS_ERR_NOERR0;
74char *timezone_flag = NULL((void *)0);
75
76int rcs_patch_lines(struct rcs_lines *, struct rcs_lines *);
77static int rcs_movefile(char *, char *, mode_t, u_int);
78
79static void rcs_freedelta(struct rcs_delta *);
80static void rcs_strprint(const u_char *, size_t, FILE *);
81
82static BUF *rcs_expand_keywords(char *, struct rcs_delta *, BUF *, int);
83
84RCSFILE *
85rcs_open(const char *path, int fd, int flags, ...)
86{
87 int mode;
88 mode_t fmode;
89 RCSFILE *rfp;
90 va_list vap;
91 struct rcs_delta *rdp;
92 struct rcs_lock *lkr;
93
94 fmode = S_IRUSR0000400|S_IRGRP0000040|S_IROTH0000004;
95 flags &= 0xffff; /* ditch any internal flags */
96
97 if (flags & RCS_CREATE(1<<2)) {
98 va_start(vap, flags)__builtin_va_start(vap, flags);
99 mode = va_arg(vap, int)__builtin_va_arg(vap, int);
100 va_end(vap)__builtin_va_end(vap);
101 fmode = (mode_t)mode;
102 }
103
104 rfp = xcalloc(1, sizeof(*rfp));
105
106 rfp->rf_path = xstrdup(path);
107 rfp->rf_flags = flags | RCS_SLOCK(1<<6) | RCS_SYNCED(1<<5);
108 rfp->rf_mode = fmode;
109 if (fd == -1)
110 rfp->rf_file = NULL((void *)0);
111 else if ((rfp->rf_file = fdopen(fd, "r")) == NULL((void *)0))
112 err(1, "rcs_open: fdopen: `%s'", path);
113
114 TAILQ_INIT(&(rfp->rf_delta))do { (&(rfp->rf_delta))->tqh_first = ((void *)0); (
&(rfp->rf_delta))->tqh_last = &(&(rfp->rf_delta
))->tqh_first; } while (0)
;
115 TAILQ_INIT(&(rfp->rf_access))do { (&(rfp->rf_access))->tqh_first = ((void *)0); (
&(rfp->rf_access))->tqh_last = &(&(rfp->
rf_access))->tqh_first; } while (0)
;
116 TAILQ_INIT(&(rfp->rf_symbols))do { (&(rfp->rf_symbols))->tqh_first = ((void *)0);
(&(rfp->rf_symbols))->tqh_last = &(&(rfp->
rf_symbols))->tqh_first; } while (0)
;
117 TAILQ_INIT(&(rfp->rf_locks))do { (&(rfp->rf_locks))->tqh_first = ((void *)0); (
&(rfp->rf_locks))->tqh_last = &(&(rfp->rf_locks
))->tqh_first; } while (0)
;
118
119 if (!(rfp->rf_flags & RCS_CREATE(1<<2))) {
120 if (rcsparse_init(rfp))
121 errx(1, "could not parse admin data");
122
123 /* fill in rd_locker */
124 TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list)for((lkr) = ((&(rfp->rf_locks))->tqh_first); (lkr) !=
((void *)0); (lkr) = ((lkr)->rl_list.tqe_next))
{
125 if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL((void *)0)) {
126 rcs_close(rfp);
127 return (NULL((void *)0));
128 }
129
130 rdp->rd_locker = xstrdup(lkr->rl_name);
131 }
132 }
133
134 return (rfp);
135}
136
137/*
138 * rcs_close()
139 *
140 * Close an RCS file handle.
141 */
142void
143rcs_close(RCSFILE *rfp)
144{
145 struct rcs_delta *rdp;
146 struct rcs_access *rap;
147 struct rcs_lock *rlp;
148 struct rcs_sym *rsp;
149
150 if ((rfp->rf_flags & RCS_WRITE(1<<1)) && !(rfp->rf_flags & RCS_SYNCED(1<<5)))
151 rcs_write(rfp);
152
153 while (!TAILQ_EMPTY(&(rfp->rf_delta))(((&(rfp->rf_delta))->tqh_first) == ((void *)0))) {
154 rdp = TAILQ_FIRST(&(rfp->rf_delta))((&(rfp->rf_delta))->tqh_first);
155 TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list)do { if (((rdp)->rd_list.tqe_next) != ((void *)0)) (rdp)->
rd_list.tqe_next->rd_list.tqe_prev = (rdp)->rd_list.tqe_prev
; else (&(rfp->rf_delta))->tqh_last = (rdp)->rd_list
.tqe_prev; *(rdp)->rd_list.tqe_prev = (rdp)->rd_list.tqe_next
; ; ; } while (0)
;
156 rcs_freedelta(rdp);
157 }
158
159 while (!TAILQ_EMPTY(&(rfp->rf_access))(((&(rfp->rf_access))->tqh_first) == ((void *)0))) {
160 rap = TAILQ_FIRST(&(rfp->rf_access))((&(rfp->rf_access))->tqh_first);
161 TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list)do { if (((rap)->ra_list.tqe_next) != ((void *)0)) (rap)->
ra_list.tqe_next->ra_list.tqe_prev = (rap)->ra_list.tqe_prev
; else (&(rfp->rf_access))->tqh_last = (rap)->ra_list
.tqe_prev; *(rap)->ra_list.tqe_prev = (rap)->ra_list.tqe_next
; ; ; } while (0)
;
162 free(rap->ra_name);
163 free(rap);
164 }
165
166 while (!TAILQ_EMPTY(&(rfp->rf_symbols))(((&(rfp->rf_symbols))->tqh_first) == ((void *)0))) {
167 rsp = TAILQ_FIRST(&(rfp->rf_symbols))((&(rfp->rf_symbols))->tqh_first);
168 TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list)do { if (((rsp)->rs_list.tqe_next) != ((void *)0)) (rsp)->
rs_list.tqe_next->rs_list.tqe_prev = (rsp)->rs_list.tqe_prev
; else (&(rfp->rf_symbols))->tqh_last = (rsp)->rs_list
.tqe_prev; *(rsp)->rs_list.tqe_prev = (rsp)->rs_list.tqe_next
; ; ; } while (0)
;
169 rcsnum_free(rsp->rs_num);
170 free(rsp->rs_name);
171 free(rsp);
172 }
173
174 while (!TAILQ_EMPTY(&(rfp->rf_locks))(((&(rfp->rf_locks))->tqh_first) == ((void *)0))) {
175 rlp = TAILQ_FIRST(&(rfp->rf_locks))((&(rfp->rf_locks))->tqh_first);
176 TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list)do { if (((rlp)->rl_list.tqe_next) != ((void *)0)) (rlp)->
rl_list.tqe_next->rl_list.tqe_prev = (rlp)->rl_list.tqe_prev
; else (&(rfp->rf_locks))->tqh_last = (rlp)->rl_list
.tqe_prev; *(rlp)->rl_list.tqe_prev = (rlp)->rl_list.tqe_next
; ; ; } while (0)
;
177 rcsnum_free(rlp->rl_num);
178 free(rlp->rl_name);
179 free(rlp);
180 }
181
182 rcsnum_free(rfp->rf_head);
183 rcsnum_free(rfp->rf_branch);
184
185 if (rfp->rf_file != NULL((void *)0))
186 fclose(rfp->rf_file);
187
188 free(rfp->rf_path);
189 free(rfp->rf_comment);
190 free(rfp->rf_expand);
191 free(rfp->rf_desc);
192 if (rfp->rf_pdata != NULL((void *)0))
193 rcsparse_free(rfp);
194
195 free(rfp);
196}
197
198/*
199 * rcs_write()
200 *
201 * Write the contents of the RCS file handle <rfp> to disk in the file whose
202 * path is in <rf_path>.
203 */
204void
205rcs_write(RCSFILE *rfp)
206{
207 FILE *fp;
208 char numbuf[RCS_REV_BUFSZ64], *fn;
209 struct rcs_access *ap;
210 struct rcs_sym *symp;
211 struct rcs_branch *brp;
212 struct rcs_delta *rdp;
213 struct rcs_lock *lkp;
214 size_t len;
215 int fd;
216
217 fn = NULL((void *)0);
218
219 if (rfp->rf_flags & RCS_SYNCED(1<<5))
220 return;
221
222 /* Write operations need the whole file parsed */
223 if (rcsparse_deltatexts(rfp, NULL((void *)0)))
224 errx(1, "problem parsing deltatexts");
225
226 (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", rcs_tmpdir);
227
228 if ((fd = mkstemp(fn)) == -1)
229 err(1, "%s", fn);
230
231 if ((fp = fdopen(fd, "w+")) == NULL((void *)0)) {
232 int saved_errno;
233
234 saved_errno = errno(*__errno());
235 (void)unlink(fn);
236 errno(*__errno()) = saved_errno;
237 err(1, "%s", fn);
238 }
239
240 worklist_add(fn, &temp_files);
241
242 if (rfp->rf_head != NULL((void *)0))
243 rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
244 else
245 numbuf[0] = '\0';
246
247 fprintf(fp, "head\t%s;\n", numbuf);
248
249 if (rfp->rf_branch != NULL((void *)0)) {
250 rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
251 fprintf(fp, "branch\t%s;\n", numbuf);
252 }
253
254 fputs("access", fp);
255 TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list)for((ap) = ((&(rfp->rf_access))->tqh_first); (ap) !=
((void *)0); (ap) = ((ap)->ra_list.tqe_next))
{
256 fprintf(fp, "\n\t%s", ap->ra_name);
257 }
258 fputs(";\n", fp);
259
260 fprintf(fp, "symbols");
261 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list)for((symp) = ((&(rfp->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
{
262 if (RCSNUM_ISBRANCH(symp->rs_num)((symp->rs_num)->rn_len % 2))
263 rcsnum_addmagic(symp->rs_num);
264 rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
265 fprintf(fp, "\n\t%s:%s", symp->rs_name, numbuf);
266 }
267 fprintf(fp, ";\n");
268
269 fprintf(fp, "locks");
270 TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list)for((lkp) = ((&(rfp->rf_locks))->tqh_first); (lkp) !=
((void *)0); (lkp) = ((lkp)->rl_list.tqe_next))
{
271 rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
272 fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
273 }
274
275 fprintf(fp, ";");
276
277 if (rfp->rf_flags & RCS_SLOCK(1<<6))
278 fprintf(fp, " strict;");
279 fputc('\n', fp);
280
281 fputs("comment\t@", fp);
282 if (rfp->rf_comment != NULL((void *)0)) {
283 rcs_strprint((const u_char *)rfp->rf_comment,
284 strlen(rfp->rf_comment), fp);
285 fputs("@;\n", fp);
286 } else
287 fputs("# @;\n", fp);
288
289 if (rfp->rf_expand != NULL((void *)0)) {
290 fputs("expand @", fp);
291 rcs_strprint((const u_char *)rfp->rf_expand,
292 strlen(rfp->rf_expand), fp);
293 fputs("@;\n", fp);
294 }
295
296 fputs("\n\n", fp);
297
298 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list)for((rdp) = ((&(rfp->rf_delta))->tqh_first); (rdp) !=
((void *)0); (rdp) = ((rdp)->rd_list.tqe_next))
{
299 fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
300 sizeof(numbuf)));
301 fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
302 rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
303 rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
304 rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
305 fprintf(fp, "\tauthor %s;\tstate %s;\n",
306 rdp->rd_author, rdp->rd_state);
307 fputs("branches", fp);
308 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list)for((brp) = ((&(rdp->rd_branches))->tqh_first); (brp
) != ((void *)0); (brp) = ((brp)->rb_list.tqe_next))
{
309 fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
310 sizeof(numbuf)));
311 }
312 fputs(";\n", fp);
313 fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
314 numbuf, sizeof(numbuf)));
315 }
316
317 fputs("\ndesc\n@", fp);
318 if (rfp->rf_desc != NULL((void *)0) && (len = strlen(rfp->rf_desc)) > 0) {
319 rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
320 if (rfp->rf_desc[len-1] != '\n')
321 fputc('\n', fp);
322 }
323 fputs("@\n", fp);
324
325 /* deltatexts */
326 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list)for((rdp) = ((&(rfp->rf_delta))->tqh_first); (rdp) !=
((void *)0); (rdp) = ((rdp)->rd_list.tqe_next))
{
327 fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
328 sizeof(numbuf)));
329 fputs("log\n@", fp);
330 if (rdp->rd_log != NULL((void *)0)) {
331 len = strlen(rdp->rd_log);
332 rcs_strprint((const u_char *)rdp->rd_log, len, fp);
333 if (len == 0 || rdp->rd_log[len-1] != '\n')
334 fputc('\n', fp);
335 }
336 fputs("@\ntext\n@", fp);
337 if (rdp->rd_text != NULL((void *)0))
338 rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
339 fputs("@\n", fp);
340 }
341 (void)fclose(fp);
342
343 if (rcs_movefile(fn, rfp->rf_path, rfp->rf_mode, rfp->rf_flags) == -1) {
344 (void)unlink(fn);
345 errx(1, "rcs_movefile failed");
346 }
347
348 rfp->rf_flags |= RCS_SYNCED(1<<5);
349
350 free(fn);
351}
352
353/*
354 * rcs_movefile()
355 *
356 * Move a file using rename(2) if possible and copying if not.
357 * Returns 0 on success, -1 on failure.
358 */
359static int
360rcs_movefile(char *from, char *to, mode_t perm, u_int to_flags)
361{
362 FILE *src, *dst;
363 size_t nread, nwritten;
364 char *buf;
365
366 if (rename(from, to) == 0) {
367 if (chmod(to, perm) == -1) {
368 warn("%s", to);
369 return (-1);
370 }
371 return (0);
372 } else if (errno(*__errno()) != EXDEV18) {
373 warn("failed to access temp RCS output file");
374 return (-1);
375 }
376
377 if ((chmod(to, S_IWUSR0000200) == -1) && !(to_flags & RCS_CREATE(1<<2))) {
378 warnx("chmod(%s, 0%o) failed", to, S_IWUSR0000200);
379 return (-1);
380 }
381
382 /* different filesystem, have to copy the file */
383 if ((src = fopen(from, "r")) == NULL((void *)0)) {
384 warn("%s", from);
385 return (-1);
386 }
387 if ((dst = fopen(to, "w")) == NULL((void *)0)) {
388 warn("%s", to);
389 (void)fclose(src);
390 return (-1);
391 }
392 if (fchmod(fileno(dst)(!__isthreaded ? ((dst)->_file) : (fileno)(dst)), perm)) {
393 warn("%s", to);
394 (void)unlink(to);
395 (void)fclose(src);
396 (void)fclose(dst);
397 return (-1);
398 }
399
400 buf = xmalloc(_MAXBSIZE(64 * 1024));
401 while ((nread = fread(buf, sizeof(char), _MAXBSIZE(64 * 1024), src)) != 0) {
402 if (ferror(src)(!__isthreaded ? (((src)->_flags & 0x0040) != 0) : (ferror
)(src))
) {
403 warnx("failed to read `%s'", from);
404 (void)unlink(to);
405 goto out;
406 }
407 nwritten = fwrite(buf, sizeof(char), nread, dst);
408 if (nwritten != nread) {
409 warnx("failed to write `%s'", to);
410 (void)unlink(to);
411 goto out;
412 }
413 }
414
415 (void)unlink(from);
416
417out:
418 (void)fclose(src);
419 (void)fclose(dst);
420 free(buf);
421
422 return (0);
423}
424
425/*
426 * rcs_head_set()
427 *
428 * Set the revision number of the head revision for the RCS file <file> to
429 * <rev>, which must reference a valid revision within the file.
430 */
431int
432rcs_head_set(RCSFILE *file, RCSNUM *rev)
433{
434 if (rcs_findrev(file, rev) == NULL((void *)0))
435 return (-1);
436
437 if (file->rf_head == NULL((void *)0))
438 file->rf_head = rcsnum_alloc();
439
440 rcsnum_cpy(rev, file->rf_head, 0);
441 file->rf_flags &= ~RCS_SYNCED(1<<5);
442 return (0);
443}
444
445
446/*
447 * rcs_branch_get()
448 *
449 * Retrieve the default branch number for the RCS file <file>.
450 * Returns the number on success. If NULL is returned, then there is no
451 * default branch for this file.
452 */
453const RCSNUM *
454rcs_branch_get(RCSFILE *file)
455{
456 return (file->rf_branch);
457}
458
459/*
460 * rcs_access_add()
461 *
462 * Add the login name <login> to the access list for the RCS file <file>.
463 * Returns 0 on success, or -1 on failure.
464 */
465int
466rcs_access_add(RCSFILE *file, const char *login)
467{
468 struct rcs_access *ap;
469
470 /* first look for duplication */
471 TAILQ_FOREACH(ap, &(file->rf_access), ra_list)for((ap) = ((&(file->rf_access))->tqh_first); (ap) !=
((void *)0); (ap) = ((ap)->ra_list.tqe_next))
{
472 if (strcmp(ap->ra_name, login) == 0) {
473 rcs_errno = RCS_ERR_DUPENT2;
474 return (-1);
475 }
476 }
477
478 ap = xmalloc(sizeof(*ap));
479 ap->ra_name = xstrdup(login);
480 TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list)do { (ap)->ra_list.tqe_next = ((void *)0); (ap)->ra_list
.tqe_prev = (&(file->rf_access))->tqh_last; *(&
(file->rf_access))->tqh_last = (ap); (&(file->rf_access
))->tqh_last = &(ap)->ra_list.tqe_next; } while (0)
;
481
482 /* not synced anymore */
483 file->rf_flags &= ~RCS_SYNCED(1<<5);
484 return (0);
485}
486
487/*
488 * rcs_access_remove()
489 *
490 * Remove an entry with login name <login> from the access list of the RCS
491 * file <file>.
492 * Returns 0 on success, or -1 on failure.
493 */
494int
495rcs_access_remove(RCSFILE *file, const char *login)
496{
497 struct rcs_access *ap;
498
499 TAILQ_FOREACH(ap, &(file->rf_access), ra_list)for((ap) = ((&(file->rf_access))->tqh_first); (ap) !=
((void *)0); (ap) = ((ap)->ra_list.tqe_next))
500 if (strcmp(ap->ra_name, login) == 0)
501 break;
502
503 if (ap == NULL((void *)0)) {
504 rcs_errno = RCS_ERR_NOENT1;
505 return (-1);
506 }
507
508 TAILQ_REMOVE(&(file->rf_access), ap, ra_list)do { if (((ap)->ra_list.tqe_next) != ((void *)0)) (ap)->
ra_list.tqe_next->ra_list.tqe_prev = (ap)->ra_list.tqe_prev
; else (&(file->rf_access))->tqh_last = (ap)->ra_list
.tqe_prev; *(ap)->ra_list.tqe_prev = (ap)->ra_list.tqe_next
; ; ; } while (0)
;
509 free(ap->ra_name);
510 free(ap);
511
512 /* not synced anymore */
513 file->rf_flags &= ~RCS_SYNCED(1<<5);
514 return (0);
515}
516
517/*
518 * rcs_sym_add()
519 *
520 * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
521 * is named <sym> and is bound to the RCS revision <snum>.
522 * Returns 0 on success, or -1 on failure.
523 */
524int
525rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
526{
527 struct rcs_sym *symp;
528
529 if (!rcs_sym_check(sym)) {
530 rcs_errno = RCS_ERR_BADSYM4;
531 return (-1);
532 }
533
534 /* first look for duplication */
535 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list)for((symp) = ((&(rfp->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
{
536 if (strcmp(symp->rs_name, sym) == 0) {
537 rcs_errno = RCS_ERR_DUPENT2;
538 return (-1);
539 }
540 }
541
542 symp = xmalloc(sizeof(*symp));
543 symp->rs_name = xstrdup(sym);
544 symp->rs_num = rcsnum_alloc();
545 rcsnum_cpy(snum, symp->rs_num, 0);
546
547 TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list)do { if (((symp)->rs_list.tqe_next = (&(rfp->rf_symbols
))->tqh_first) != ((void *)0)) (&(rfp->rf_symbols))
->tqh_first->rs_list.tqe_prev = &(symp)->rs_list
.tqe_next; else (&(rfp->rf_symbols))->tqh_last = &
(symp)->rs_list.tqe_next; (&(rfp->rf_symbols))->
tqh_first = (symp); (symp)->rs_list.tqe_prev = &(&
(rfp->rf_symbols))->tqh_first; } while (0)
;
548
549 /* not synced anymore */
550 rfp->rf_flags &= ~RCS_SYNCED(1<<5);
551 return (0);
552}
553
554/*
555 * rcs_sym_remove()
556 *
557 * Remove the symbol with name <sym> from the symbol list for the RCS file
558 * <file>. If no such symbol is found, the call fails and returns with an
559 * error.
560 * Returns 0 on success, or -1 on failure.
561 */
562int
563rcs_sym_remove(RCSFILE *file, const char *sym)
564{
565 struct rcs_sym *symp;
566
567 if (!rcs_sym_check(sym)) {
568 rcs_errno = RCS_ERR_BADSYM4;
569 return (-1);
570 }
571
572 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)for((symp) = ((&(file->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
573 if (strcmp(symp->rs_name, sym) == 0)
574 break;
575
576 if (symp == NULL((void *)0)) {
577 rcs_errno = RCS_ERR_NOENT1;
578 return (-1);
579 }
580
581 TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list)do { if (((symp)->rs_list.tqe_next) != ((void *)0)) (symp)
->rs_list.tqe_next->rs_list.tqe_prev = (symp)->rs_list
.tqe_prev; else (&(file->rf_symbols))->tqh_last = (
symp)->rs_list.tqe_prev; *(symp)->rs_list.tqe_prev = (symp
)->rs_list.tqe_next; ; ; } while (0)
;
582 free(symp->rs_name);
583 rcsnum_free(symp->rs_num);
584 free(symp);
585
586 /* not synced anymore */
587 file->rf_flags &= ~RCS_SYNCED(1<<5);
588 return (0);
589}
590
591/*
592 * rcs_sym_getrev()
593 *
594 * Retrieve the RCS revision number associated with the symbol <sym> for the
595 * RCS file <file>. The returned value is a dynamically-allocated copy and
596 * should be freed by the caller once they are done with it.
597 * Returns the RCSNUM on success, or NULL on failure.
598 */
599RCSNUM *
600rcs_sym_getrev(RCSFILE *file, const char *sym)
601{
602 RCSNUM *num;
603 struct rcs_sym *symp;
604
605 if (!rcs_sym_check(sym)) {
606 rcs_errno = RCS_ERR_BADSYM4;
607 return (NULL((void *)0));
608 }
609
610 num = NULL((void *)0);
611 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)for((symp) = ((&(file->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
612 if (strcmp(symp->rs_name, sym) == 0)
613 break;
614
615 if (symp == NULL((void *)0)) {
616 rcs_errno = RCS_ERR_NOENT1;
617 } else {
618 num = rcsnum_alloc();
619 rcsnum_cpy(symp->rs_num, num, 0);
620 }
621
622 return (num);
623}
624
625/*
626 * rcs_sym_check()
627 *
628 * Check the RCS symbol name <sym> for any unsupported characters.
629 * Returns 1 if the tag is correct, 0 if it isn't valid.
630 */
631int
632rcs_sym_check(const char *sym)
633{
634 int ret;
635 const unsigned char *cp;
636
637 ret = 1;
638 cp = sym;
639 if (!isalpha(*cp++))
640 return (0);
641
642 for (; *cp != '\0'; cp++)
643 if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL((void *)0))) {
644 ret = 0;
645 break;
646 }
647
648 return (ret);
649}
650
651/*
652 * rcs_lock_getmode()
653 *
654 * Retrieve the locking mode of the RCS file <file>.
655 */
656int
657rcs_lock_getmode(RCSFILE *file)
658{
659 return (file->rf_flags & RCS_SLOCK(1<<6)) ? RCS_LOCK_STRICT1 : RCS_LOCK_LOOSE0;
660}
661
662/*
663 * rcs_lock_setmode()
664 *
665 * Set the locking mode of the RCS file <file> to <mode>, which must either
666 * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
667 * Returns the previous mode on success, or -1 on failure.
668 */
669int
670rcs_lock_setmode(RCSFILE *file, int mode)
671{
672 int pmode;
673 pmode = rcs_lock_getmode(file);
674
675 if (mode == RCS_LOCK_STRICT1)
676 file->rf_flags |= RCS_SLOCK(1<<6);
677 else if (mode == RCS_LOCK_LOOSE0)
678 file->rf_flags &= ~RCS_SLOCK(1<<6);
679 else
680 errx(1, "rcs_lock_setmode: invalid mode `%d'", mode);
681
682 file->rf_flags &= ~RCS_SYNCED(1<<5);
683 return (pmode);
684}
685
686/*
687 * rcs_lock_add()
688 *
689 * Add an RCS lock for the user <user> on revision <rev>.
690 * Returns 0 on success, or -1 on failure.
691 */
692int
693rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
694{
695 struct rcs_lock *lkp;
696 struct rcs_delta *rdp;
697
698 if ((rdp = rcs_findrev(file, rev)) == NULL((void *)0)) {
699 rcs_errno = RCS_ERR_NOENT1;
700 return (-1);
701 }
702
703 /* first look for duplication */
704 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)for((lkp) = ((&(file->rf_locks))->tqh_first); (lkp)
!= ((void *)0); (lkp) = ((lkp)->rl_list.tqe_next))
{
705 if (strcmp(lkp->rl_name, user) == 0 &&
706 rcsnum_cmp(rev, lkp->rl_num, 0) == 0) {
707 rcs_errno = RCS_ERR_DUPENT2;
708 return (-1);
709 }
710 }
711
712 lkp = xmalloc(sizeof(*lkp));
713 lkp->rl_name = xstrdup(user);
714 lkp->rl_num = rcsnum_alloc();
715 rcsnum_cpy(rev, lkp->rl_num, 0);
716
717 free(rdp->rd_locker);
718 rdp->rd_locker = xstrdup(user);
719
720 TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list)do { (lkp)->rl_list.tqe_next = ((void *)0); (lkp)->rl_list
.tqe_prev = (&(file->rf_locks))->tqh_last; *(&(
file->rf_locks))->tqh_last = (lkp); (&(file->rf_locks
))->tqh_last = &(lkp)->rl_list.tqe_next; } while (0
)
;
721
722 /* not synced anymore */
723 file->rf_flags &= ~RCS_SYNCED(1<<5);
724 return (0);
725}
726
727
728/*
729 * rcs_lock_remove()
730 *
731 * Remove the RCS lock on revision <rev>.
732 * Returns 0 on success, or -1 on failure.
733 */
734int
735rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
736{
737 struct rcs_lock *lkp;
738 struct rcs_delta *rdp;
739
740 if ((rdp = rcs_findrev(file, rev)) == NULL((void *)0)) {
741 rcs_errno = RCS_ERR_NOENT1;
742 return (-1);
743 }
744
745 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)for((lkp) = ((&(file->rf_locks))->tqh_first); (lkp)
!= ((void *)0); (lkp) = ((lkp)->rl_list.tqe_next))
{
746 if (strcmp(lkp->rl_name, user) == 0 &&
747 rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
748 break;
749 }
750
751 if (lkp == NULL((void *)0)) {
752 rcs_errno = RCS_ERR_NOENT1;
753 return (-1);
754 }
755
756 TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list)do { if (((lkp)->rl_list.tqe_next) != ((void *)0)) (lkp)->
rl_list.tqe_next->rl_list.tqe_prev = (lkp)->rl_list.tqe_prev
; else (&(file->rf_locks))->tqh_last = (lkp)->rl_list
.tqe_prev; *(lkp)->rl_list.tqe_prev = (lkp)->rl_list.tqe_next
; ; ; } while (0)
;
757 rcsnum_free(lkp->rl_num);
758 free(lkp->rl_name);
759 free(lkp);
760
761 free(rdp->rd_locker);
762 rdp->rd_locker = NULL((void *)0);
763
764 /* not synced anymore */
765 file->rf_flags &= ~RCS_SYNCED(1<<5);
766 return (0);
767}
768
769/*
770 * rcs_desc_set()
771 *
772 * Set the description for the RCS file <file>.
773 */
774void
775rcs_desc_set(RCSFILE *file, const char *desc)
776{
777 char *tmp;
778
779 tmp = xstrdup(desc);
780 free(file->rf_desc);
781 file->rf_desc = tmp;
782 file->rf_flags &= ~RCS_SYNCED(1<<5);
783}
784
785/*
786 * rcs_comment_set()
787 *
788 * Set the comment leader for the RCS file <file>.
789 */
790void
791rcs_comment_set(RCSFILE *file, const char *comment)
792{
793 char *tmp;
794
795 tmp = xstrdup(comment);
796 free(file->rf_comment);
797 file->rf_comment = tmp;
798 file->rf_flags &= ~RCS_SYNCED(1<<5);
799}
800
801int
802rcs_patch_lines(struct rcs_lines *dlines, struct rcs_lines *plines)
803{
804 char op, *ep;
805 struct rcs_line *lp, *dlp, *ndlp;
806 int i, lineno, nbln;
807 u_char tmp;
808
809 dlp = TAILQ_FIRST(&(dlines->l_lines))((&(dlines->l_lines))->tqh_first);
810 lp = TAILQ_FIRST(&(plines->l_lines))((&(plines->l_lines))->tqh_first);
811
812 /* skip first bogus line */
813 for (lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next); lp != NULL((void *)0);
1
Assuming 'lp' is not equal to NULL
2
Loop condition is true. Entering loop body
814 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next)) {
815 if (lp->l_len < 2)
3
Assuming field 'l_len' is >= 2
4
Taking false branch
816 errx(1, "line too short, RCS patch seems broken");
817 op = *(lp->l_line);
818 /* NUL-terminate line buffer for strtol() safety. */
819 tmp = lp->l_line[lp->l_len - 1];
820 lp->l_line[lp->l_len - 1] = '\0';
821 lineno = (int)strtol((lp->l_line + 1), &ep, 10);
822 if (lineno > dlines->l_nblines || lineno < 0 ||
5
Assuming 'lineno' is <= field 'l_nblines'
6
Assuming 'lineno' is >= 0
8
Taking false branch
823 *ep != ' ')
7
Assuming the condition is false
824 errx(1, "invalid line specification in RCS patch");
825 ep++;
826 nbln = (int)strtol(ep, &ep, 10);
827 /* Restore the last byte of the buffer */
828 lp->l_line[lp->l_len - 1] = tmp;
829 if (nbln < 0)
9
Assuming 'nbln' is >= 0
10
Taking false branch
830 errx(1,
831 "invalid line number specification in RCS patch");
832
833 /* find the appropriate line */
834 for (;;) {
11
Loop condition is true. Entering loop body
835 if (dlp == NULL((void *)0))
12
Assuming 'dlp' is not equal to NULL
13
Taking false branch
836 break;
837 if (dlp->l_lineno == lineno)
14
Assuming 'lineno' is equal to field 'l_lineno'
15
Taking true branch
838 break;
16
Execution continues on line 848
839 if (dlp->l_lineno > lineno) {
840 dlp = TAILQ_PREV(dlp, tqh, l_list)(*(((struct tqh *)((dlp)->l_list.tqe_prev))->tqh_last));
841 } else if (dlp->l_lineno < lineno) {
842 if (((ndlp = TAILQ_NEXT(dlp, l_list)((dlp)->l_list.tqe_next)) == NULL((void *)0)) ||
843 ndlp->l_lineno > lineno)
844 break;
845 dlp = ndlp;
846 }
847 }
848 if (dlp
16.1
'dlp' is not equal to NULL
== NULL((void *)0))
17
Taking false branch
849 errx(1, "can't find referenced line in RCS patch");
850
851 if (op == 'd') {
18
Assuming the condition is true
19
Taking true branch
852 for (i = 0; (i < nbln) && (dlp
20.1
'dlp' is not equal to NULL
!= NULL((void *)0)); i++) {
20
Assuming 'i' is < 'nbln'
21
Loop condition is true. Entering loop body
27
Assuming 'i' is >= 'nbln'
853 ndlp = TAILQ_NEXT(dlp, l_list)((dlp)->l_list.tqe_next);
854 TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list)do { if (((dlp)->l_list.tqe_next) != ((void *)0)) (dlp)->
l_list.tqe_next->l_list.tqe_prev = (dlp)->l_list.tqe_prev
; else (&(dlines->l_lines))->tqh_last = (dlp)->l_list
.tqe_prev; *(dlp)->l_list.tqe_prev = (dlp)->l_list.tqe_next
; ; ; } while (0)
;
22
Assuming field 'tqe_next' is not equal to null
23
Taking true branch
24
Loop condition is false. Exiting loop
855 free(dlp);
25
Memory is released
856 dlp = ndlp;
857 /* last line is gone - reset dlp */
858 if (dlp
25.1
'dlp' is not equal to NULL
== NULL((void *)0)) {
26
Taking false branch
859 ndlp = TAILQ_LAST(&(dlines->l_lines),(*(((struct tqh *)((&(dlines->l_lines))->tqh_last))
->tqh_last))
860 tqh)(*(((struct tqh *)((&(dlines->l_lines))->tqh_last))
->tqh_last))
;
861 dlp = ndlp;
862 }
863 }
864 } else if (op == 'a') {
865 for (i = 0; i < nbln; i++) {
866 ndlp = lp;
867 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next);
868 if (lp == NULL((void *)0))
869 errx(1, "truncated RCS patch");
870 TAILQ_REMOVE(&(plines->l_lines), lp, l_list)do { if (((lp)->l_list.tqe_next) != ((void *)0)) (lp)->
l_list.tqe_next->l_list.tqe_prev = (lp)->l_list.tqe_prev
; else (&(plines->l_lines))->tqh_last = (lp)->l_list
.tqe_prev; *(lp)->l_list.tqe_prev = (lp)->l_list.tqe_next
; ; ; } while (0)
;
871 TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,do { if (((lp)->l_list.tqe_next = (dlp)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(dlines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (dlp)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(dlp)->l_list
.tqe_next; } while (0)
872 lp, l_list)do { if (((lp)->l_list.tqe_next = (dlp)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(dlines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (dlp)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(dlp)->l_list
.tqe_next; } while (0)
;
873 dlp = lp;
874
875 /* we don't want lookup to block on those */
876 lp->l_lineno = lineno;
877
878 lp = ndlp;
879 }
880 } else
881 errx(1, "unknown RCS patch operation `%c'", op);
882
883 /* last line of the patch, done */
884 if (lp->l_lineno == plines->l_nblines)
28
Assuming field 'l_lineno' is equal to field 'l_nblines'
29
Taking true branch
885 break;
30
Execution continues on line 889
886 }
887
888 /* once we're done patching, rebuild the line numbers */
889 lineno = 0;
890 TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)for((lp) = ((&(dlines->l_lines))->tqh_first); (lp) !=
((void *)0); (lp) = ((lp)->l_list.tqe_next))
31
Loop condition is true. Entering loop body
891 lp->l_lineno = lineno++;
32
Use of memory after it is freed
892 dlines->l_nblines = lineno - 1;
893
894 return (0);
895}
896
897/*
898 * rcs_getrev()
899 *
900 * Get the whole contents of revision <rev> from the RCSFILE <rfp>. The
901 * returned buffer is dynamically allocated and should be released using
902 * buf_free() once the caller is done using it.
903 */
904BUF *
905rcs_getrev(RCSFILE *rfp, RCSNUM *frev)
906{
907 u_int i, numlen;
908 int isbranch, lookonbranch, found;
909 size_t dlen, plen, len;
910 RCSNUM *crev, *rev, *brev;
911 BUF *rbuf;
912 struct rcs_delta *rdp = NULL((void *)0);
913 struct rcs_branch *rb;
914 u_char *data, *patch;
915
916 if (rfp->rf_head == NULL((void *)0))
917 return (NULL((void *)0));
918
919 if (frev == RCS_HEAD_REV((RCSNUM *)(-1)))
920 rev = rfp->rf_head;
921 else
922 rev = frev;
923
924 /* XXX rcsnum_cmp() */
925 for (i = 0; i < rfp->rf_head->rn_len; i++) {
926 if (rfp->rf_head->rn_id[i] < rev->rn_id[i]) {
927 rcs_errno = RCS_ERR_NOENT1;
928 return (NULL((void *)0));
929 }
930 }
931
932 /* No matter what, we'll need everything parsed up until the description
933 so go for it. */
934 if (rcsparse_deltas(rfp, NULL((void *)0)))
935 return (NULL((void *)0));
936
937 rdp = rcs_findrev(rfp, rfp->rf_head);
938 if (rdp == NULL((void *)0)) {
939 warnx("failed to get RCS HEAD revision");
940 return (NULL((void *)0));
941 }
942
943 if (rdp->rd_tlen == 0)
944 if (rcsparse_deltatexts(rfp, rfp->rf_head))
945 return (NULL((void *)0));
946
947 len = rdp->rd_tlen;
948 if (len == 0) {
949 rbuf = buf_alloc(1);
950 buf_empty(rbuf);
951 return (rbuf);
952 }
953
954 rbuf = buf_alloc(len);
955 buf_append(rbuf, rdp->rd_text, len);
956
957 isbranch = 0;
958 brev = NULL((void *)0);
959
960 /*
961 * If a branch was passed, get the latest revision on it.
962 */
963 if (RCSNUM_ISBRANCH(rev)((rev)->rn_len % 2)) {
964 brev = rev;
965 rdp = rcs_findrev(rfp, rev);
966 if (rdp == NULL((void *)0)) {
967 buf_free(rbuf);
968 return (NULL((void *)0));
969 }
970
971 rev = rdp->rd_num;
972 } else {
973 if (RCSNUM_ISBRANCHREV(rev)(!((rev)->rn_len % 2) && ((rev)->rn_len >= 4
))
) {
974 brev = rcsnum_revtobr(rev);
975 isbranch = 1;
976 }
977 }
978
979 lookonbranch = 0;
980 crev = NULL((void *)0);
981
982 /* Apply patches backwards to get the right version.
983 */
984 do {
985 found = 0;
986
987 if (rcsnum_cmp(rfp->rf_head, rev, 0) == 0)
988 break;
989
990 if (isbranch == 1 && rdp->rd_num->rn_len < rev->rn_len &&
991 !TAILQ_EMPTY(&(rdp->rd_branches))(((&(rdp->rd_branches))->tqh_first) == ((void *)0)))
992 lookonbranch = 1;
993
994 if (isbranch && lookonbranch == 1) {
995 lookonbranch = 0;
996 TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list)for((rb) = ((&(rdp->rd_branches))->tqh_first); (rb)
!= ((void *)0); (rb) = ((rb)->rb_list.tqe_next))
{
997 /* XXX rcsnum_cmp() is totally broken for
998 * this purpose.
999 */
1000 numlen = MINIMUM(brev->rn_len,(((brev->rn_len) < (rb->rb_num->rn_len - 1)) ? (brev
->rn_len) : (rb->rb_num->rn_len - 1))
1001 rb->rb_num->rn_len - 1)(((brev->rn_len) < (rb->rb_num->rn_len - 1)) ? (brev
->rn_len) : (rb->rb_num->rn_len - 1))
;
1002 for (i = 0; i < numlen; i++) {
1003 if (rb->rb_num->rn_id[i] !=
1004 brev->rn_id[i])
1005 break;
1006 }
1007
1008 if (i == numlen) {
1009 crev = rb->rb_num;
1010 found = 1;
1011 break;
1012 }
1013 }
1014 if (found == 0)
1015 crev = rdp->rd_next;
1016 } else {
1017 crev = rdp->rd_next;
1018 }
1019
1020 rdp = rcs_findrev(rfp, crev);
1021 if (rdp == NULL((void *)0)) {
1022 buf_free(rbuf);
1023 return (NULL((void *)0));
1024 }
1025
1026 plen = rdp->rd_tlen;
1027 dlen = buf_len(rbuf);
1028 patch = rdp->rd_text;
1029 data = buf_release(rbuf);
1030 /* check if we have parsed this rev's deltatext */
1031 if (rdp->rd_tlen == 0)
1032 if (rcsparse_deltatexts(rfp, rdp->rd_num))
1033 return (NULL((void *)0));
1034
1035 rbuf = rcs_patchfile(data, dlen, patch, plen, rcs_patch_lines);
1036 free(data);
1037
1038 if (rbuf == NULL((void *)0))
1039 break;
1040 } while (rcsnum_cmp(crev, rev, 0) != 0);
1041
1042 return (rbuf);
1043}
1044
1045void
1046rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
1047{
1048 struct rcs_lines *plines;
1049 struct rcs_line *lp;
1050 int added, i, nbln, removed;
1051 char op, *ep;
1052 u_char tmp;
1053
1054 added = removed = 0;
1055
1056 plines = rcs_splitlines(rdp->rd_text, rdp->rd_tlen);
1057 lp = TAILQ_FIRST(&(plines->l_lines))((&(plines->l_lines))->tqh_first);
1058
1059 /* skip first bogus line */
1060 for (lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next); lp != NULL((void *)0);
1061 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next)) {
1062 if (lp->l_len < 2)
1063 errx(1,
1064 "line too short, RCS patch seems broken");
1065 op = *(lp->l_line);
1066 /* NUL-terminate line buffer for strtol() safety. */
1067 tmp = lp->l_line[lp->l_len - 1];
1068 lp->l_line[lp->l_len - 1] = '\0';
1069 (void)strtol((lp->l_line + 1), &ep, 10);
1070 ep++;
1071 nbln = (int)strtol(ep, &ep, 10);
1072 /* Restore the last byte of the buffer */
1073 lp->l_line[lp->l_len - 1] = tmp;
1074 if (nbln < 0)
1075 errx(1, "invalid line number specification "
1076 "in RCS patch");
1077
1078 if (op == 'a') {
1079 added += nbln;
1080 for (i = 0; i < nbln; i++) {
1081 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next);
1082 if (lp == NULL((void *)0))
1083 errx(1, "truncated RCS patch");
1084 }
1085 } else if (op == 'd')
1086 removed += nbln;
1087 else
1088 errx(1, "unknown RCS patch operation '%c'", op);
1089 }
1090
1091 rcs_freelines(plines);
1092
1093 *ladded = added;
1094 *lremoved = removed;
1095}
1096
1097/*
1098 * rcs_rev_add()
1099 *
1100 * Add a revision to the RCS file <rf>. The new revision's number can be
1101 * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
1102 * new revision will have a number equal to the previous head revision plus
1103 * one). The <msg> argument specifies the log message for that revision, and
1104 * <date> specifies the revision's date (a value of -1 is
1105 * equivalent to using the current time).
1106 * If <author> is NULL, set the author for this revision to the current user.
1107 * Returns 0 on success, or -1 on failure.
1108 */
1109int
1110rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1111 const char *author)
1112{
1113 time_t now;
1114 struct passwd *pw;
1115 struct rcs_delta *ordp, *rdp;
1116
1117 if (rev == RCS_HEAD_REV((RCSNUM *)(-1))) {
1118 if (rf->rf_flags & RCS_CREATE(1<<2)) {
1119 if ((rev = rcsnum_parse(RCS_HEAD_INIT"1.1")) == NULL((void *)0))
1120 return (-1);
1121 rf->rf_head = rev;
1122 } else {
1123 rev = rcsnum_inc(rf->rf_head);
1124 }
1125 } else {
1126 if ((rdp = rcs_findrev(rf, rev)) != NULL((void *)0)) {
1127 rcs_errno = RCS_ERR_DUPENT2;
1128 return (-1);
1129 }
1130 }
1131
1132 rdp = xcalloc(1, sizeof(*rdp));
1133
1134 TAILQ_INIT(&(rdp->rd_branches))do { (&(rdp->rd_branches))->tqh_first = ((void *)0)
; (&(rdp->rd_branches))->tqh_last = &(&(rdp
->rd_branches))->tqh_first; } while (0)
;
1135
1136 rdp->rd_num = rcsnum_alloc();
1137 rcsnum_cpy(rev, rdp->rd_num, 0);
1138
1139 rdp->rd_next = rcsnum_alloc();
1140
1141 if (!(rf->rf_flags & RCS_CREATE(1<<2))) {
1142 /* next should point to the previous HEAD */
1143 ordp = TAILQ_FIRST(&(rf->rf_delta))((&(rf->rf_delta))->tqh_first);
1144 rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1145 }
1146
1147 if (!author && !(author = getlogin())) {
1148 if (!(pw = getpwuid(getuid())))
1149 errx(1, "getpwuid failed");
1150 author = pw->pw_name;
1151 }
1152 rdp->rd_author = xstrdup(author);
1153 rdp->rd_state = xstrdup(RCS_STATE_EXP"Exp");
1154 rdp->rd_log = xstrdup(msg);
1155
1156 if (date != (time_t)(-1))
1157 now = date;
1158 else
1159 time(&now);
1160 gmtime_r(&now, &(rdp->rd_date));
1161
1162 TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list)do { if (((rdp)->rd_list.tqe_next = (&(rf->rf_delta
))->tqh_first) != ((void *)0)) (&(rf->rf_delta))->
tqh_first->rd_list.tqe_prev = &(rdp)->rd_list.tqe_next
; else (&(rf->rf_delta))->tqh_last = &(rdp)->
rd_list.tqe_next; (&(rf->rf_delta))->tqh_first = (rdp
); (rdp)->rd_list.tqe_prev = &(&(rf->rf_delta))
->tqh_first; } while (0)
;
1163 rf->rf_ndelta++;
1164
1165 /* not synced anymore */
1166 rf->rf_flags &= ~RCS_SYNCED(1<<5);
1167
1168 return (0);
1169}
1170
1171/*
1172 * rcs_rev_remove()
1173 *
1174 * Remove the revision whose number is <rev> from the RCS file <rf>.
1175 */
1176int
1177rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1178{
1179 char *path_tmp1, *path_tmp2;
1180 struct rcs_delta *rdp, *prevrdp, *nextrdp;
1181 BUF *newdeltatext, *nextbuf, *prevbuf, *newdiff;
1182
1183 nextrdp = prevrdp = NULL((void *)0);
1184 path_tmp1 = path_tmp2 = NULL((void *)0);
1185
1186 if (rev == RCS_HEAD_REV((RCSNUM *)(-1)))
1187 rev = rf->rf_head;
1188
1189 /* do we actually have that revision? */
1190 if ((rdp = rcs_findrev(rf, rev)) == NULL((void *)0)) {
1191 rcs_errno = RCS_ERR_NOENT1;
1192 return (-1);
1193 }
1194
1195 /*
1196 * This is confusing, the previous delta is next in the TAILQ list.
1197 * the next delta is the previous one in the TAILQ list.
1198 *
1199 * When the HEAD revision got specified, nextrdp will be NULL.
1200 * When the first revision got specified, prevrdp will be NULL.
1201 */
1202 prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list)((rdp)->rd_list.tqe_next);
1203 nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, tqh, rd_list)(*(((struct tqh *)((rdp)->rd_list.tqe_prev))->tqh_last)
)
;
1204
1205 newdeltatext = prevbuf = nextbuf = NULL((void *)0);
1206
1207 if (prevrdp != NULL((void *)0)) {
1208 if ((prevbuf = rcs_getrev(rf, prevrdp->rd_num)) == NULL((void *)0))
1209 errx(1, "error getting revision");
1210 }
1211
1212 if (prevrdp != NULL((void *)0) && nextrdp != NULL((void *)0)) {
1213 if ((nextbuf = rcs_getrev(rf, nextrdp->rd_num)) == NULL((void *)0))
1214 errx(1, "error getting revision");
1215
1216 newdiff = buf_alloc(64);
1217
1218 /* calculate new diff */
1219 (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
1220 buf_write_stmp(nextbuf, path_tmp1);
1221 buf_free(nextbuf);
1222
1223 (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
1224 buf_write_stmp(prevbuf, path_tmp2);
1225 buf_free(prevbuf);
1226
1227 diff_format = D_RCSDIFF5;
1228 if (diffreg(path_tmp1, path_tmp2, newdiff, D_FORCEASCII0x01) == D_ERROR2)
1229 errx(1, "diffreg failed");
1230
1231 newdeltatext = newdiff;
1232 } else if (nextrdp == NULL((void *)0) && prevrdp != NULL((void *)0)) {
1233 newdeltatext = prevbuf;
1234 }
1235
1236 if (newdeltatext != NULL((void *)0)) {
1237 if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1238 errx(1, "error setting new deltatext");
1239 }
1240
1241 TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list)do { if (((rdp)->rd_list.tqe_next) != ((void *)0)) (rdp)->
rd_list.tqe_next->rd_list.tqe_prev = (rdp)->rd_list.tqe_prev
; else (&(rf->rf_delta))->tqh_last = (rdp)->rd_list
.tqe_prev; *(rdp)->rd_list.tqe_prev = (rdp)->rd_list.tqe_next
; ; ; } while (0)
;
1242
1243 /* update pointers */
1244 if (prevrdp != NULL((void *)0) && nextrdp != NULL((void *)0)) {
1245 rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1246 } else if (prevrdp != NULL((void *)0)) {
1247 if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1248 errx(1, "rcs_head_set failed");
1249 } else if (nextrdp != NULL((void *)0)) {
1250 rcsnum_free(nextrdp->rd_next);
1251 nextrdp->rd_next = rcsnum_alloc();
1252 } else {
1253 rcsnum_free(rf->rf_head);
1254 rf->rf_head = NULL((void *)0);
1255 }
1256
1257 rf->rf_ndelta--;
1258 rf->rf_flags &= ~RCS_SYNCED(1<<5);
1259
1260 rcs_freedelta(rdp);
1261
1262 free(path_tmp1);
1263 free(path_tmp2);
1264
1265 return (0);
1266}
1267
1268/*
1269 * rcs_findrev()
1270 *
1271 * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1272 * The revision number is given in <rev>.
1273 *
1274 * If the given revision is a branch number, we translate it into the latest
1275 * revision on the branch.
1276 *
1277 * Returns a pointer to the delta on success, or NULL on failure.
1278 */
1279struct rcs_delta *
1280rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1281{
1282 u_int cmplen;
1283 struct rcs_delta *rdp;
1284 RCSNUM *brev, *frev;
1285
1286 /*
1287 * We need to do more parsing if the last revision in the linked list
1288 * is greater than the requested revision.
1289 */
1290 rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist)(*(((struct rcs_dlist *)((&(rfp->rf_delta))->tqh_last
))->tqh_last))
;
1291 if (rdp == NULL((void *)0) ||
1292 rcsnum_cmp(rdp->rd_num, rev, 0) == -1) {
1293 if (rcsparse_deltas(rfp, rev))
1294 return (NULL((void *)0));
1295 }
1296
1297 /*
1298 * Translate a branch into the latest revision on the branch itself.
1299 */
1300 if (RCSNUM_ISBRANCH(rev)((rev)->rn_len % 2)) {
1301 brev = rcsnum_brtorev(rev);
1302 frev = brev;
1303 for (;;) {
1304 rdp = rcs_findrev(rfp, frev);
1305 if (rdp == NULL((void *)0))
1306 return (NULL((void *)0));
1307
1308 if (rdp->rd_next->rn_len == 0)
1309 break;
1310
1311 frev = rdp->rd_next;
1312 }
1313
1314 rcsnum_free(brev);
1315 return (rdp);
1316 }
1317
1318 cmplen = rev->rn_len;
1319
1320 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list)for((rdp) = ((&(rfp->rf_delta))->tqh_first); (rdp) !=
((void *)0); (rdp) = ((rdp)->rd_list.tqe_next))
{
1321 if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0)
1322 return (rdp);
1323 }
1324
1325 return (NULL((void *)0));
1326}
1327
1328/*
1329 * rcs_kwexp_set()
1330 *
1331 * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1332 */
1333void
1334rcs_kwexp_set(RCSFILE *file, int mode)
1335{
1336 int i;
1337 char *tmp, buf[8] = "";
1338
1339 if (RCS_KWEXP_INVAL(mode)((mode & 0x10) || ((mode & 0x08) && (mode &
~0x08)))
)
1340 return;
1341
1342 i = 0;
1343 if (mode == RCS_KWEXP_NONE0x00)
1344 buf[0] = 'b';
1345 else if (mode == RCS_KWEXP_OLD0x08)
1346 buf[0] = 'o';
1347 else {
1348 if (mode & RCS_KWEXP_NAME0x01)
1349 buf[i++] = 'k';
1350 if (mode & RCS_KWEXP_VAL0x02)
1351 buf[i++] = 'v';
1352 if (mode & RCS_KWEXP_LKR0x04)
1353 buf[i++] = 'l';
1354 }
1355
1356 tmp = xstrdup(buf);
1357 free(file->rf_expand);
1358 file->rf_expand = tmp;
1359 /* not synced anymore */
1360 file->rf_flags &= ~RCS_SYNCED(1<<5);
1361}
1362
1363/*
1364 * rcs_kwexp_get()
1365 *
1366 * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1367 */
1368int
1369rcs_kwexp_get(RCSFILE *file)
1370{
1371 if (file->rf_expand == NULL((void *)0))
1372 return (RCS_KWEXP_DEFAULT(0x01 | 0x02));
1373
1374 return (rcs_kflag_get(file->rf_expand));
1375}
1376
1377/*
1378 * rcs_kflag_get()
1379 *
1380 * Get the keyword expansion mode from a set of character flags given in
1381 * <flags> and return the appropriate flag mask. In case of an error, the
1382 * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1383 */
1384int
1385rcs_kflag_get(const char *flags)
1386{
1387 int fl;
1388 size_t len;
1389 const char *fp;
1390
1391 if (flags == NULL((void *)0) || !(len = strlen(flags)))
1392 return (RCS_KWEXP_ERR0x10);
1393
1394 fl = 0;
1395 for (fp = flags; *fp != '\0'; fp++) {
1396 if (*fp == 'k')
1397 fl |= RCS_KWEXP_NAME0x01;
1398 else if (*fp == 'v')
1399 fl |= RCS_KWEXP_VAL0x02;
1400 else if (*fp == 'l')
1401 fl |= RCS_KWEXP_LKR0x04;
1402 else if (*fp == 'o') {
1403 if (len != 1)
1404 fl |= RCS_KWEXP_ERR0x10;
1405 fl |= RCS_KWEXP_OLD0x08;
1406 } else if (*fp == 'b') {
1407 if (len != 1)
1408 fl |= RCS_KWEXP_ERR0x10;
1409 fl |= RCS_KWEXP_NONE0x00;
1410 } else /* unknown letter */
1411 fl |= RCS_KWEXP_ERR0x10;
1412 }
1413
1414 return (fl);
1415}
1416
1417/*
1418 * rcs_freedelta()
1419 *
1420 * Free the contents of a delta structure.
1421 */
1422static void
1423rcs_freedelta(struct rcs_delta *rdp)
1424{
1425 struct rcs_branch *rb;
1426
1427 rcsnum_free(rdp->rd_num);
1428 rcsnum_free(rdp->rd_next);
1429
1430 free(rdp->rd_author);
1431 free(rdp->rd_locker);
1432 free(rdp->rd_state);
1433 free(rdp->rd_log);
1434 free(rdp->rd_text);
1435
1436 while ((rb = TAILQ_FIRST(&(rdp->rd_branches))((&(rdp->rd_branches))->tqh_first)) != NULL((void *)0)) {
1437 TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list)do { if (((rb)->rb_list.tqe_next) != ((void *)0)) (rb)->
rb_list.tqe_next->rb_list.tqe_prev = (rb)->rb_list.tqe_prev
; else (&(rdp->rd_branches))->tqh_last = (rb)->rb_list
.tqe_prev; *(rb)->rb_list.tqe_prev = (rb)->rb_list.tqe_next
; ; ; } while (0)
;
1438 rcsnum_free(rb->rb_num);
1439 free(rb);
1440 }
1441
1442 free(rdp);
1443}
1444
1445/*
1446 * rcs_strprint()
1447 *
1448 * Output an RCS string <str> of size <slen> to the stream <stream>. Any
1449 * '@' characters are escaped. Otherwise, the string can contain arbitrary
1450 * binary data.
1451 */
1452static void
1453rcs_strprint(const u_char *str, size_t slen, FILE *stream)
1454{
1455 const u_char *ap, *ep, *sp;
1456
1457 if (slen == 0)
1458 return;
1459
1460 ep = str + slen - 1;
1461
1462 for (sp = str; sp <= ep;) {
1463 ap = memchr(sp, '@', ep - sp);
1464 if (ap == NULL((void *)0))
1465 ap = ep;
1466 (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1467
1468 if (*ap == '@')
1469 putc('@', stream)(!__isthreaded ? __sputc('@', stream) : (putc)('@', stream));
1470 sp = ap + 1;
1471 }
1472}
1473
1474/*
1475 * rcs_expand_keywords()
1476 *
1477 * Return expansion any RCS keywords in <data>
1478 *
1479 * On error, return NULL.
1480 */
1481static BUF *
1482rcs_expand_keywords(char *rcsfile_in, struct rcs_delta *rdp, BUF *bp, int mode)
1483{
1484 BUF *newbuf;
1485 u_char *c, *kw, *fin;
1486 char buf[256], *tmpf, resolved[PATH_MAX1024], *rcsfile;
1487 u_char *line, *line2;
1488 u_int i, j;
1489 int kwtype;
1490 int found;
1491 struct tm tb;
1492
1493 tb = rdp->rd_date;
1494 if (timezone_flag != NULL((void *)0))
1495 rcs_set_tz(timezone_flag, rdp, &tb);
1496
1497 if (realpath(rcsfile_in, resolved) == NULL((void *)0))
1498 rcsfile = rcsfile_in;
1499 else
1500 rcsfile = resolved;
1501
1502 newbuf = buf_alloc(buf_len(bp));
1503
1504 /*
1505 * Keyword formats:
1506 * $Keyword$
1507 * $Keyword: value$
1508 */
1509 c = buf_get(bp);
1510 fin = c + buf_len(bp);
1511 /* Copying to newbuf is deferred until the first keyword. */
1512 found = 0;
1513
1514 while (c < fin) {
1515 kw = memchr(c, '$', fin - c);
1516 if (kw == NULL((void *)0))
1517 break;
1518 ++kw;
1519 if (found) {
1520 /* Copy everything up to and including the $. */
1521 buf_append(newbuf, c, kw - c);
1522 }
1523 c = kw;
1524 /* c points after the $ now. */
1525 if (c == fin)
1526 break;
1527 if (!isalpha(*c)) /* all valid keywords start with a letter */
1528 continue;
1529
1530 for (i = 0; i < RCS_NKWORDS(sizeof(rcs_expkw)/sizeof(rcs_expkw[0])); ++i) {
1531 size_t kwlen;
1532
1533 kwlen = strlen(rcs_expkw[i].kw_str);
1534 /*
1535 * kwlen must be less than clen since clen includes
1536 * either a terminating `$' or a `:'.
1537 */
1538 if (c + kwlen < fin &&
1539 memcmp(c , rcs_expkw[i].kw_str, kwlen) == 0 &&
1540 (c[kwlen] == '$' || c[kwlen] == ':')) {
1541 c += kwlen;
1542 break;
1543 }
1544 }
1545 if (i == RCS_NKWORDS(sizeof(rcs_expkw)/sizeof(rcs_expkw[0])))
1546 continue;
1547 kwtype = rcs_expkw[i].kw_type;
1548
1549 /*
1550 * If the next character is ':' we need to look for an '$'
1551 * before the end of the line to be sure it is in fact a
1552 * keyword.
1553 */
1554 if (*c == ':') {
1555 for (; c < fin; ++c) {
1556 if (*c == '$' || *c == '\n')
1557 break;
1558 }
1559
1560 if (*c != '$') {
1561 if (found)
1562 buf_append(newbuf, kw, c - kw);
1563 continue;
1564 }
1565 }
1566 ++c;
1567
1568 if (!found) {
1569 found = 1;
1570 /* Copy everything up to and including the $. */
1571 buf_append(newbuf, buf_get(bp), kw - buf_get(bp));
1572 }
1573
1574 if (mode & RCS_KWEXP_NAME0x01) {
1575 buf_puts(newbuf, rcs_expkw[i].kw_str);
1576 if (mode & RCS_KWEXP_VAL0x02)
1577 buf_puts(newbuf, ": ");
1578 }
1579
1580 /* Order matters because of RCS_KW_ID and RCS_KW_HEADER. */
1581 if (mode & RCS_KWEXP_VAL0x02) {
1582 if (kwtype & (RCS_KW_RCSFILE0x0100|RCS_KW_LOG0x4000)) {
1583 if ((kwtype & RCS_KW_FULLPATH0x0010) ||
1584 (tmpf = strrchr(rcsfile, '/')) == NULL((void *)0))
1585 buf_puts(newbuf, rcsfile);
1586 else
1587 buf_puts(newbuf, tmpf + 1);
1588 buf_putc(newbuf, ' ');
1589 }
1590
1591 if (kwtype & RCS_KW_REVISION0x0200) {
1592 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
1593 buf_puts(newbuf, buf);
1594 buf_putc(newbuf, ' ');
1595 }
1596
1597 if (kwtype & RCS_KW_DATE0x2000) {
1598 strftime(buf, sizeof(buf),
1599 "%Y/%m/%d %H:%M:%S ", &tb);
1600 buf_puts(newbuf, buf);
1601 }
1602
1603 if (kwtype & RCS_KW_AUTHOR0x1000) {
1604 buf_puts(newbuf, rdp->rd_author);
1605 buf_putc(newbuf, ' ');
1606 }
1607
1608 if (kwtype & RCS_KW_STATE0x0800) {
1609 buf_puts(newbuf, rdp->rd_state);
1610 buf_putc(newbuf, ' ');
1611 }
1612
1613 /* Order does not matter anymore below. */
1614 if (kwtype & RCS_KW_SOURCE0x0400) {
1615 buf_puts(newbuf, rcsfile);
1616 buf_putc(newbuf, ' ');
1617 }
1618
1619 if (kwtype & RCS_KW_MDOCDATE0x0020) {
1620 strftime(buf, sizeof(buf), "%B", &tb);
1621 buf_puts(newbuf, buf);
1622 /* Only one blank before single-digit day. */
1623 snprintf(buf, sizeof(buf), " %d", tb.tm_mday);
1624 buf_puts(newbuf, buf);
1625 strftime(buf, sizeof(buf), " %Y ", &tb);
1626 buf_puts(newbuf, buf);
1627 }
1628
1629 if (kwtype & RCS_KW_NAME0x8000)
1630 buf_putc(newbuf, ' ');
1631
1632 if ((kwtype & RCS_KW_LOCKER0x10000)) {
1633 if (rdp->rd_locker) {
1634 buf_puts(newbuf, rdp->rd_locker);
1635 buf_putc(newbuf, ' ');
1636 }
1637 }
1638 }
1639
1640 /* End the expansion. */
1641 if (mode & RCS_KWEXP_NAME0x01)
1642 buf_putc(newbuf, '$');
1643
1644 if (kwtype & RCS_KW_LOG0x4000) {
1645 line = memrchr(buf_get(bp), '\n', kw - buf_get(bp) - 1);
1646 if (line == NULL((void *)0))
1647 line = buf_get(bp);
1648 else
1649 ++line;
1650 line2 = kw - 1;
1651 while (line2 > line && line2[-1] == ' ')
1652 --line2;
1653
1654 buf_putc(newbuf, '\n');
1655 buf_append(newbuf, line, kw - 1 - line);
1656 buf_puts(newbuf, "Revision ");
1657 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
1658 buf_puts(newbuf, buf);
1659 buf_puts(newbuf, " ");
1660 strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", &tb);
1661 buf_puts(newbuf, buf);
1662
1663 buf_puts(newbuf, " ");
1664 buf_puts(newbuf, rdp->rd_author);
1665 buf_putc(newbuf, '\n');
1666
1667 for (i = 0; rdp->rd_log[i]; i += j) {
1668 j = strcspn(rdp->rd_log + i, "\n");
1669 if (j == 0)
1670 buf_append(newbuf, line, line2 - line);
1671 else
1672 buf_append(newbuf, line, kw - 1 - line);
1673 if (rdp->rd_log[i + j])
1674 ++j;
1675 buf_append(newbuf, rdp->rd_log + i, j);
1676 }
1677
1678 if (i > 0 && rdp->rd_log[i - 1] != '\n')
1679 buf_putc(newbuf, '\n');
1680
1681 buf_append(newbuf, line, line2 - line);
1682 for (j = 0; c + j < fin; ++j) {
1683 if (c[j] != ' ')
1684 break;
1685 }
1686 if (c + j == fin || c[j] == '\n')
1687 c += j;
1688 }
1689 }
1690
1691 if (found) {
1692 buf_append(newbuf, c, fin - c);
1693 buf_free(bp);
1694 return (newbuf);
1695 } else {
1696 buf_free(newbuf);
1697 return (bp);
1698 }
1699}
1700
1701/*
1702 * rcs_deltatext_set()
1703 *
1704 * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1705 * Returns -1 on error, 0 on success.
1706 */
1707int
1708rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1709{
1710 size_t len;
1711 u_char *dtext;
1712 struct rcs_delta *rdp;
1713
1714 /* Write operations require full parsing */
1715 if (rcsparse_deltatexts(rfp, NULL((void *)0)))
1716 return (-1);
1717
1718 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1719 return (-1);
1720
1721 free(rdp->rd_text);
1722
1723 len = buf_len(bp);
1724 dtext = buf_release(bp);
1725 bp = NULL((void *)0);
1726
1727 if (len != 0) {
1728 rdp->rd_text = xmalloc(len);
1729 rdp->rd_tlen = len;
1730 (void)memcpy(rdp->rd_text, dtext, len);
1731 } else {
1732 rdp->rd_text = NULL((void *)0);
1733 rdp->rd_tlen = 0;
1734 }
1735
1736 free(dtext);
1737
1738 return (0);
1739}
1740
1741/*
1742 * rcs_rev_setlog()
1743 *
1744 * Sets the log message of revision <rev> to <logtext>.
1745 */
1746int
1747rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
1748{
1749 struct rcs_delta *rdp;
1750
1751 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1752 return (-1);
1753
1754 free(rdp->rd_log);
1755
1756 rdp->rd_log = xstrdup(logtext);
1757 rfp->rf_flags &= ~RCS_SYNCED(1<<5);
1758 return (0);
1759}
1760/*
1761 * rcs_rev_getdate()
1762 *
1763 * Get the date corresponding to a given revision.
1764 * Returns the date on success, -1 on failure.
1765 */
1766time_t
1767rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
1768{
1769 struct rcs_delta *rdp;
1770
1771 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1772 return (-1);
1773
1774 return (mktime(&rdp->rd_date));
1775}
1776
1777/*
1778 * rcs_state_set()
1779 *
1780 * Sets the state of revision <rev> to <state>
1781 * NOTE: default state is 'Exp'. States may not contain spaces.
1782 *
1783 * Returns -1 on failure, 0 on success.
1784 */
1785int
1786rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
1787{
1788 struct rcs_delta *rdp;
1789
1790 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1791 return (-1);
1792
1793 free(rdp->rd_state);
1794
1795 rdp->rd_state = xstrdup(state);
1796
1797 rfp->rf_flags &= ~RCS_SYNCED(1<<5);
1798
1799 return (0);
1800}
1801
1802/*
1803 * rcs_state_check()
1804 *
1805 * Check if string <state> is valid.
1806 *
1807 * Returns 0 if the string is valid, -1 otherwise.
1808 */
1809int
1810rcs_state_check(const char *state)
1811{
1812 int ret;
1813 const unsigned char *cp;
1814
1815 ret = 0;
1816 cp = state;
1817 if (!isalpha(*cp++))
1818 return (-1);
1819
1820 for (; *cp != '\0'; cp++)
1821 if (!isgraph(*cp) || (strchr(rcs_state_invch, *cp) != NULL((void *)0))) {
1822 ret = -1;
1823 break;
1824 }
1825
1826 return (ret);
1827}
1828
1829/*
1830 * rcs_kwexp_buf()
1831 *
1832 * Do keyword expansion on a buffer if necessary
1833 *
1834 */
1835BUF *
1836rcs_kwexp_buf(BUF *bp, RCSFILE *rf, RCSNUM *rev)
1837{
1838 struct rcs_delta *rdp;
1839 int expmode;
1840
1841 /*
1842 * Do keyword expansion if required.
1843 */
1844 expmode = rcs_kwexp_get(rf);
1845
1846 if (!(expmode & RCS_KWEXP_NONE0x00)) {
1847 if ((rdp = rcs_findrev(rf, rev)) == NULL((void *)0))
1848 errx(1, "could not fetch revision");
1849 return (rcs_expand_keywords(rf->rf_path, rdp, bp, expmode));
1850 }
1851 return (bp);
1852}