Bug Summary

File:src/usr.bin/cvs/rcs.c
Warning:line 1965, column 8
Access to field 'rd_next' results in a dereference of a null pointer (loaded from variable 'rdp')

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/cvs/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/cvs -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/cvs/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/cvs/rcs.c
1/* $OpenBSD: rcs.c,v 1.320 2020/10/19 19:51:20 naddy 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/stat.h>
28
29#include <ctype.h>
30#include <errno(*__errno()).h>
31#include <libgen.h>
32#include <pwd.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36
37#include "atomicio.h"
38#include "cvs.h"
39#include "diff.h"
40#include "rcs.h"
41#include "rcsparse.h"
42
43#define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
44
45#define RCS_KWEXP_SIZE1024 1024
46
47#define ANNOTATE_NEVER0 0
48#define ANNOTATE_NOW1 1
49#define ANNOTATE_LATER2 2
50
51/* invalid characters in RCS symbol names */
52static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR"$,.:;@";
53
54/* comment leaders, depending on the file's suffix */
55static const struct rcs_comment {
56 const char *rc_suffix;
57 const char *rc_cstr;
58} rcs_comments[] = {
59 { "1", ".\\\" " },
60 { "2", ".\\\" " },
61 { "3", ".\\\" " },
62 { "4", ".\\\" " },
63 { "5", ".\\\" " },
64 { "6", ".\\\" " },
65 { "7", ".\\\" " },
66 { "8", ".\\\" " },
67 { "9", ".\\\" " },
68 { "a", "-- " }, /* Ada */
69 { "ada", "-- " },
70 { "adb", "-- " },
71 { "asm", ";; " }, /* assembler (MS-DOS) */
72 { "ads", "-- " }, /* Ada */
73 { "bat", ":: " }, /* batch (MS-DOS) */
74 { "body", "-- " }, /* Ada */
75 { "c", " * " }, /* C */
76 { "c++", "// " }, /* C++ */
77 { "cc", "// " },
78 { "cpp", "// " },
79 { "cxx", "// " },
80 { "m", "// " }, /* Objective-C */
81 { "cl", ";;; " }, /* Common Lisp */
82 { "cmd", ":: " }, /* command (OS/2) */
83 { "cmf", "c " }, /* CM Fortran */
84 { "csh", "# " }, /* shell */
85 { "e", "# " }, /* efl */
86 { "epsf", "% " }, /* encapsulated postscript */
87 { "epsi", "% " }, /* encapsulated postscript */
88 { "el", "; " }, /* Emacs Lisp */
89 { "f", "c " }, /* Fortran */
90 { "for", "c " },
91 { "h", " * " }, /* C-header */
92 { "hh", "// " }, /* C++ header */
93 { "hpp", "// " },
94 { "hxx", "// " },
95 { "in", "# " }, /* for Makefile.in */
96 { "l", " * " }, /* lex */
97 { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
98 { "mak", "# " }, /* makefile, e.g. Visual C++ */
99 { "me", ".\\\" " }, /* me-macros t/nroff */
100 { "ml", "; " }, /* mocklisp */
101 { "mm", ".\\\" " }, /* mm-macros t/nroff */
102 { "ms", ".\\\" " }, /* ms-macros t/nroff */
103 { "man", ".\\\" " }, /* man-macros t/nroff */
104 { "p", " * " }, /* pascal */
105 { "pas", " * " },
106 { "pl", "# " }, /* Perl (conflict with Prolog) */
107 { "pm", "# " }, /* Perl module */
108 { "ps", "% " }, /* postscript */
109 { "psw", "% " }, /* postscript wrap */
110 { "pswm", "% " }, /* postscript wrap */
111 { "r", "# " }, /* ratfor */
112 { "rc", " * " }, /* Microsoft Windows resource file */
113 { "red", "% " }, /* psl/rlisp */
114 { "sh", "# " }, /* shell */
115 { "sl", "% " }, /* psl */
116 { "spec", "-- " }, /* Ada */
117 { "tex", "% " }, /* tex */
118 { "y", " * " }, /* yacc */
119 { "ye", " * " }, /* yacc-efl */
120 { "yr", " * " }, /* yacc-ratfor */
121};
122
123struct rcs_kw rcs_expkw[] = {
124 { "Author", RCS_KW_AUTHOR0x1000 },
125 { "Date", RCS_KW_DATE0x2000 },
126 { "Header", RCS_KW_HEADER((0x0100 | 0x0200 | 0x2000 | 0x1000 | 0x0800) | 0x0010) },
127 { "Id", RCS_KW_ID(0x0100 | 0x0200 | 0x2000 | 0x1000 | 0x0800) },
128 { "Locker", RCS_KW_LOCKER0x0040 },
129 { "Log", RCS_KW_LOG0x4000 },
130 { "Name", RCS_KW_NAME0x8000 },
131 { "RCSfile", RCS_KW_RCSFILE0x0100 },
132 { "Revision", RCS_KW_REVISION0x0200 },
133 { "Source", RCS_KW_SOURCE0x0400 },
134 { "State", RCS_KW_STATE0x0800 },
135 { "Mdocdate", RCS_KW_MDOCDATE0x0020 },
136};
137
138#define NB_COMTYPES(sizeof(rcs_comments)/sizeof(rcs_comments[0])) (sizeof(rcs_comments)/sizeof(rcs_comments[0]))
139
140static RCSNUM *rcs_get_revision(const char *, RCSFILE *);
141int rcs_patch_lines(struct rcs_lines *, struct rcs_lines *,
142 struct rcs_line **, struct rcs_delta *);
143static void rcs_freedelta(struct rcs_delta *);
144static void rcs_strprint(const u_char *, size_t, FILE *);
145
146static void rcs_kwexp_line(char *, struct rcs_delta *, struct rcs_lines *,
147 struct rcs_line *, int mode);
148
149/*
150 * Prepare RCSFILE for parsing. The given file descriptor (if any) must be
151 * read-only and is closed on rcs_close().
152 */
153RCSFILE *
154rcs_open(const char *path, int fd, int flags, ...)
155{
156 int mode;
157 mode_t fmode;
158 RCSFILE *rfp;
159 va_list vap;
160 struct stat st;
161 struct rcs_delta *rdp;
162 struct rcs_lock *lkr;
163
164 fmode = S_IRUSR0000400|S_IRGRP0000040|S_IROTH0000004;
165 flags &= 0xffff; /* ditch any internal flags */
166
167 if (flags & RCS_CREATE(1<<2)) {
168 va_start(vap, flags)__builtin_va_start(vap, flags);
169 mode = va_arg(vap, int)__builtin_va_arg(vap, int);
170 va_end(vap)__builtin_va_end(vap);
171 fmode = (mode_t)mode;
172 } else {
173 if (fstat(fd, &st) == -1)
174 fatal("rcs_open: %s: fstat: %s", path, strerror(errno(*__errno())));
175 fmode = st.st_mode;
176 }
177
178 fmode &= ~cvs_umask;
179
180 rfp = xcalloc(1, sizeof(*rfp));
181
182 rfp->rf_path = xstrdup(path);
183 rfp->rf_flags = flags | RCS_SLOCK(1<<6) | RCS_SYNCED(1<<5);
184 rfp->rf_mode = fmode;
185 if (fd == -1)
186 rfp->rf_file = NULL((void *)0);
187 else if ((rfp->rf_file = fdopen(fd, "r")) == NULL((void *)0))
188 fatal("rcs_open: %s: fdopen: %s", path, strerror(errno(*__errno())));
189 rfp->rf_dead = 0;
190
191 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)
;
192 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)
;
193 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)
;
194 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)
;
195
196 if (!(rfp->rf_flags & RCS_CREATE(1<<2))) {
197 if (rcsparse_init(rfp))
198 fatal("could not parse admin data");
199 }
200
201 /* fill in rd_locker */
202 TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list)for((lkr) = ((&(rfp->rf_locks))->tqh_first); (lkr) !=
((void *)0); (lkr) = ((lkr)->rl_list.tqe_next))
{
203 if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL((void *)0)) {
204 rcs_close(rfp);
205 return (NULL((void *)0));
206 }
207
208 rdp->rd_locker = xstrdup(lkr->rl_name);
209 }
210
211 return (rfp);
212}
213
214/*
215 * rcs_close()
216 *
217 * Close an RCS file handle.
218 */
219void
220rcs_close(RCSFILE *rfp)
221{
222 struct rcs_delta *rdp;
223 struct rcs_access *rap;
224 struct rcs_lock *rlp;
225 struct rcs_sym *rsp;
226
227 if ((rfp->rf_flags & RCS_WRITE(1<<1)) && !(rfp->rf_flags & RCS_SYNCED(1<<5)))
228 rcs_write(rfp);
229
230 while (!TAILQ_EMPTY(&(rfp->rf_delta))(((&(rfp->rf_delta))->tqh_first) == ((void *)0))) {
231 rdp = TAILQ_FIRST(&(rfp->rf_delta))((&(rfp->rf_delta))->tqh_first);
232 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)
;
233 rcs_freedelta(rdp);
234 }
235
236 while (!TAILQ_EMPTY(&(rfp->rf_access))(((&(rfp->rf_access))->tqh_first) == ((void *)0))) {
237 rap = TAILQ_FIRST(&(rfp->rf_access))((&(rfp->rf_access))->tqh_first);
238 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)
;
239 free(rap->ra_name);
240 free(rap);
241 }
242
243 while (!TAILQ_EMPTY(&(rfp->rf_symbols))(((&(rfp->rf_symbols))->tqh_first) == ((void *)0))) {
244 rsp = TAILQ_FIRST(&(rfp->rf_symbols))((&(rfp->rf_symbols))->tqh_first);
245 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)
;
246 free(rsp->rs_num);
247 free(rsp->rs_name);
248 free(rsp);
249 }
250
251 while (!TAILQ_EMPTY(&(rfp->rf_locks))(((&(rfp->rf_locks))->tqh_first) == ((void *)0))) {
252 rlp = TAILQ_FIRST(&(rfp->rf_locks))((&(rfp->rf_locks))->tqh_first);
253 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)
;
254 free(rlp->rl_num);
255 free(rlp->rl_name);
256 free(rlp);
257 }
258
259 free(rfp->rf_head);
260 free(rfp->rf_branch);
261
262 if (rfp->rf_file != NULL((void *)0))
263 fclose(rfp->rf_file);
264 free(rfp->rf_path);
265 free(rfp->rf_comment);
266 free(rfp->rf_expand);
267 free(rfp->rf_desc);
268 if (rfp->rf_pdata != NULL((void *)0))
269 rcsparse_free(rfp);
270 free(rfp);
271}
272
273/*
274 * rcs_write()
275 *
276 * Write the contents of the RCS file handle <rfp> to disk in the file whose
277 * path is in <rf_path>.
278 */
279void
280rcs_write(RCSFILE *rfp)
281{
282 FILE *fp;
283 char numbuf[CVS_REV_BUFSZ32], *fn, tmpdir[PATH_MAX1024];
284 struct rcs_access *ap;
285 struct rcs_sym *symp;
286 struct rcs_branch *brp;
287 struct rcs_delta *rdp;
288 struct rcs_lock *lkp;
289 size_t len;
290 int fd, saved_errno;
291
292 fd = -1;
293
294 if (rfp->rf_flags & RCS_SYNCED(1<<5))
295 return;
296
297 if (cvs_noexec == 1)
298 return;
299
300 /* Write operations need the whole file parsed */
301 if (rcsparse_deltatexts(rfp, NULL((void *)0)))
302 fatal("rcs_write: rcsparse_deltatexts");
303
304 if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
305 fatal("rcs_write: truncation");
306 (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
307
308 if ((fd = mkstemp(fn)) == -1)
309 fatal("%s", fn);
310
311 if ((fp = fdopen(fd, "w")) == NULL((void *)0)) {
312 saved_errno = errno(*__errno());
313 (void)unlink(fn);
314 fatal("fdopen %s: %s", fn, strerror(saved_errno));
315 }
316
317 worklist_add(fn, &temp_files);
318
319 if (rfp->rf_head != NULL((void *)0))
320 rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
321 else
322 numbuf[0] = '\0';
323
324 fprintf(fp, "head\t%s;\n", numbuf);
325
326 if (rfp->rf_branch != NULL((void *)0)) {
327 rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
328 fprintf(fp, "branch\t%s;\n", numbuf);
329 }
330
331 fputs("access", fp);
332 TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list)for((ap) = ((&(rfp->rf_access))->tqh_first); (ap) !=
((void *)0); (ap) = ((ap)->ra_list.tqe_next))
{
333 fprintf(fp, "\n\t%s", ap->ra_name);
334 }
335 fputs(";\n", fp);
336
337 fprintf(fp, "symbols");
338 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list)for((symp) = ((&(rfp->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
{
339 if (RCSNUM_ISBRANCH(symp->rs_num)((symp->rs_num)->rn_len % 2))
340 rcsnum_addmagic(symp->rs_num);
341 rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
342 fprintf(fp, "\n\t%s:%s", symp->rs_name, numbuf);
343 }
344 fprintf(fp, ";\n");
345
346 fprintf(fp, "locks");
347 TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list)for((lkp) = ((&(rfp->rf_locks))->tqh_first); (lkp) !=
((void *)0); (lkp) = ((lkp)->rl_list.tqe_next))
{
348 rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
349 fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
350 }
351
352 fprintf(fp, ";");
353
354 if (rfp->rf_flags & RCS_SLOCK(1<<6))
355 fprintf(fp, " strict;");
356 fputc('\n', fp);
357
358 fputs("comment\t@", fp);
359 if (rfp->rf_comment != NULL((void *)0)) {
360 rcs_strprint((const u_char *)rfp->rf_comment,
361 strlen(rfp->rf_comment), fp);
362 fputs("@;\n", fp);
363 } else
364 fputs("# @;\n", fp);
365
366 if (rfp->rf_expand != NULL((void *)0)) {
367 fputs("expand @", fp);
368 rcs_strprint((const u_char *)rfp->rf_expand,
369 strlen(rfp->rf_expand), fp);
370 fputs("@;\n", fp);
371 }
372
373 fputs("\n\n", fp);
374
375 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list)for((rdp) = ((&(rfp->rf_delta))->tqh_first); (rdp) !=
((void *)0); (rdp) = ((rdp)->rd_list.tqe_next))
{
376 fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
377 sizeof(numbuf)));
378 fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
379 rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
380 rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
381 rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
382 fprintf(fp, "\tauthor %s;\tstate %s;\n",
383 rdp->rd_author, rdp->rd_state);
384 fputs("branches", fp);
385 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list)for((brp) = ((&(rdp->rd_branches))->tqh_first); (brp
) != ((void *)0); (brp) = ((brp)->rb_list.tqe_next))
{
386 fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
387 sizeof(numbuf)));
388 }
389 fputs(";\n", fp);
390 fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
391 numbuf, sizeof(numbuf)));
392 }
393
394 fputs("\ndesc\n@", fp);
395 if (rfp->rf_desc != NULL((void *)0) && (len = strlen(rfp->rf_desc)) > 0) {
396 rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
397 if (rfp->rf_desc[len-1] != '\n')
398 fputc('\n', fp);
399 }
400 fputs("@\n", fp);
401
402 /* deltatexts */
403 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list)for((rdp) = ((&(rfp->rf_delta))->tqh_first); (rdp) !=
((void *)0); (rdp) = ((rdp)->rd_list.tqe_next))
{
404 fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
405 sizeof(numbuf)));
406 fputs("log\n@", fp);
407 if (rdp->rd_log != NULL((void *)0)) {
408 len = strlen(rdp->rd_log);
409 rcs_strprint((const u_char *)rdp->rd_log, len, fp);
410 if (len == 0 || rdp->rd_log[len-1] != '\n')
411 fputc('\n', fp);
412 }
413 fputs("@\ntext\n@", fp);
414 if (rdp->rd_text != NULL((void *)0))
415 rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
416 fputs("@\n", fp);
417 }
418
419 if (fchmod(fd, rfp->rf_mode) == -1) {
420 saved_errno = errno(*__errno());
421 (void)unlink(fn);
422 fatal("fchmod %s: %s", fn, strerror(saved_errno));
423 }
424
425 (void)fclose(fp);
426
427 if (rename(fn, rfp->rf_path) == -1) {
428 saved_errno = errno(*__errno());
429 (void)unlink(fn);
430 fatal("rename(%s, %s): %s", fn, rfp->rf_path,
431 strerror(saved_errno));
432 }
433
434 rfp->rf_flags |= RCS_SYNCED(1<<5);
435 free(fn);
436}
437
438/*
439 * rcs_head_get()
440 *
441 * Retrieve the revision number of the head revision for the RCS file <file>.
442 */
443RCSNUM *
444rcs_head_get(RCSFILE *file)
445{
446 struct rcs_branch *brp;
447 struct rcs_delta *rdp;
448 RCSNUM *rev, *rootrev;
449
450 if (file->rf_head == NULL((void *)0))
451 return NULL((void *)0);
452
453 rev = rcsnum_alloc();
454 if (file->rf_branch != NULL((void *)0)) {
455 /* we have a default branch, use that to calculate the
456 * real HEAD*/
457 rootrev = rcsnum_alloc();
458 rcsnum_cpy(file->rf_branch, rootrev,
459 file->rf_branch->rn_len - 1);
460 if ((rdp = rcs_findrev(file, rootrev)) == NULL((void *)0))
461 fatal("rcs_head_get: could not find root revision");
462
463 /* HEAD should be the last revision on the default branch */
464 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list)for((brp) = ((&(rdp->rd_branches))->tqh_first); (brp
) != ((void *)0); (brp) = ((brp)->rb_list.tqe_next))
{
465 if (rcsnum_cmp(brp->rb_num, file->rf_branch,
466 file->rf_branch->rn_len) == 0)
467 break;
468 }
469 free(rootrev);
470
471 if (brp == NULL((void *)0))
472 fatal("rcs_head_get: could not find first default "
473 "branch revision");
474
475 if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL((void *)0))
476 fatal("rcs_head_get: could not find branch revision");
477 while (rdp->rd_next->rn_len != 0)
478 if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL((void *)0))
479 fatal("rcs_head_get: could not find "
480 "next branch revision");
481
482 rcsnum_cpy(rdp->rd_num, rev, 0);
483 } else {
484 rcsnum_cpy(file->rf_head, rev, 0);
485 }
486
487 return (rev);
488}
489
490/*
491 * rcs_head_set()
492 *
493 * Set the revision number of the head revision for the RCS file <file> to
494 * <rev>, which must reference a valid revision within the file.
495 */
496int
497rcs_head_set(RCSFILE *file, RCSNUM *rev)
498{
499 if (rcs_findrev(file, rev) == NULL((void *)0))
500 return (-1);
501
502 if (file->rf_head == NULL((void *)0))
503 file->rf_head = rcsnum_alloc();
504
505 rcsnum_cpy(rev, file->rf_head, 0);
506 file->rf_flags &= ~RCS_SYNCED(1<<5);
507 return (0);
508}
509
510/*
511 * rcs_branch_new()
512 *
513 * Create a new branch out of supplied revision for the RCS file <file>.
514 */
515RCSNUM *
516rcs_branch_new(RCSFILE *file, RCSNUM *rev)
517{
518 RCSNUM *brev;
519 struct rcs_sym *sym;
520
521 if ((brev = rcsnum_new_branch(rev)) == NULL((void *)0))
522 return (NULL((void *)0));
523
524 for (;;) {
525 TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list)for((sym) = ((&(file->rf_symbols))->tqh_first); (sym
) != ((void *)0); (sym) = ((sym)->rs_list.tqe_next))
526 if (!rcsnum_cmp(sym->rs_num, brev, 0))
527 break;
528
529 if (sym == NULL((void *)0))
530 break;
531
532 if (rcsnum_inc(brev) == NULL((void *)0) ||
533 rcsnum_inc(brev) == NULL((void *)0)) {
534 free(brev);
535 return (NULL((void *)0));
536 }
537 }
538
539 return (brev);
540}
541
542/*
543 * rcs_branch_get()
544 *
545 * Retrieve the default branch number for the RCS file <file>.
546 * Returns the number on success. If NULL is returned, then there is no
547 * default branch for this file.
548 */
549const RCSNUM *
550rcs_branch_get(RCSFILE *file)
551{
552 return (file->rf_branch);
553}
554
555/*
556 * rcs_branch_set()
557 *
558 * Set the default branch for the RCS file <file> to <bnum>.
559 * Returns 0 on success, -1 on failure.
560 */
561int
562rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
563{
564 if (file->rf_branch == NULL((void *)0))
565 file->rf_branch = rcsnum_alloc();
566
567 rcsnum_cpy(bnum, file->rf_branch, 0);
568 file->rf_flags &= ~RCS_SYNCED(1<<5);
569 return (0);
570}
571
572/*
573 * rcs_access_add()
574 *
575 * Add the login name <login> to the access list for the RCS file <file>.
576 * Returns 0 on success, or -1 on failure.
577 */
578int
579rcs_access_add(RCSFILE *file, const char *login)
580{
581 struct rcs_access *ap;
582
583 /* first look for duplication */
584 TAILQ_FOREACH(ap, &(file->rf_access), ra_list)for((ap) = ((&(file->rf_access))->tqh_first); (ap) !=
((void *)0); (ap) = ((ap)->ra_list.tqe_next))
{
585 if (strcmp(ap->ra_name, login) == 0)
586 return (-1);
587 }
588
589 ap = xmalloc(sizeof(*ap));
590 ap->ra_name = xstrdup(login);
591 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)
;
592
593 /* not synced anymore */
594 file->rf_flags &= ~RCS_SYNCED(1<<5);
595 return (0);
596}
597
598/*
599 * rcs_access_remove()
600 *
601 * Remove an entry with login name <login> from the access list of the RCS
602 * file <file>.
603 * Returns 0 on success, or -1 on failure.
604 */
605int
606rcs_access_remove(RCSFILE *file, const char *login)
607{
608 struct rcs_access *ap;
609
610 TAILQ_FOREACH(ap, &(file->rf_access), ra_list)for((ap) = ((&(file->rf_access))->tqh_first); (ap) !=
((void *)0); (ap) = ((ap)->ra_list.tqe_next))
611 if (strcmp(ap->ra_name, login) == 0)
612 break;
613
614 if (ap == NULL((void *)0))
615 return (-1);
616
617 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)
;
618 free(ap->ra_name);
619 free(ap);
620
621 /* not synced anymore */
622 file->rf_flags &= ~RCS_SYNCED(1<<5);
623 return (0);
624}
625
626/*
627 * rcs_sym_add()
628 *
629 * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
630 * is named <sym> and is bound to the RCS revision <snum>.
631 */
632int
633rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
634{
635 struct rcs_sym *symp;
636
637 if (!rcs_sym_check(sym))
638 return (-1);
639
640 /* first look for duplication */
641 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list)for((symp) = ((&(rfp->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
{
642 if (strcmp(symp->rs_name, sym) == 0)
643 return (1);
644 }
645
646 symp = xmalloc(sizeof(*symp));
647 symp->rs_name = xstrdup(sym);
648 symp->rs_num = rcsnum_alloc();
649 rcsnum_cpy(snum, symp->rs_num, 0);
650
651 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)
;
652
653 /* not synced anymore */
654 rfp->rf_flags &= ~RCS_SYNCED(1<<5);
655 return (0);
656}
657
658/*
659 * rcs_sym_remove()
660 *
661 * Remove the symbol with name <sym> from the symbol list for the RCS file
662 * <file>. If no such symbol is found, the call fails and returns with an
663 * error.
664 * Returns 0 on success, or -1 on failure.
665 */
666int
667rcs_sym_remove(RCSFILE *file, const char *sym)
668{
669 struct rcs_sym *symp;
670
671 if (!rcs_sym_check(sym))
672 return (-1);
673
674 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)for((symp) = ((&(file->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
675 if (strcmp(symp->rs_name, sym) == 0)
676 break;
677
678 if (symp == NULL((void *)0))
679 return (-1);
680
681 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)
;
682 free(symp->rs_name);
683 free(symp->rs_num);
684 free(symp);
685
686 /* not synced anymore */
687 file->rf_flags &= ~RCS_SYNCED(1<<5);
688 return (0);
689}
690
691/*
692 * rcs_sym_get()
693 *
694 * Find a specific symbol <sym> entry in the tree of the RCS file <file>.
695 *
696 * Returns a pointer to the symbol on success, or NULL on failure.
697 */
698struct rcs_sym *
699rcs_sym_get(RCSFILE *file, const char *sym)
700{
701 struct rcs_sym *symp;
702
703 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)for((symp) = ((&(file->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
704 if (strcmp(symp->rs_name, sym) == 0)
705 return (symp);
706
707 return (NULL((void *)0));
708}
709
710/*
711 * rcs_sym_getrev()
712 *
713 * Retrieve the RCS revision number associated with the symbol <sym> for the
714 * RCS file <file>. The returned value is a dynamically-allocated copy and
715 * should be freed by the caller once they are done with it.
716 * Returns the RCSNUM on success, or NULL on failure.
717 */
718RCSNUM *
719rcs_sym_getrev(RCSFILE *file, const char *sym)
720{
721 RCSNUM *num;
722 struct rcs_sym *symp;
723
724 if (!rcs_sym_check(sym) || file->rf_head == NULL((void *)0))
725 return (NULL((void *)0));
726
727 if (!strcmp(sym, RCS_HEAD_BRANCH"HEAD")) {
728 num = rcsnum_alloc();
729 rcsnum_cpy(file->rf_head, num, 0);
730 return (num);
731 }
732
733 num = NULL((void *)0);
734 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)for((symp) = ((&(file->rf_symbols))->tqh_first); (symp
) != ((void *)0); (symp) = ((symp)->rs_list.tqe_next))
735 if (strcmp(symp->rs_name, sym) == 0)
736 break;
737
738 if (symp != NULL((void *)0)) {
739 num = rcsnum_alloc();
740 rcsnum_cpy(symp->rs_num, num, 0);
741 }
742
743 return (num);
744}
745
746/*
747 * rcs_sym_check()
748 *
749 * Check the RCS symbol name <sym> for any unsupported characters.
750 * Returns 1 if the tag is correct, 0 if it isn't valid.
751 */
752int
753rcs_sym_check(const char *sym)
754{
755 int ret;
756 const unsigned char *cp;
757
758 ret = 1;
759 cp = sym;
760 if (!isalpha(*cp++))
761 return (0);
762
763 for (; *cp != '\0'; cp++)
764 if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL((void *)0))) {
765 ret = 0;
766 break;
767 }
768
769 return (ret);
770}
771
772/*
773 * rcs_lock_getmode()
774 *
775 * Retrieve the locking mode of the RCS file <file>.
776 */
777int
778rcs_lock_getmode(RCSFILE *file)
779{
780 return (file->rf_flags & RCS_SLOCK(1<<6)) ? RCS_LOCK_STRICT1 : RCS_LOCK_LOOSE0;
781}
782
783/*
784 * rcs_lock_setmode()
785 *
786 * Set the locking mode of the RCS file <file> to <mode>, which must either
787 * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
788 * Returns the previous mode on success, or -1 on failure.
789 */
790int
791rcs_lock_setmode(RCSFILE *file, int mode)
792{
793 int pmode;
794 pmode = rcs_lock_getmode(file);
795
796 if (mode == RCS_LOCK_STRICT1)
797 file->rf_flags |= RCS_SLOCK(1<<6);
798 else if (mode == RCS_LOCK_LOOSE0)
799 file->rf_flags &= ~RCS_SLOCK(1<<6);
800 else
801 fatal("rcs_lock_setmode: invalid mode `%d'", mode);
802
803 file->rf_flags &= ~RCS_SYNCED(1<<5);
804 return (pmode);
805}
806
807/*
808 * rcs_lock_add()
809 *
810 * Add an RCS lock for the user <user> on revision <rev>.
811 * Returns 0 on success, or -1 on failure.
812 */
813int
814rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
815{
816 struct rcs_lock *lkp;
817
818 /* first look for duplication */
819 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)for((lkp) = ((&(file->rf_locks))->tqh_first); (lkp)
!= ((void *)0); (lkp) = ((lkp)->rl_list.tqe_next))
{
820 if (strcmp(lkp->rl_name, user) == 0 &&
821 rcsnum_cmp(rev, lkp->rl_num, 0) == 0)
822 return (-1);
823 }
824
825 lkp = xmalloc(sizeof(*lkp));
826 lkp->rl_name = xstrdup(user);
827 lkp->rl_num = rcsnum_alloc();
828 rcsnum_cpy(rev, lkp->rl_num, 0);
829
830 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
)
;
831
832 /* not synced anymore */
833 file->rf_flags &= ~RCS_SYNCED(1<<5);
834 return (0);
835}
836
837
838/*
839 * rcs_lock_remove()
840 *
841 * Remove the RCS lock on revision <rev>.
842 * Returns 0 on success, or -1 on failure.
843 */
844int
845rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
846{
847 struct rcs_lock *lkp;
848
849 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)for((lkp) = ((&(file->rf_locks))->tqh_first); (lkp)
!= ((void *)0); (lkp) = ((lkp)->rl_list.tqe_next))
{
850 if (strcmp(lkp->rl_name, user) == 0 &&
851 rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
852 break;
853 }
854
855 if (lkp == NULL((void *)0))
856 return (-1);
857
858 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)
;
859 free(lkp->rl_num);
860 free(lkp->rl_name);
861 free(lkp);
862
863 /* not synced anymore */
864 file->rf_flags &= ~RCS_SYNCED(1<<5);
865 return (0);
866}
867
868/*
869 * rcs_desc_get()
870 *
871 * Retrieve the description for the RCS file <file>.
872 */
873const char *
874rcs_desc_get(RCSFILE *file)
875{
876 return (file->rf_desc);
877}
878
879/*
880 * rcs_desc_set()
881 *
882 * Set the description for the RCS file <file>.
883 */
884void
885rcs_desc_set(RCSFILE *file, const char *desc)
886{
887 char *tmp;
888
889 tmp = xstrdup(desc);
890 free(file->rf_desc);
891 file->rf_desc = tmp;
892 file->rf_flags &= ~RCS_SYNCED(1<<5);
893}
894
895/*
896 * rcs_comment_lookup()
897 *
898 * Lookup the assumed comment leader based on a file's suffix.
899 * Returns a pointer to the string on success, or NULL on failure.
900 */
901const char *
902rcs_comment_lookup(const char *filename)
903{
904 int i;
905 const char *sp;
906
907 if ((sp = strrchr(filename, '.')) == NULL((void *)0))
908 return (NULL((void *)0));
909 sp++;
910
911 for (i = 0; i < (int)NB_COMTYPES(sizeof(rcs_comments)/sizeof(rcs_comments[0])); i++)
912 if (strcmp(rcs_comments[i].rc_suffix, sp) == 0)
913 return (rcs_comments[i].rc_cstr);
914 return (NULL((void *)0));
915}
916
917/*
918 * rcs_comment_get()
919 *
920 * Retrieve the comment leader for the RCS file <file>.
921 */
922const char *
923rcs_comment_get(RCSFILE *file)
924{
925 return (file->rf_comment);
926}
927
928/*
929 * rcs_comment_set()
930 *
931 * Set the comment leader for the RCS file <file>.
932 */
933void
934rcs_comment_set(RCSFILE *file, const char *comment)
935{
936 char *tmp;
937
938 tmp = xstrdup(comment);
939 free(file->rf_comment);
940 file->rf_comment = tmp;
941 file->rf_flags &= ~RCS_SYNCED(1<<5);
942}
943
944int
945rcs_patch_lines(struct rcs_lines *dlines, struct rcs_lines *plines,
946 struct rcs_line **alines, struct rcs_delta *rdp)
947{
948 u_char op;
949 char *ep;
950 struct rcs_line *lp, *dlp, *ndlp;
951 int i, lineno, nbln;
952 u_char tmp;
953
954 dlp = TAILQ_FIRST(&(dlines->l_lines))((&(dlines->l_lines))->tqh_first);
955 lp = TAILQ_FIRST(&(plines->l_lines))((&(plines->l_lines))->tqh_first);
956
957 /* skip first bogus line */
958 for (lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next); lp != NULL((void *)0);
959 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next)) {
960 if (lp->l_len < 2)
961 fatal("line too short, RCS patch seems broken");
962 op = *(lp->l_line);
963 /* NUL-terminate line buffer for strtol() safety. */
964 tmp = lp->l_line[lp->l_len - 1];
965 lp->l_line[lp->l_len - 1] = '\0';
966 lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
967 if (lineno - 1 > dlines->l_nblines || lineno < 0) {
968 fatal("invalid line specification in RCS patch");
969 }
970 ep++;
971 nbln = (int)strtol(ep, &ep, 10);
972 /* Restore the last byte of the buffer */
973 lp->l_line[lp->l_len - 1] = tmp;
974 if (nbln < 0)
975 fatal("invalid line number specification in RCS patch");
976
977 /* find the appropriate line */
978 for (;;) {
979 if (dlp == NULL((void *)0))
980 break;
981 if (dlp->l_lineno == lineno)
982 break;
983 if (dlp->l_lineno > lineno) {
984 dlp = TAILQ_PREV(dlp, tqh, l_list)(*(((struct tqh *)((dlp)->l_list.tqe_prev))->tqh_last));
985 } else if (dlp->l_lineno < lineno) {
986 if (((ndlp = TAILQ_NEXT(dlp, l_list)((dlp)->l_list.tqe_next)) == NULL((void *)0)) ||
987 ndlp->l_lineno > lineno)
988 break;
989 dlp = ndlp;
990 }
991 }
992 if (dlp == NULL((void *)0))
993 fatal("can't find referenced line in RCS patch");
994
995 if (op == 'd') {
996 for (i = 0; (i < nbln) && (dlp != NULL((void *)0)); i++) {
997 ndlp = TAILQ_NEXT(dlp, l_list)((dlp)->l_list.tqe_next);
998 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)
;
999 if (alines != NULL((void *)0) && dlp->l_line != NULL((void *)0)) {
1000 dlp->l_delta = rdp;
1001 alines[dlp->l_lineno_orig - 1] =
1002 dlp;
1003 } else
1004 free(dlp);
1005 dlp = ndlp;
1006 /* last line is gone - reset dlp */
1007 if (dlp == NULL((void *)0)) {
1008 ndlp = TAILQ_LAST(&(dlines->l_lines),(*(((struct tqh *)((&(dlines->l_lines))->tqh_last))
->tqh_last))
1009 tqh)(*(((struct tqh *)((&(dlines->l_lines))->tqh_last))
->tqh_last))
;
1010 dlp = ndlp;
1011 }
1012 }
1013 } else if (op == 'a') {
1014 for (i = 0; i < nbln; i++) {
1015 ndlp = lp;
1016 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next);
1017 if (lp == NULL((void *)0))
1018 fatal("truncated RCS patch");
1019 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)
;
1020 if (alines != NULL((void *)0)) {
1021 if (lp->l_needsfree == 1)
1022 free(lp->l_line);
1023 lp->l_line = NULL((void *)0);
1024 lp->l_needsfree = 0;
1025 }
1026 lp->l_delta = rdp;
1027 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)
1028 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)
;
1029 dlp = lp;
1030
1031 /* we don't want lookup to block on those */
1032 lp->l_lineno = lineno;
1033
1034 lp = ndlp;
1035 }
1036 } else
1037 fatal("unknown RCS patch operation `%c'", op);
1038
1039 /* last line of the patch, done */
1040 if (lp->l_lineno == plines->l_nblines)
1041 break;
1042 }
1043
1044 /* once we're done patching, rebuild the line numbers */
1045 lineno = 0;
1046 TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)for((lp) = ((&(dlines->l_lines))->tqh_first); (lp) !=
((void *)0); (lp) = ((lp)->l_list.tqe_next))
1047 lp->l_lineno = lineno++;
1048 dlines->l_nblines = lineno - 1;
1049
1050 return (0);
1051}
1052
1053void
1054rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
1055{
1056 struct rcs_lines *plines;
1057 struct rcs_line *lp;
1058 int added, i, nbln, removed;
1059 char op, *ep;
1060 u_char tmp;
1061
1062 added = removed = 0;
1063
1064 plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
1065 lp = TAILQ_FIRST(&(plines->l_lines))((&(plines->l_lines))->tqh_first);
1066
1067 /* skip first bogus line */
1068 for (lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next); lp != NULL((void *)0);
1069 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next)) {
1070 if (lp->l_len < 2)
1071 fatal("line too short, RCS patch seems broken");
1072 op = *(lp->l_line);
1073 /* NUL-terminate line buffer for strtol() safety. */
1074 tmp = lp->l_line[lp->l_len - 1];
1075 lp->l_line[lp->l_len - 1] = '\0';
1076 (void)strtol((lp->l_line + 1), &ep, 10);
1077 ep++;
1078 nbln = (int)strtol(ep, &ep, 10);
1079 /* Restore the last byte of the buffer */
1080 lp->l_line[lp->l_len - 1] = tmp;
1081 if (nbln < 0)
1082 fatal("invalid line number specification in RCS patch");
1083
1084 if (op == 'a') {
1085 added += nbln;
1086 for (i = 0; i < nbln; i++) {
1087 lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next);
1088 if (lp == NULL((void *)0))
1089 fatal("truncated RCS patch");
1090 }
1091 }
1092 else if (op == 'd')
1093 removed += nbln;
1094 else
1095 fatal("unknown RCS patch operation '%c'", op);
1096 }
1097
1098 cvs_freelines(plines);
1099
1100 *ladded = added;
1101 *lremoved = removed;
1102}
1103
1104/*
1105 * rcs_rev_add()
1106 *
1107 * Add a revision to the RCS file <rf>. The new revision's number can be
1108 * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
1109 * new revision will have a number equal to the previous head revision plus
1110 * one). The <msg> argument specifies the log message for that revision, and
1111 * <date> specifies the revision's date (a value of -1 is
1112 * equivalent to using the current time).
1113 * If <author> is NULL, set the author for this revision to the current user.
1114 * Returns 0 on success, or -1 on failure.
1115 */
1116int
1117rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1118 const char *author)
1119{
1120 time_t now;
1121 RCSNUM *root = NULL((void *)0);
1122 struct passwd *pw;
1123 struct rcs_branch *brp, *obrp;
1124 struct rcs_delta *ordp, *rdp;
1125
1126 if (rev == RCS_HEAD_REV((RCSNUM *)(-1))) {
1127 if (rf->rf_flags & RCS_CREATE(1<<2)) {
1128 if ((rev = rcsnum_parse(RCS_HEAD_INIT"1.1")) == NULL((void *)0))
1129 return (-1);
1130 free(rf->rf_head);
1131 rf->rf_head = rev;
1132 } else if (rf->rf_head == NULL((void *)0)) {
1133 return (-1);
1134 } else {
1135 rev = rcsnum_inc(rf->rf_head);
1136 }
1137 } else {
1138 if ((rdp = rcs_findrev(rf, rev)) != NULL((void *)0))
1139 return (-1);
1140 }
1141
1142 rdp = xcalloc(1, sizeof(*rdp));
1143
1144 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)
;
1145
1146 rdp->rd_num = rcsnum_alloc();
1147 rcsnum_cpy(rev, rdp->rd_num, 0);
1148
1149 rdp->rd_next = rcsnum_alloc();
1150
1151 if (!author && !(author = getlogin())) {
1152 if (!(pw = getpwuid(getuid())))
1153 fatal("getpwuid failed");
1154 author = pw->pw_name;
1155 }
1156 rdp->rd_author = xstrdup(author);
1157 rdp->rd_state = xstrdup(RCS_STATE_EXP"Exp");
1158 rdp->rd_log = xstrdup(msg);
1159
1160 if (date != (time_t)(-1))
1161 now = date;
1162 else
1163 time(&now);
1164 gmtime_r(&now, &(rdp->rd_date));
1165
1166 if (RCSNUM_ISBRANCHREV(rev)(!((rev)->rn_len % 2) && ((rev)->rn_len >= 4
))
)
1167 TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list)do { (rdp)->rd_list.tqe_next = ((void *)0); (rdp)->rd_list
.tqe_prev = (&(rf->rf_delta))->tqh_last; *(&(rf
->rf_delta))->tqh_last = (rdp); (&(rf->rf_delta)
)->tqh_last = &(rdp)->rd_list.tqe_next; } while (0)
;
1168 else
1169 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)
;
1170 rf->rf_ndelta++;
1171
1172 if (!(rf->rf_flags & RCS_CREATE(1<<2))) {
1173 if (RCSNUM_ISBRANCHREV(rev)(!((rev)->rn_len % 2) && ((rev)->rn_len >= 4
))
) {
1174 if (rev->rn_id[rev->rn_len - 1] == 1) {
1175 /* a new branch */
1176 root = rcsnum_branch_root(rev);
1177 brp = xmalloc(sizeof(*brp));
1178 brp->rb_num = rcsnum_alloc();
1179 rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1180
1181 if ((ordp = rcs_findrev(rf, root)) == NULL((void *)0))
1182 fatal("root node not found");
1183
1184 TAILQ_FOREACH(obrp, &(ordp->rd_branches),for((obrp) = ((&(ordp->rd_branches))->tqh_first); (
obrp) != ((void *)0); (obrp) = ((obrp)->rb_list.tqe_next))
1185 rb_list)for((obrp) = ((&(ordp->rd_branches))->tqh_first); (
obrp) != ((void *)0); (obrp) = ((obrp)->rb_list.tqe_next))
{
1186 if (!rcsnum_cmp(obrp->rb_num,
1187 brp->rb_num,
1188 brp->rb_num->rn_len - 1))
1189 break;
1190 }
1191
1192 if (obrp == NULL((void *)0)) {
1193 TAILQ_INSERT_TAIL(&(ordp->rd_branches),do { (brp)->rb_list.tqe_next = ((void *)0); (brp)->rb_list
.tqe_prev = (&(ordp->rd_branches))->tqh_last; *(&
(ordp->rd_branches))->tqh_last = (brp); (&(ordp->
rd_branches))->tqh_last = &(brp)->rb_list.tqe_next;
} while (0)
1194 brp, rb_list)do { (brp)->rb_list.tqe_next = ((void *)0); (brp)->rb_list
.tqe_prev = (&(ordp->rd_branches))->tqh_last; *(&
(ordp->rd_branches))->tqh_last = (brp); (&(ordp->
rd_branches))->tqh_last = &(brp)->rb_list.tqe_next;
} while (0)
;
1195 }
1196 } else {
1197 root = rcsnum_alloc();
1198 rcsnum_cpy(rev, root, 0);
1199 rcsnum_dec(root);
1200 if ((ordp = rcs_findrev(rf, root)) == NULL((void *)0))
1201 fatal("previous revision not found");
1202 rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1203 }
1204 } else {
1205 ordp = TAILQ_NEXT(rdp, rd_list)((rdp)->rd_list.tqe_next);
1206 rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1207 }
1208 }
1209
1210 free(root);
1211
1212 /* not synced anymore */
1213 rf->rf_flags &= ~RCS_SYNCED(1<<5);
1214
1215 return (0);
1216}
1217
1218/*
1219 * rcs_rev_remove()
1220 *
1221 * Remove the revision whose number is <rev> from the RCS file <rf>.
1222 */
1223int
1224rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1225{
1226 int fd1, fd2;
1227 char *path_tmp1, *path_tmp2;
1228 struct rcs_delta *rdp, *prevrdp, *nextrdp;
1229 BUF *prevbuf, *newdiff, *newdeltatext;
1230
1231 if (rev == RCS_HEAD_REV((RCSNUM *)(-1)))
1232 rev = rf->rf_head;
1233
1234 if (rev == NULL((void *)0))
1235 return (-1);
1236
1237 /* do we actually have that revision? */
1238 if ((rdp = rcs_findrev(rf, rev)) == NULL((void *)0))
1239 return (-1);
1240
1241 /*
1242 * This is confusing, the previous delta is next in the TAILQ list.
1243 * the next delta is the previous one in the TAILQ list.
1244 *
1245 * When the HEAD revision got specified, nextrdp will be NULL.
1246 * When the first revision got specified, prevrdp will be NULL.
1247 */
1248 prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list)((rdp)->rd_list.tqe_next);
1249 nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, tqh, rd_list)(*(((struct tqh *)((rdp)->rd_list.tqe_prev))->tqh_last)
)
;
1250
1251 newdeltatext = NULL((void *)0);
1252 prevbuf = NULL((void *)0);
1253 path_tmp1 = path_tmp2 = NULL((void *)0);
1254
1255 if (prevrdp != NULL((void *)0) && nextrdp != NULL((void *)0)) {
1256 newdiff = buf_alloc(64);
1257
1258 /* calculate new diff */
1259 (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
1260 fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1261
1262 (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
1263 fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1264
1265 diff_format = D_RCSDIFF5;
1266 if (diffreg(path_tmp1, path_tmp2,
1267 fd1, fd2, newdiff, D_FORCEASCII0x01) == D_ERROR7)
1268 fatal("rcs_diffreg failed");
1269
1270 close(fd1);
1271 close(fd2);
1272
1273 newdeltatext = newdiff;
1274 } else if (nextrdp == NULL((void *)0) && prevrdp != NULL((void *)0)) {
1275 newdeltatext = prevbuf;
1276 }
1277
1278 if (newdeltatext != NULL((void *)0)) {
1279 if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1280 fatal("error setting new deltatext");
1281 }
1282
1283 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)
;
1284
1285 /* update pointers */
1286 if (prevrdp != NULL((void *)0) && nextrdp != NULL((void *)0)) {
1287 rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1288 } else if (prevrdp != NULL((void *)0)) {
1289 if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1290 fatal("rcs_head_set failed");
1291 } else if (nextrdp != NULL((void *)0)) {
1292 free(nextrdp->rd_next);
1293 nextrdp->rd_next = rcsnum_alloc();
1294 } else {
1295 free(rf->rf_head);
1296 rf->rf_head = NULL((void *)0);
1297 }
1298
1299 rf->rf_ndelta--;
1300 rf->rf_flags &= ~RCS_SYNCED(1<<5);
1301
1302 rcs_freedelta(rdp);
1303 free(newdeltatext);
1304 free(path_tmp1);
1305 free(path_tmp2);
1306
1307 return (0);
1308}
1309
1310/*
1311 * rcs_findrev()
1312 *
1313 * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1314 * The revision number is given in <rev>.
1315 *
1316 * Returns a pointer to the delta on success, or NULL on failure.
1317 */
1318struct rcs_delta *
1319rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1320{
1321 int isbrev;
1322 struct rcs_delta *rdp;
1323
1324 if (rev == NULL((void *)0))
1325 return NULL((void *)0);
1326
1327 isbrev = RCSNUM_ISBRANCHREV(rev)(!((rev)->rn_len % 2) && ((rev)->rn_len >= 4
))
;
1328
1329 /*
1330 * We need to do more parsing if the last revision in the linked list
1331 * is greater than the requested revision.
1332 */
1333 rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist)(*(((struct rcs_dlist *)((&(rfp->rf_delta))->tqh_last
))->tqh_last))
;
1334 if (rdp == NULL((void *)0) ||
1335 (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1336 ((isbrev && rdp->rd_num->rn_len < 4) ||
1337 (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1338 if (rcsparse_deltas(rfp, rev))
1339 fatal("error parsing deltas");
1340 }
1341
1342 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list)for((rdp) = ((&(rfp->rf_delta))->tqh_first); (rdp) !=
((void *)0); (rdp) = ((rdp)->rd_list.tqe_next))
{
1343 if (rcsnum_differ(rdp->rd_num, rev))
1344 continue;
1345 else
1346 return (rdp);
1347 }
1348
1349 return (NULL((void *)0));
1350}
1351
1352/*
1353 * rcs_kwexp_set()
1354 *
1355 * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1356 */
1357void
1358rcs_kwexp_set(RCSFILE *file, int mode)
1359{
1360 int i;
1361 char *tmp, buf[8] = "";
1362
1363 if (RCS_KWEXP_INVAL(mode)((mode & 0x20) || ((mode & 0x10) && (mode &
~0x10)))
)
1364 return;
1365
1366 i = 0;
1367 if (mode == RCS_KWEXP_NONE0x01)
1368 buf[0] = 'b';
1369 else if (mode == RCS_KWEXP_OLD0x10)
1370 buf[0] = 'o';
1371 else {
1372 if (mode & RCS_KWEXP_NAME0x02)
1373 buf[i++] = 'k';
1374 if (mode & RCS_KWEXP_VAL0x04)
1375 buf[i++] = 'v';
1376 if (mode & RCS_KWEXP_LKR0x08)
1377 buf[i++] = 'l';
1378 }
1379
1380 tmp = xstrdup(buf);
1381 free(file->rf_expand);
1382 file->rf_expand = tmp;
1383 /* not synced anymore */
1384 file->rf_flags &= ~RCS_SYNCED(1<<5);
1385}
1386
1387/*
1388 * rcs_kwexp_get()
1389 *
1390 * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1391 */
1392int
1393rcs_kwexp_get(RCSFILE *file)
1394{
1395 if (file->rf_expand == NULL((void *)0))
1396 return (RCS_KWEXP_DEFAULT(0x02 | 0x04));
1397
1398 return (rcs_kflag_get(file->rf_expand));
1399}
1400
1401/*
1402 * rcs_kflag_get()
1403 *
1404 * Get the keyword expansion mode from a set of character flags given in
1405 * <flags> and return the appropriate flag mask. In case of an error, the
1406 * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1407 */
1408int
1409rcs_kflag_get(const char *flags)
1410{
1411 int fl;
1412 size_t len;
1413 const char *fp;
1414
1415 if (flags == NULL((void *)0) || !(len = strlen(flags)))
1416 return (RCS_KWEXP_ERR0x20);
1417
1418 fl = 0;
1419 for (fp = flags; *fp != '\0'; fp++) {
1420 if (*fp == 'k')
1421 fl |= RCS_KWEXP_NAME0x02;
1422 else if (*fp == 'v')
1423 fl |= RCS_KWEXP_VAL0x04;
1424 else if (*fp == 'l')
1425 fl |= RCS_KWEXP_LKR0x08;
1426 else if (*fp == 'o') {
1427 if (len != 1)
1428 fl |= RCS_KWEXP_ERR0x20;
1429 fl |= RCS_KWEXP_OLD0x10;
1430 } else if (*fp == 'b') {
1431 if (len != 1)
1432 fl |= RCS_KWEXP_ERR0x20;
1433 fl |= RCS_KWEXP_NONE0x01;
1434 } else /* unknown letter */
1435 fl |= RCS_KWEXP_ERR0x20;
1436 }
1437
1438 return (fl);
1439}
1440
1441/*
1442 * rcs_freedelta()
1443 *
1444 * Free the contents of a delta structure.
1445 */
1446static void
1447rcs_freedelta(struct rcs_delta *rdp)
1448{
1449 struct rcs_branch *rb;
1450
1451 free(rdp->rd_num);
1452 free(rdp->rd_next);
1453 free(rdp->rd_author);
1454 free(rdp->rd_locker);
1455 free(rdp->rd_state);
1456 free(rdp->rd_log);
1457 free(rdp->rd_text);
1458
1459 while ((rb = TAILQ_FIRST(&(rdp->rd_branches))((&(rdp->rd_branches))->tqh_first)) != NULL((void *)0)) {
1460 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)
;
1461 free(rb->rb_num);
1462 free(rb);
1463 }
1464
1465 free(rdp);
1466}
1467
1468/*
1469 * rcs_strprint()
1470 *
1471 * Output an RCS string <str> of size <slen> to the stream <stream>. Any
1472 * '@' characters are escaped. Otherwise, the string can contain arbitrary
1473 * binary data.
1474 */
1475static void
1476rcs_strprint(const u_char *str, size_t slen, FILE *stream)
1477{
1478 const u_char *ap, *ep, *sp;
1479
1480 if (slen == 0)
1481 return;
1482
1483 ep = str + slen - 1;
1484
1485 for (sp = str; sp <= ep;) {
1486 ap = memchr(sp, '@', ep - sp);
1487 if (ap == NULL((void *)0))
1488 ap = ep;
1489 (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1490
1491 if (*ap == '@')
1492 putc('@', stream)(!__isthreaded ? __sputc('@', stream) : (putc)('@', stream));
1493 sp = ap + 1;
1494 }
1495}
1496
1497/*
1498 * rcs_deltatext_set()
1499 *
1500 * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1501 * Returns -1 on error, 0 on success.
1502 */
1503int
1504rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1505{
1506 size_t len;
1507 u_char *dtext;
1508 struct rcs_delta *rdp;
1509
1510 /* Write operations require full parsing */
1511 if (rcsparse_deltatexts(rfp, NULL((void *)0)))
1512 return (-1);
1513
1514 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1515 return (-1);
1516
1517 free(rdp->rd_text);
1518
1519 len = buf_len(bp);
1520 dtext = buf_release(bp);
1521 bp = NULL((void *)0);
1522
1523 if (len != 0) {
1524 rdp->rd_text = xmalloc(len);
1525 rdp->rd_tlen = len;
1526 (void)memcpy(rdp->rd_text, dtext, len);
1527 } else {
1528 rdp->rd_text = NULL((void *)0);
1529 rdp->rd_tlen = 0;
1530 }
1531
1532 free(dtext);
1533 return (0);
1534}
1535
1536/*
1537 * rcs_rev_setlog()
1538 *
1539 * Sets the log message of revision <rev> to <logtext>.
1540 */
1541int
1542rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
1543{
1544 struct rcs_delta *rdp;
1545
1546 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1547 return (-1);
1548
1549 free(rdp->rd_log);
1550
1551 rdp->rd_log = xstrdup(logtext);
1552 rfp->rf_flags &= ~RCS_SYNCED(1<<5);
1553 return (0);
1554}
1555/*
1556 * rcs_rev_getdate()
1557 *
1558 * Get the date corresponding to a given revision.
1559 * Returns the date on success, -1 on failure.
1560 */
1561time_t
1562rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
1563{
1564 struct rcs_delta *rdp;
1565
1566 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1567 return (-1);
1568
1569 return (timegm(&rdp->rd_date));
1570}
1571
1572/*
1573 * rcs_state_set()
1574 *
1575 * Sets the state of revision <rev> to <state>
1576 * NOTE: default state is 'Exp'. States may not contain spaces.
1577 *
1578 * Returns -1 on failure, 0 on success.
1579 */
1580int
1581rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
1582{
1583 struct rcs_delta *rdp;
1584
1585 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1586 return (-1);
1587
1588 free(rdp->rd_state);
1589
1590 rdp->rd_state = xstrdup(state);
1591
1592 rfp->rf_flags &= ~RCS_SYNCED(1<<5);
1593
1594 return (0);
1595}
1596
1597/*
1598 * rcs_state_check()
1599 *
1600 * Check if string <state> is valid.
1601 *
1602 * Returns 0 if the string is valid, -1 otherwise.
1603 */
1604int
1605rcs_state_check(const char *state)
1606{
1607 if (strcmp(state, RCS_STATE_DEAD"dead") && strcmp(state, RCS_STATE_EXP"Exp"))
1608 return (-1);
1609
1610 return (0);
1611}
1612
1613/*
1614 * rcs_state_get()
1615 *
1616 * Get the state for a given revision of a specified RCSFILE.
1617 *
1618 * Returns NULL on failure.
1619 */
1620const char *
1621rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
1622{
1623 struct rcs_delta *rdp;
1624
1625 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
1626 return (NULL((void *)0));
1627
1628 return (rdp->rd_state);
1629}
1630
1631/* rcs_get_revision() */
1632static RCSNUM *
1633rcs_get_revision(const char *revstr, RCSFILE *rfp)
1634{
1635 RCSNUM *rev, *brev, *frev;
1636 struct rcs_branch *brp;
1637 struct rcs_delta *rdp;
1638 size_t i;
1639
1640 rdp = NULL((void *)0);
1641
1642 if (!strcmp(revstr, RCS_HEAD_BRANCH"HEAD")) {
1643 if (rfp->rf_head == NULL((void *)0))
1644 return (NULL((void *)0));
1645
1646 frev = rcsnum_alloc();
1647 rcsnum_cpy(rfp->rf_head, frev, 0);
1648 return (frev);
1649 }
1650
1651 /* Possibly we could be passed a version number */
1652 if ((rev = rcsnum_parse(revstr)) != NULL((void *)0)) {
1653 /* Do not return if it is not in RCS file */
1654 if ((rdp = rcs_findrev(rfp, rev)) != NULL((void *)0))
1655 return (rev);
1656 } else {
1657 /* More likely we will be passed a symbol */
1658 rev = rcs_sym_getrev(rfp, revstr);
1659 }
1660
1661 if (rev == NULL((void *)0))
1662 return (NULL((void *)0));
1663
1664 /*
1665 * If it was not a branch, thats ok the symbolic
1666 * name refered to a revision, so return the resolved
1667 * revision for the given name. */
1668 if (!RCSNUM_ISBRANCH(rev)((rev)->rn_len % 2)) {
1669 /* Sanity check: The first two elements of any
1670 * revision (be it on a branch or on trunk) cannot
1671 * be greater than HEAD.
1672 *
1673 * XXX: To avoid comparing to uninitialized memory,
1674 * the minimum of both revision lengths is taken
1675 * instead of just 2.
1676 */
1677 if (rfp->rf_head == NULL((void *)0) || rcsnum_cmp(rev, rfp->rf_head,
1678 MINIMUM(rfp->rf_head->rn_len, rev->rn_len)(((rfp->rf_head->rn_len) < (rev->rn_len)) ? (rfp->
rf_head->rn_len) : (rev->rn_len))
) < 0) {
1679 free(rev);
1680 return (NULL((void *)0));
1681 }
1682 return (rev);
1683 }
1684
1685 brev = rcsnum_alloc();
1686 rcsnum_cpy(rev, brev, rev->rn_len - 1);
1687
1688 if ((rdp = rcs_findrev(rfp, brev)) == NULL((void *)0))
1689 fatal("rcs_get_revision: tag `%s' does not exist", revstr);
1690 free(brev);
1691
1692 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list)for((brp) = ((&(rdp->rd_branches))->tqh_first); (brp
) != ((void *)0); (brp) = ((brp)->rb_list.tqe_next))
{
1693 for (i = 0; i < rev->rn_len; i++)
1694 if (brp->rb_num->rn_id[i] != rev->rn_id[i])
1695 break;
1696 if (i != rev->rn_len)
1697 continue;
1698 break;
1699 }
1700
1701 free(rev);
1702 frev = rcsnum_alloc();
1703 if (brp == NULL((void *)0)) {
1704 rcsnum_cpy(rdp->rd_num, frev, 0);
1705 return (frev);
1706 } else {
1707 /* Fetch the delta with the correct branch num */
1708 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL((void *)0))
1709 fatal("rcs_get_revision: could not fetch branch "
1710 "delta");
1711 rcsnum_cpy(rdp->rd_num, frev, 0);
1712 return (frev);
1713 }
1714}
1715
1716/*
1717 * rcs_rev_getlines()
1718 *
1719 * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
1720 * return it as a pointer to a struct rcs_lines.
1721 */
1722struct rcs_lines *
1723rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
1724{
1725 size_t plen;
1726 int annotate, done, i, nextroot;
1727 RCSNUM *tnum, *bnum;
1728 struct rcs_branch *brp;
1729 struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
1730 u_char *patch;
1731 struct rcs_line *line, *nline;
1732 struct rcs_lines *dlines, *plines;
1733
1734 hrdp = prdp = rdp = trdp = NULL((void *)0);
1735
1736 if (rfp->rf_head == NULL((void *)0) ||
1737 (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL((void *)0))
1738 fatal("rcs_rev_getlines: no HEAD revision");
1739
1740 tnum = frev;
1741 if (rcsparse_deltatexts(rfp, hrdp->rd_num))
1742 fatal("rcs_rev_getlines: rcsparse_deltatexts");
1743
1744 /* revision on branch, get the branch root */
1745 nextroot = 2;
1746 bnum = rcsnum_alloc();
1747 if (RCSNUM_ISBRANCHREV(tnum)(!((tnum)->rn_len % 2) && ((tnum)->rn_len >=
4))
)
1748 rcsnum_cpy(tnum, bnum, nextroot);
1749 else
1750 rcsnum_cpy(tnum, bnum, tnum->rn_len);
1751
1752 if (alines != NULL((void *)0)) {
1753 /* start with annotate first at requested revision */
1754 annotate = ANNOTATE_LATER2;
1755 *alines = NULL((void *)0);
1756 } else
1757 annotate = ANNOTATE_NEVER0;
1758
1759 dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
1760
1761 done = 0;
1762
1763 rdp = hrdp;
1764 if (!rcsnum_differ(rdp->rd_num, bnum)) {
1765 if (annotate == ANNOTATE_LATER2) {
1766 /* found requested revision for annotate */
1767 i = 0;
1768 TAILQ_FOREACH(line, &(dlines->l_lines), l_list)for((line) = ((&(dlines->l_lines))->tqh_first); (line
) != ((void *)0); (line) = ((line)->l_list.tqe_next))
{
1769 line->l_lineno_orig = line->l_lineno;
1770 i++;
1771 }
1772
1773 *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1774 (*alines)[i] = NULL((void *)0);
1775 annotate = ANNOTATE_NOW1;
1776
1777 /* annotate down to 1.1 from where we are */
1778 free(bnum);
1779 bnum = rcsnum_parse("1.1");
1780 if (!rcsnum_differ(rdp->rd_num, bnum)) {
1781 goto next;
1782 }
1783 } else
1784 goto next;
1785 }
1786
1787 prdp = hrdp;
1788 if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL((void *)0))
1789 goto done;
1790
1791again:
1792 while (rdp != NULL((void *)0)) {
1793 if (rdp->rd_next->rn_len != 0) {
1794 trdp = rcs_findrev(rfp, rdp->rd_next);
1795 if (trdp == NULL((void *)0))
1796 fatal("failed to grab next revision");
1797 }
1798
1799 if (rdp->rd_tlen == 0) {
1800 if (rcsparse_deltatexts(rfp, rdp->rd_num))
1801 fatal("rcs_rev_getlines: rcsparse_deltatexts");
1802 if (rdp->rd_tlen == 0) {
1803 if (!rcsnum_differ(rdp->rd_num, bnum))
1804 break;
1805 rdp = trdp;
1806 continue;
1807 }
1808 }
1809
1810 plen = rdp->rd_tlen;
1811 patch = rdp->rd_text;
1812 plines = cvs_splitlines(patch, plen);
1813 if (annotate == ANNOTATE_NOW1)
1814 rcs_patch_lines(dlines, plines, *alines, prdp);
1815 else
1816 rcs_patch_lines(dlines, plines, NULL((void *)0), NULL((void *)0));
1817 cvs_freelines(plines);
1818
1819 if (!rcsnum_differ(rdp->rd_num, bnum)) {
1820 if (annotate != ANNOTATE_LATER2)
1821 break;
1822
1823 /* found requested revision for annotate */
1824 i = 0;
1825 TAILQ_FOREACH(line, &(dlines->l_lines), l_list)for((line) = ((&(dlines->l_lines))->tqh_first); (line
) != ((void *)0); (line) = ((line)->l_list.tqe_next))
{
1826 line->l_lineno_orig = line->l_lineno;
1827 i++;
1828 }
1829
1830 *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1831 (*alines)[i] = NULL((void *)0);
1832 annotate = ANNOTATE_NOW1;
1833
1834 /* annotate down to 1.1 from where we are */
1835 free(bnum);
1836 bnum = rcsnum_parse("1.1");
1837
1838 if (!rcsnum_differ(rdp->rd_num, bnum))
1839 break;
1840 }
1841
1842 prdp = rdp;
1843 rdp = trdp;
1844 }
1845
1846next:
1847 if (rdp == NULL((void *)0) || !rcsnum_differ(rdp->rd_num, frev))
1848 done = 1;
1849
1850 if (RCSNUM_ISBRANCHREV(frev)(!((frev)->rn_len % 2) && ((frev)->rn_len >=
4))
&& done != 1) {
1851 nextroot += 2;
1852 rcsnum_cpy(frev, bnum, nextroot);
1853
1854 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list)for((brp) = ((&(rdp->rd_branches))->tqh_first); (brp
) != ((void *)0); (brp) = ((brp)->rb_list.tqe_next))
{
1855 for (i = 0; i < nextroot - 1; i++)
1856 if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1857 break;
1858 if (i == nextroot - 1)
1859 break;
1860 }
1861
1862 if (brp == NULL((void *)0)) {
1863 if (annotate != ANNOTATE_NEVER0) {
1864 free(*alines);
1865 *alines = NULL((void *)0);
1866 cvs_freelines(dlines);
1867 free(bnum);
1868 return (NULL((void *)0));
1869 }
1870 fatal("expected branch not found on branch list");
1871 }
1872
1873 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL((void *)0))
1874 fatal("rcs_rev_getlines: failed to get delta for target rev");
1875
1876 goto again;
1877 }
1878done:
1879 /* put remaining lines into annotate buffer */
1880 if (annotate == ANNOTATE_NOW1) {
1881 for (line = TAILQ_FIRST(&(dlines->l_lines))((&(dlines->l_lines))->tqh_first);
1882 line != NULL((void *)0); line = nline) {
1883 nline = TAILQ_NEXT(line, l_list)((line)->l_list.tqe_next);
1884 TAILQ_REMOVE(&(dlines->l_lines), line, l_list)do { if (((line)->l_list.tqe_next) != ((void *)0)) (line)->
l_list.tqe_next->l_list.tqe_prev = (line)->l_list.tqe_prev
; else (&(dlines->l_lines))->tqh_last = (line)->
l_list.tqe_prev; *(line)->l_list.tqe_prev = (line)->l_list
.tqe_next; ; ; } while (0)
;
1885 if (line->l_line == NULL((void *)0)) {
1886 free(line);
1887 continue;
1888 }
1889
1890 line->l_delta = rdp;
1891 (*alines)[line->l_lineno_orig - 1] = line;
1892 }
1893
1894 cvs_freelines(dlines);
1895 dlines = NULL((void *)0);
1896 }
1897
1898 if (bnum != tnum)
1899 free(bnum);
1900
1901 return (dlines);
1902}
1903
1904void
1905rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
1906{
1907 size_t plen;
1908 int i, nextroot;
1909 RCSNUM *bnum;
1910 struct rcs_branch *brp;
1911 struct rcs_delta *rdp, *trdp;
1912 u_char *patch;
1913 struct rcs_line *line;
1914 struct rcs_lines *dlines, *plines;
1915
1916 rdp = trdp = NULL((void *)0);
1
Null pointer value stored to 'trdp'
1917
1918 if (!RCSNUM_ISBRANCHREV(frev)(!((frev)->rn_len % 2) && ((frev)->rn_len >=
4))
)
2
Assuming the condition is true
3
Assuming field 'rn_len' is >= 4
4
Taking false branch
1919 fatal("rcs_annotate_getlines: branch revision expected");
1920
1921 /* revision on branch, get the branch root */
1922 nextroot = 2;
1923 bnum = rcsnum_alloc();
1924 rcsnum_cpy(frev, bnum, nextroot);
1925
1926 /*
1927 * Going from HEAD to 1.1 enables the use of an array, which is
1928 * much faster. Unfortunately this is not possible with branch
1929 * revisions, so copy over our alines (array) into dlines (tailq).
1930 */
1931 dlines = xcalloc(1, sizeof(*dlines));
1932 TAILQ_INIT(&(dlines->l_lines))do { (&(dlines->l_lines))->tqh_first = ((void *)0);
(&(dlines->l_lines))->tqh_last = &(&(dlines
->l_lines))->tqh_first; } while (0)
;
5
Loop condition is false. Exiting loop
1933 line = xcalloc(1, sizeof(*line));
1934 TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list)do { (line)->l_list.tqe_next = ((void *)0); (line)->l_list
.tqe_prev = (&(dlines->l_lines))->tqh_last; *(&
(dlines->l_lines))->tqh_last = (line); (&(dlines->
l_lines))->tqh_last = &(line)->l_list.tqe_next; } while
(0)
;
6
Loop condition is false. Exiting loop
1935
1936 for (i = 0; (*alines)[i] != NULL((void *)0); i++) {
7
Assuming the condition is false
8
Loop condition is false. Execution continues on line 1942
1937 line = (*alines)[i];
1938 line->l_lineno = i + 1;
1939 TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list)do { (line)->l_list.tqe_next = ((void *)0); (line)->l_list
.tqe_prev = (&(dlines->l_lines))->tqh_last; *(&
(dlines->l_lines))->tqh_last = (line); (&(dlines->
l_lines))->tqh_last = &(line)->l_list.tqe_next; } while
(0)
;
1940 }
1941
1942 rdp = rcs_findrev(rfp, bnum);
1943 if (rdp == NULL((void *)0))
9
Assuming 'rdp' is not equal to NULL
10
Taking false branch
1944 fatal("failed to grab branch root revision");
1945
1946 do {
1947 nextroot += 2;
1948 rcsnum_cpy(frev, bnum, nextroot);
1949
1950 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list)for((brp) = ((&(rdp->rd_branches))->tqh_first); (brp
) != ((void *)0); (brp) = ((brp)->rb_list.tqe_next))
{
11
Assuming 'brp' is not equal to null
12
Loop condition is true. Entering loop body
1951 for (i = 0; i < nextroot - 1; i++)
13
Loop condition is true. Entering loop body
16
Loop condition is true. Entering loop body
19
Loop condition is true. Entering loop body
22
Loop condition is false. Execution continues on line 1954
1952 if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
14
Assuming the condition is false
15
Taking false branch
17
Assuming the condition is false
18
Taking false branch
20
Assuming the condition is false
21
Taking false branch
1953 break;
1954 if (i == nextroot - 1)
23
Taking true branch
1955 break;
24
Execution continues on line 1958
1956 }
1957
1958 if (brp
24.1
'brp' is not equal to NULL
== NULL((void *)0))
25
Taking false branch
1959 fatal("expected branch not found on branch list");
1960
1961 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL((void *)0))
26
Assuming the condition is false
27
Taking false branch
1962 fatal("failed to get delta for target rev");
1963
1964 for (;;) {
28
Loop condition is true. Entering loop body
40
Loop condition is true. Entering loop body
1965 if (rdp->rd_next->rn_len != 0) {
29
Assuming field 'rn_len' is equal to 0
30
Taking false branch
41
Access to field 'rd_next' results in a dereference of a null pointer (loaded from variable 'rdp')
1966 trdp = rcs_findrev(rfp, rdp->rd_next);
1967 if (trdp == NULL((void *)0))
1968 fatal("failed to grab next revision");
1969 }
1970
1971 if (rdp->rd_tlen == 0) {
31
Assuming field 'rd_tlen' is equal to 0
32
Taking true branch
1972 if (rcsparse_deltatexts(rfp, rdp->rd_num))
33
Assuming the condition is false
34
Taking false branch
1973 fatal("rcs_annotate_getlines: "
1974 "rcsparse_deltatexts");
1975 if (rdp->rd_tlen
34.1
Field 'rd_tlen' is equal to 0
== 0) {
35
Taking true branch
1976 if (!rcsnum_differ(rdp->rd_num, bnum))
36
Assuming the condition is false
37
Taking false branch
1977 break;
1978 rdp = trdp;
38
Null pointer value stored to 'rdp'
1979 continue;
39
Execution continues on line 1964
1980 }
1981 }
1982
1983 plen = rdp->rd_tlen;
1984 patch = rdp->rd_text;
1985 plines = cvs_splitlines(patch, plen);
1986 rcs_patch_lines(dlines, plines, NULL((void *)0), rdp);
1987 cvs_freelines(plines);
1988
1989 if (!rcsnum_differ(rdp->rd_num, bnum))
1990 break;
1991
1992 rdp = trdp;
1993 }
1994 } while (rcsnum_differ(rdp->rd_num, frev));
1995
1996 if (bnum != frev)
1997 free(bnum);
1998
1999 /*
2000 * All lines have been parsed, now they must be copied over
2001 * into alines (array) again.
2002 */
2003 free(*alines);
2004
2005 i = 0;
2006 TAILQ_FOREACH(line, &(dlines->l_lines), l_list)for((line) = ((&(dlines->l_lines))->tqh_first); (line
) != ((void *)0); (line) = ((line)->l_list.tqe_next))
{
2007 if (line->l_line != NULL((void *)0))
2008 i++;
2009 }
2010 *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
2011 (*alines)[i] = NULL((void *)0);
2012
2013 i = 0;
2014 TAILQ_FOREACH(line, &(dlines->l_lines), l_list)for((line) = ((&(dlines->l_lines))->tqh_first); (line
) != ((void *)0); (line) = ((line)->l_list.tqe_next))
{
2015 if (line->l_line != NULL((void *)0))
2016 (*alines)[i++] = line;
2017 }
2018}
2019
2020/*
2021 * rcs_rev_getbuf()
2022 *
2023 * XXX: This is really really slow and should be avoided if at all possible!
2024 *
2025 * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
2026 * return it as a BUF pointer.
2027 */
2028BUF *
2029rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
2030{
2031 int expmode, expand;
2032 struct rcs_delta *rdp;
2033 struct rcs_lines *lines;
2034 struct rcs_line *lp, *nlp;
2035 BUF *bp;
2036
2037 rdp = NULL((void *)0);
2038 expmode = RCS_KWEXP_NONE0x01;
2039 expand = 0;
2040 lines = rcs_rev_getlines(rfp, rev, NULL((void *)0));
2041 bp = buf_alloc(1024 * 16);
2042
2043 if (!(mode & RCS_KWEXP_NONE0x01)) {
2044 expmode = rcs_kwexp_get(rfp);
2045
2046 if (!(expmode & RCS_KWEXP_NONE0x01)) {
2047 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0)) {
2048 char version[RCSNUM_MAXSTR64];
2049
2050 rcsnum_tostr(rev, version, sizeof(version));
2051 fatal("could not find desired version %s in %s",
2052 version, rfp->rf_path);
2053 }
2054
2055 expand = 1;
2056 }
2057 }
2058
2059 for (lp = TAILQ_FIRST(&lines->l_lines)((&lines->l_lines)->tqh_first); lp != NULL((void *)0);) {
2060 nlp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next);
2061
2062 if (lp->l_line == NULL((void *)0)) {
2063 lp = nlp;
2064 continue;
2065 }
2066
2067 if (expand)
2068 rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
2069
2070 do {
2071 buf_append(bp, lp->l_line, lp->l_len);
2072 } while ((lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next)) != nlp);
2073 }
2074
2075 cvs_freelines(lines);
2076
2077 return (bp);
2078}
2079
2080/*
2081 * rcs_rev_write_fd()
2082 *
2083 * Write the entire contents of revision <frev> from the rcsfile <rfp> to
2084 * file descriptor <fd>.
2085 */
2086void
2087rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode)
2088{
2089 int fd;
2090 FILE *fp;
2091 size_t ret;
2092 int expmode, expand;
2093 struct rcs_delta *rdp;
2094 struct rcs_lines *lines;
2095 struct rcs_line *lp, *nlp;
2096 extern int print_stdout;
2097
2098 rdp = NULL((void *)0);
2099 expmode = RCS_KWEXP_NONE0x01;
2100 expand = 0;
2101 lines = rcs_rev_getlines(rfp, rev, NULL((void *)0));
2102
2103 if (!(mode & RCS_KWEXP_NONE0x01)) {
2104 expmode = rcs_kwexp_get(rfp);
2105
2106 if (!(expmode & RCS_KWEXP_NONE0x01)) {
2107 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
2108 fatal("could not fetch revision");
2109 expand = 1;
2110 }
2111 }
2112
2113 fd = dup(_fd);
2114 if (fd == -1)
2115 fatal("rcs_rev_write_fd: dup: %s", strerror(errno(*__errno())));
2116
2117 if ((fp = fdopen(fd, "w")) == NULL((void *)0))
2118 fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno(*__errno())));
2119
2120 for (lp = TAILQ_FIRST(&lines->l_lines)((&lines->l_lines)->tqh_first); lp != NULL((void *)0);) {
2121 nlp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next);
2122
2123 if (lp->l_line == NULL((void *)0)) {
2124 lp = nlp;
2125 continue;
2126 }
2127
2128 if (expand)
2129 rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
2130
2131 do {
2132 /*
2133 * Solely for the checkout and update -p options.
2134 */
2135 if (cvs_server_active == 1 &&
2136 (cvs_cmdop == CVS_OP_CHECKOUT4 ||
2137 cvs_cmdop == CVS_OP_UPDATE24) && print_stdout == 1) {
2138 ret = fwrite("M ", 1, 2, fp);
2139 if (ret != 2)
2140 fatal("rcs_rev_write_fd: %s",
2141 strerror(errno(*__errno())));
2142 }
2143
2144 ret = fwrite(lp->l_line, 1, lp->l_len, fp);
2145 if (ret != lp->l_len)
2146 fatal("rcs_rev_write_fd: %s", strerror(errno(*__errno())));
2147 } while ((lp = TAILQ_NEXT(lp, l_list)((lp)->l_list.tqe_next)) != nlp);
2148 }
2149
2150 cvs_freelines(lines);
2151 (void)fclose(fp);
2152}
2153
2154/*
2155 * rcs_rev_write_stmp()
2156 *
2157 * Write the contents of the rev <rev> to a temporary file whose path is
2158 * specified using <template> (see mkstemp(3)). NB. This function will modify
2159 * <template>, as per mkstemp.
2160 */
2161int
2162rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode)
2163{
2164 int fd;
2165
2166 if ((fd = mkstemp(template)) == -1)
2167 fatal("mkstemp: `%s': %s", template, strerror(errno(*__errno())));
2168
2169 worklist_add(template, &temp_files);
2170 rcs_rev_write_fd(rfp, rev, fd, mode);
2171
2172 if (lseek(fd, 0, SEEK_SET0) == -1)
2173 fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno(*__errno())));
2174
2175 return (fd);
2176}
2177
2178static void
2179rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct rcs_lines *lines,
2180 struct rcs_line *line, int mode)
2181{
2182 BUF *tmpbuf;
2183 int kwtype;
2184 u_int j, found;
2185 const u_char *c, *start, *fin, *end;
2186 char *kwstr, *rcsfile_basename;
2187 char expbuf[256], buf[256], path[PATH_MAX1024];
2188 size_t clen, kwlen, len, tlen;
2189
2190 kwtype = 0;
2191 kwstr = NULL((void *)0);
2192
2193 if (mode & RCS_KWEXP_OLD0x10)
2194 return;
2195
2196 len = line->l_len;
2197 if (len == 0)
2198 return;
2199
2200 c = line->l_line;
2201 found = 0;
2202 /* Final character in buffer. */
2203 fin = c + len - 1;
2204
2205 if (strlcpy(path, rcsfile, sizeof(path)) >= sizeof(path))
2206 fatal("rcs_kwexp_line: truncation");
2207 rcsfile_basename = basename(path);
2208
2209 /*
2210 * Keyword formats:
2211 * $Keyword$
2212 * $Keyword: value$
2213 */
2214 for (; c < fin; c++) {
2215 if (*c != '$')
2216 continue;
2217
2218 /* remember start of this possible keyword */
2219 start = c;
2220
2221 /* first following character has to be alphanumeric */
2222 c++;
2223 if (!isalpha(*c)) {
2224 c = start;
2225 continue;
2226 }
2227
2228 /* Number of characters between c and fin, inclusive. */
2229 clen = fin - c + 1;
2230
2231 /* look for any matching keywords */
2232 found = 0;
2233 for (j = 0; j < RCS_NKWORDS(sizeof(rcs_expkw)/sizeof(rcs_expkw[0])); j++) {
2234 kwlen = strlen(rcs_expkw[j].kw_str);
2235 /*
2236 * kwlen must be less than clen since clen
2237 * includes either a terminating `$' or a `:'.
2238 */
2239 if (kwlen < clen &&
2240 memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
2241 (c[kwlen] == '$' || c[kwlen] == ':')) {
2242 found = 1;
2243 kwstr = rcs_expkw[j].kw_str;
2244 kwtype = rcs_expkw[j].kw_type;
2245 c += kwlen;
2246 break;
2247 }
2248 }
2249
2250 if (found == 0 && cvs_tagname != NULL((void *)0)) {
2251 kwlen = strlen(cvs_tagname);
2252 if (kwlen < clen &&
2253 memcmp(c, cvs_tagname, kwlen) == 0 &&
2254 (c[kwlen] == '$' || c[kwlen] == ':')) {
2255 found = 1;
2256 kwstr = cvs_tagname;
2257 kwtype = RCS_KW_ID(0x0100 | 0x0200 | 0x2000 | 0x1000 | 0x0800);
2258 c += kwlen;
2259 }
2260 }
2261
2262 /* unknown keyword, continue looking */
2263 if (found == 0) {
2264 c = start;
2265 continue;
2266 }
2267
2268 /*
2269 * if the next character was ':' we need to look for
2270 * an '$' before the end of the line to be sure it is
2271 * in fact a keyword.
2272 */
2273 if (*c == ':') {
2274 for (; c <= fin; ++c) {
2275 if (*c == '$' || *c == '\n')
2276 break;
2277 }
2278
2279 if (*c != '$') {
2280 c = start;
2281 continue;
2282 }
2283 }
2284 end = c + 1;
2285
2286 /* start constructing the expansion */
2287 expbuf[0] = '\0';
2288
2289 if (mode & RCS_KWEXP_NAME0x02) {
2290 if (strlcat(expbuf, "$", sizeof(expbuf)) >=
2291 sizeof(expbuf) || strlcat(expbuf, kwstr,
2292 sizeof(expbuf)) >= sizeof(expbuf))
2293 fatal("rcs_kwexp_line: truncated");
2294 if ((mode & RCS_KWEXP_VAL0x04) &&
2295 strlcat(expbuf, ": ", sizeof(expbuf)) >=
2296 sizeof(expbuf))
2297 fatal("rcs_kwexp_line: truncated");
2298 }
2299
2300 /*
2301 * order matters because of RCS_KW_ID and
2302 * RCS_KW_HEADER here
2303 */
2304 if (mode & RCS_KWEXP_VAL0x04) {
2305 if (kwtype & RCS_KW_RCSFILE0x0100) {
2306 if (!(kwtype & RCS_KW_FULLPATH0x0010))
2307 (void)strlcat(expbuf, rcsfile_basename,
2308 sizeof(expbuf));
2309 else
2310 (void)strlcat(expbuf, rcsfile,
2311 sizeof(expbuf));
2312 if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2313 sizeof(expbuf))
2314 fatal("rcs_kwexp_line: truncated");
2315 }
2316
2317 if (kwtype & RCS_KW_REVISION0x0200) {
2318 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2319 if (strlcat(buf, " ", sizeof(buf)) >=
2320 sizeof(buf) || strlcat(expbuf, buf,
2321 sizeof(expbuf)) >= sizeof(buf))
2322 fatal("rcs_kwexp_line: truncated");
2323 }
2324
2325 if (kwtype & RCS_KW_DATE0x2000) {
2326 if (strftime(buf, sizeof(buf),
2327 "%Y/%m/%d %H:%M:%S ",
2328 &rdp->rd_date) == 0)
2329 fatal("rcs_kwexp_line: strftime "
2330 "failure");
2331 if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2332 sizeof(expbuf))
2333 fatal("rcs_kwexp_line: string "
2334 "truncated");
2335 }
2336
2337 if (kwtype & RCS_KW_MDOCDATE0x0020) {
2338 /*
2339 * Do not prepend ' ' for a single
2340 * digit, %e would do so and there is
2341 * no better format for strftime().
2342 */
2343 if (strftime(buf, sizeof(buf),
2344 (rdp->rd_date.tm_mday < 10) ?
2345 "%B%e %Y " : "%B %e %Y ",
2346 &rdp->rd_date) == 0)
2347 fatal("rcs_kwexp_line: strftime "
2348 "failure");
2349 if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2350 sizeof(expbuf))
2351 fatal("rcs_kwexp_line: string "
2352 "truncated");
2353 }
2354
2355 if (kwtype & RCS_KW_AUTHOR0x1000) {
2356 if (strlcat(expbuf, rdp->rd_author,
2357 sizeof(expbuf)) >= sizeof(expbuf) ||
2358 strlcat(expbuf, " ", sizeof(expbuf)) >=
2359 sizeof(expbuf))
2360 fatal("rcs_kwexp_line: string "
2361 "truncated");
2362 }
2363
2364 if (kwtype & RCS_KW_STATE0x0800) {
2365 if (strlcat(expbuf, rdp->rd_state,
2366 sizeof(expbuf)) >= sizeof(expbuf) ||
2367 strlcat(expbuf, " ", sizeof(expbuf)) >=
2368 sizeof(expbuf))
2369 fatal("rcs_kwexp_line: string "
2370 "truncated");
2371 }
2372
2373 /* order does not matter anymore below */
2374 if (kwtype & RCS_KW_LOG0x4000) {
2375 char linebuf[256];
2376 struct rcs_line *cur, *lp;
2377 char *logp, *l_line, *prefix, *q, *sprefix;
2378 size_t i;
2379
2380 /* Log line */
2381 if (!(kwtype & RCS_KW_FULLPATH0x0010))
2382 (void)strlcat(expbuf,
2383 rcsfile_basename, sizeof(expbuf));
2384 else
2385 (void)strlcat(expbuf, rcsfile,
2386 sizeof(expbuf));
2387
2388 if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2389 sizeof(expbuf))
2390 fatal("rcs_kwexp_line: string "
2391 "truncated");
2392
2393 cur = line;
2394
2395 /* copy rdp->rd_log for strsep */
2396 logp = xstrdup(rdp->rd_log);
2397
2398 /* copy our prefix for later processing */
2399 prefix = xmalloc(start - line->l_line + 1);
2400 memcpy(prefix, line->l_line,
2401 start - line->l_line);
2402 prefix[start - line->l_line] = '\0';
2403
2404 /* copy also prefix without trailing blanks. */
2405 sprefix = xstrdup(prefix);
2406 for (i = strlen(sprefix); i > 0 &&
2407 sprefix[i - 1] == ' '; i--)
2408 sprefix[i - 1] = '\0';
2409
2410 /* new line: revision + date + author */
2411 linebuf[0] = '\0';
2412 if (strlcat(linebuf, "Revision ",
2413 sizeof(linebuf)) >= sizeof(linebuf))
2414 fatal("rcs_kwexp_line: truncated");
2415 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2416 if (strlcat(linebuf, buf, sizeof(linebuf))
2417 >= sizeof(buf))
2418 fatal("rcs_kwexp_line: truncated");
2419 if (strftime(buf, sizeof(buf),
2420 " %Y/%m/%d %H:%M:%S ",
2421 &rdp->rd_date) == 0)
2422 fatal("rcs_kwexp_line: strftime "
2423 "failure");
2424 if (strlcat(linebuf, buf, sizeof(linebuf))
2425 >= sizeof(linebuf))
2426 fatal("rcs_kwexp_line: string "
2427 "truncated");
2428 if (strlcat(linebuf, rdp->rd_author,
2429 sizeof(linebuf)) >= sizeof(linebuf))
2430 fatal("rcs_kwexp_line: string "
2431 "truncated");
2432
2433 lp = xcalloc(1, sizeof(*lp));
2434 xasprintf((char **)&(lp->l_line), "%s%s\n",
2435 prefix, linebuf);
2436 lp->l_len = strlen(lp->l_line);
2437 TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,do { if (((lp)->l_list.tqe_next = (cur)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(lines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (cur)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(cur)->l_list
.tqe_next; } while (0)
2438 l_list)do { if (((lp)->l_list.tqe_next = (cur)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(lines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (cur)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(cur)->l_list
.tqe_next; } while (0)
;
2439 cur = lp;
2440
2441 /* Log message */
2442 q = logp;
2443 while ((l_line = strsep(&q, "\n")) != NULL((void *)0) &&
2444 q != NULL((void *)0)) {
2445 lp = xcalloc(1, sizeof(*lp));
2446
2447 if (l_line[0] == '\0') {
2448 xasprintf((char **)&(lp->l_line),
2449 "%s\n", sprefix);
2450 } else {
2451 xasprintf((char **)&(lp->l_line),
2452 "%s%s\n", prefix, l_line);
2453 }
2454
2455 lp->l_len = strlen(lp->l_line);
2456 TAILQ_INSERT_AFTER(&(lines->l_lines),do { if (((lp)->l_list.tqe_next = (cur)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(lines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (cur)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(cur)->l_list
.tqe_next; } while (0)
2457 cur, lp, l_list)do { if (((lp)->l_list.tqe_next = (cur)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(lines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (cur)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(cur)->l_list
.tqe_next; } while (0)
;
2458 cur = lp;
2459 }
2460 free(logp);
2461
2462 /*
2463 * This is just another hairy mess, but it must
2464 * be done: All characters behind Log will be
2465 * written in a new line next to log messages.
2466 * But that's not enough, we have to strip all
2467 * trailing whitespaces of our prefix.
2468 */
2469 lp = xcalloc(1, sizeof(*lp));
2470 xasprintf((char **)&lp->l_line, "%s%s",
2471 sprefix, end);
2472 lp->l_len = strlen(lp->l_line);
2473 TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,do { if (((lp)->l_list.tqe_next = (cur)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(lines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (cur)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(cur)->l_list
.tqe_next; } while (0)
2474 l_list)do { if (((lp)->l_list.tqe_next = (cur)->l_list.tqe_next
) != ((void *)0)) (lp)->l_list.tqe_next->l_list.tqe_prev
= &(lp)->l_list.tqe_next; else (&(lines->l_lines
))->tqh_last = &(lp)->l_list.tqe_next; (cur)->l_list
.tqe_next = (lp); (lp)->l_list.tqe_prev = &(cur)->l_list
.tqe_next; } while (0)
;
2475 cur = lp;
2476
2477 end = line->l_line + line->l_len - 1;
2478
2479 free(prefix);
2480 free(sprefix);
2481
2482 }
2483
2484 if (kwtype & RCS_KW_SOURCE0x0400) {
2485 if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
2486 sizeof(expbuf) || strlcat(expbuf, " ",
2487 sizeof(expbuf)) >= sizeof(expbuf))
2488 fatal("rcs_kwexp_line: string "
2489 "truncated");
2490 }
2491
2492 if (kwtype & RCS_KW_NAME0x8000)
2493 if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2494 sizeof(expbuf))
2495 fatal("rcs_kwexp_line: string "
2496 "truncated");
2497
2498 if (kwtype & RCS_KW_LOCKER0x0040)
2499 if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2500 sizeof(expbuf))
2501 fatal("rcs_kwexp_line: string "
2502 "truncated");
2503 }
2504
2505 /* end the expansion */
2506 if (mode & RCS_KWEXP_NAME0x02)
2507 if (strlcat(expbuf, "$",
2508 sizeof(expbuf)) >= sizeof(expbuf))
2509 fatal("rcs_kwexp_line: truncated");
2510
2511 /* Concatenate everything together. */
2512 tmpbuf = buf_alloc(len + strlen(expbuf));
2513 /* Append everything before keyword. */
2514 buf_append(tmpbuf, line->l_line,
2515 start - line->l_line);
2516 /* Append keyword. */
2517 buf_puts(tmpbuf, expbuf);
2518 /* Point c to end of keyword. */
2519 tlen = buf_len(tmpbuf) - 1;
2520 /* Append everything after keyword. */
2521 buf_append(tmpbuf, end,
2522 line->l_line + line->l_len - end);
2523 c = buf_get(tmpbuf) + tlen;
2524 /* Point fin to end of data. */
2525 fin = buf_get(tmpbuf) + buf_len(tmpbuf) - 1;
2526 /* Recalculate new length. */
2527 len = buf_len(tmpbuf);
2528
2529 /* tmpbuf is now ready, convert to string */
2530 if (line->l_needsfree)
2531 free(line->l_line);
2532 line->l_len = len;
2533 line->l_line = buf_release(tmpbuf);
2534 line->l_needsfree = 1;
2535 }
2536}
2537
2538/* rcs_translate_tag() */
2539RCSNUM *
2540rcs_translate_tag(const char *revstr, RCSFILE *rfp)
2541{
2542 int follow;
2543 time_t deltatime;
2544 char branch[CVS_REV_BUFSZ32];
2545 RCSNUM *brev, *frev, *rev;
2546 struct rcs_delta *rdp, *trdp;
2547 time_t cdate;
2548
2549 brev = frev = NULL((void *)0);
2550
2551 if (revstr == NULL((void *)0)) {
2552 if (rfp->rf_branch != NULL((void *)0)) {
2553 rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
2554 revstr = branch;
2555 } else {
2556 revstr = RCS_HEAD_BRANCH"HEAD";
2557 }
2558 }
2559
2560 if ((rev = rcs_get_revision(revstr, rfp)) == NULL((void *)0))
2561 return (NULL((void *)0));
2562
2563 if ((rdp = rcs_findrev(rfp, rev)) == NULL((void *)0))
2564 return (NULL((void *)0));
2565
2566 /* let's see if we must follow a branch */
2567 if (!strcmp(revstr, RCS_HEAD_BRANCH"HEAD"))
2568 follow = 1;
2569 else {
2570 frev = rcs_sym_getrev(rfp, revstr);
2571 if (frev == NULL((void *)0))
2572 frev = rcsnum_parse(revstr);
2573
2574 brev = rcsnum_alloc();
2575 rcsnum_cpy(rev, brev, rev->rn_len - 1);
2576
2577 if (frev != NULL((void *)0) && RCSNUM_ISBRANCH(frev)((frev)->rn_len % 2) &&
2578 !rcsnum_cmp(frev, brev, 0)) {
2579 follow = 1;
2580 } else
2581 follow = 0;
2582
2583 free(brev);
2584 }
2585
2586 if (cvs_specified_date != -1)
2587 cdate = cvs_specified_date;
2588 else
2589 cdate = cvs_directory_date;
2590
2591 if (cdate == -1) {
2592 free(frev);
2593
2594 /* XXX */
2595 if (rev->rn_len < 4 || !follow) {
2596 return (rev);
2597 }
2598
2599 /* Find the latest delta on that branch */
2600 free(rev);
2601 for (;;) {
2602 if (rdp->rd_next->rn_len == 0)
2603 break;
2604 if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL((void *)0))
2605 fatal("rcs_translate_tag: could not fetch "
2606 "branch delta");
2607 }
2608
2609 rev = rcsnum_alloc();
2610 rcsnum_cpy(rdp->rd_num, rev, 0);
2611 return (rev);
2612 }
2613
2614 if (frev != NULL((void *)0)) {
2615 brev = rcsnum_revtobr(frev);
2616 brev->rn_len = rev->rn_len - 1;
2617 free(frev);
2618 }
2619
2620 free(rev);
2621
2622 do {
2623 deltatime = timegm(&(rdp->rd_date));
2624
2625 if (RCSNUM_ISBRANCHREV(rdp->rd_num)(!((rdp->rd_num)->rn_len % 2) && ((rdp->rd_num
)->rn_len >= 4))
) {
2626 if (deltatime > cdate) {
2627 trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list)(*(((struct rcs_dlist *)((rdp)->rd_list.tqe_prev))->tqh_last
))
;
2628 if (trdp == NULL((void *)0))
2629 trdp = rdp;
2630
2631 if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
2632 return (NULL((void *)0));
2633
2634 rev = rcsnum_alloc();
2635 rcsnum_cpy(trdp->rd_num, rev, 0);
2636 return (rev);
2637 }
2638
2639 if (rdp->rd_next->rn_len == 0) {
2640 rev = rcsnum_alloc();
2641 rcsnum_cpy(rdp->rd_num, rev, 0);
2642 return (rev);
2643 }
2644 } else {
2645 if (deltatime < cdate) {
2646 rev = rcsnum_alloc();
2647 rcsnum_cpy(rdp->rd_num, rev, 0);
2648 return (rev);
2649 }
2650 }
2651
2652 if (follow && rdp->rd_next->rn_len != 0) {
2653 if (brev != NULL((void *)0) && !rcsnum_cmp(brev, rdp->rd_num, 0))
2654 break;
2655
2656 trdp = rcs_findrev(rfp, rdp->rd_next);
2657 if (trdp == NULL((void *)0))
2658 fatal("failed to grab next revision");
2659 rdp = trdp;
2660 } else
2661 follow = 0;
2662 } while (follow);
2663
2664 return (NULL((void *)0));
2665}