Bug Summary

File:src/usr.bin/cvs/rcs.c
Warning:line 164, column 2
Value stored to 'fmode' is never read

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;
Value stored to 'fmode' is never read
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);
1917
1918 if (!RCSNUM_ISBRANCHREV(frev)(!((frev)->rn_len % 2) && ((frev)->rn_len >=
4))
)
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)
;
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)
;
1935
1936 for (i = 0; (*alines)[i] != NULL((void *)0); i++) {
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))
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))
{
1951 for (i = 0; i < nextroot - 1; i++)
1952 if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1953 break;
1954 if (i == nextroot - 1)
1955 break;
1956 }
1957
1958 if (brp == NULL((void *)0))
1959 fatal("expected branch not found on branch list");
1960
1961 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL((void *)0))
1962 fatal("failed to get delta for target rev");
1963
1964 for (;;) {
1965 if (rdp->rd_next->rn_len != 0) {
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) {
1972 if (rcsparse_deltatexts(rfp, rdp->rd_num))
1973 fatal("rcs_annotate_getlines: "
1974 "rcsparse_deltatexts");
1975 if (rdp->rd_tlen == 0) {
1976 if (!rcsnum_differ(rdp->rd_num, bnum))
1977 break;
1978 rdp = trdp;
1979 continue;
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}