Annotation of src/usr.bin/cvs/rcs.c, Revision 1.256
1.256 ! tobias 1: /* $OpenBSD: rcs.c,v 1.255 2008/03/01 21:29:37 deraadt Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.15 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.15 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.15 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.15 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 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
1.15 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
1.210 otto 27: #include <sys/stat.h>
28:
29: #include <ctype.h>
30: #include <errno.h>
31: #include <libgen.h>
32: #include <pwd.h>
33: #include <stdlib.h>
34: #include <string.h>
35: #include <unistd.h>
1.1 jfb 36:
1.219 tobias 37: #include "atomicio.h"
1.94 joris 38: #include "cvs.h"
1.172 joris 39: #include "diff.h"
1.1 jfb 40: #include "rcs.h"
41:
1.57 xsa 42: #define RCS_BUFSIZE 16384
43: #define RCS_BUFEXTSIZE 8192
1.117 niallo 44: #define RCS_KWEXP_SIZE 1024
1.1 jfb 45:
46: /* RCS token types */
1.57 xsa 47: #define RCS_TOK_ERR -1
48: #define RCS_TOK_EOF 0
49: #define RCS_TOK_NUM 1
50: #define RCS_TOK_ID 2
51: #define RCS_TOK_STRING 3
52: #define RCS_TOK_SCOLON 4
53: #define RCS_TOK_COLON 5
54:
55: #define RCS_TOK_HEAD 8
56: #define RCS_TOK_BRANCH 9
57: #define RCS_TOK_ACCESS 10
58: #define RCS_TOK_SYMBOLS 11
59: #define RCS_TOK_LOCKS 12
60: #define RCS_TOK_COMMENT 13
61: #define RCS_TOK_EXPAND 14
62: #define RCS_TOK_DATE 15
63: #define RCS_TOK_AUTHOR 16
64: #define RCS_TOK_STATE 17
65: #define RCS_TOK_NEXT 18
66: #define RCS_TOK_BRANCHES 19
67: #define RCS_TOK_DESC 20
68: #define RCS_TOK_LOG 21
69: #define RCS_TOK_TEXT 22
70: #define RCS_TOK_STRICT 23
1.1 jfb 71:
1.57 xsa 72: #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
1.1 jfb 73:
1.57 xsa 74: #define RCS_NOSCOL 0x01 /* no terminating semi-colon */
75: #define RCS_VOPT 0x02 /* value is optional */
1.1 jfb 76:
1.218 tobias 77: #define ANNOTATE_NEVER 0
78: #define ANNOTATE_NOW 1
79: #define ANNOTATE_LATER 2
80:
1.1 jfb 81: /* opaque parse data */
82: struct rcs_pdata {
1.57 xsa 83: u_int rp_lines;
1.1 jfb 84:
1.57 xsa 85: char *rp_buf;
86: size_t rp_blen;
87: char *rp_bufend;
88: size_t rp_tlen;
1.1 jfb 89:
90: /* pushback token buffer */
1.57 xsa 91: char rp_ptok[128];
92: int rp_pttype; /* token type, RCS_TOK_ERR if no token */
1.1 jfb 93:
1.57 xsa 94: FILE *rp_file;
1.1 jfb 95: };
96:
1.57 xsa 97: #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
98: #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen
1.1 jfb 99:
1.47 jfb 100: /* invalid characters in RCS symbol names */
1.49 jfb 101: static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
1.47 jfb 102:
1.51 jfb 103: /* comment leaders, depending on the file's suffix */
104: static const struct rcs_comment {
1.57 xsa 105: const char *rc_suffix;
106: const char *rc_cstr;
1.51 jfb 107: } rcs_comments[] = {
108: { "1", ".\\\" " },
109: { "2", ".\\\" " },
110: { "3", ".\\\" " },
111: { "4", ".\\\" " },
112: { "5", ".\\\" " },
113: { "6", ".\\\" " },
114: { "7", ".\\\" " },
115: { "8", ".\\\" " },
116: { "9", ".\\\" " },
117: { "a", "-- " }, /* Ada */
118: { "ada", "-- " },
119: { "adb", "-- " },
120: { "asm", ";; " }, /* assembler (MS-DOS) */
121: { "ads", "-- " }, /* Ada */
122: { "bat", ":: " }, /* batch (MS-DOS) */
123: { "body", "-- " }, /* Ada */
124: { "c", " * " }, /* C */
125: { "c++", "// " }, /* C++ */
126: { "cc", "// " },
127: { "cpp", "// " },
128: { "cxx", "// " },
129: { "m", "// " }, /* Objective-C */
130: { "cl", ";;; " }, /* Common Lisp */
131: { "cmd", ":: " }, /* command (OS/2) */
132: { "cmf", "c " }, /* CM Fortran */
133: { "csh", "# " }, /* shell */
134: { "e", "# " }, /* efl */
135: { "epsf", "% " }, /* encapsulated postscript */
136: { "epsi", "% " }, /* encapsulated postscript */
137: { "el", "; " }, /* Emacs Lisp */
138: { "f", "c " }, /* Fortran */
139: { "for", "c " },
140: { "h", " * " }, /* C-header */
141: { "hh", "// " }, /* C++ header */
142: { "hpp", "// " },
143: { "hxx", "// " },
144: { "in", "# " }, /* for Makefile.in */
145: { "l", " * " }, /* lex */
146: { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
147: { "mak", "# " }, /* makefile, e.g. Visual C++ */
148: { "me", ".\\\" " }, /* me-macros t/nroff */
149: { "ml", "; " }, /* mocklisp */
150: { "mm", ".\\\" " }, /* mm-macros t/nroff */
151: { "ms", ".\\\" " }, /* ms-macros t/nroff */
152: { "man", ".\\\" " }, /* man-macros t/nroff */
153: { "p", " * " }, /* pascal */
154: { "pas", " * " },
1.52 jfb 155: { "pl", "# " }, /* Perl (conflict with Prolog) */
156: { "pm", "# " }, /* Perl module */
1.51 jfb 157: { "ps", "% " }, /* postscript */
158: { "psw", "% " }, /* postscript wrap */
159: { "pswm", "% " }, /* postscript wrap */
160: { "r", "# " }, /* ratfor */
161: { "rc", " * " }, /* Microsoft Windows resource file */
162: { "red", "% " }, /* psl/rlisp */
163: { "sh", "# " }, /* shell */
164: { "sl", "% " }, /* psl */
165: { "spec", "-- " }, /* Ada */
166: { "tex", "% " }, /* tex */
167: { "y", " * " }, /* yacc */
168: { "ye", " * " }, /* yacc-efl */
169: { "yr", " * " }, /* yacc-ratfor */
170: };
171:
1.128 niallo 172: struct rcs_kw rcs_expkw[] = {
173: { "Author", RCS_KW_AUTHOR },
174: { "Date", RCS_KW_DATE },
175: { "Header", RCS_KW_HEADER },
176: { "Id", RCS_KW_ID },
1.229 tobias 177: { "Locker", RCS_KW_LOCKER },
1.128 niallo 178: { "Log", RCS_KW_LOG },
179: { "Name", RCS_KW_NAME },
180: { "RCSfile", RCS_KW_RCSFILE },
181: { "Revision", RCS_KW_REVISION },
182: { "Source", RCS_KW_SOURCE },
183: { "State", RCS_KW_STATE },
1.211 niallo 184: { "Mdocdate", RCS_KW_MDOCDATE },
1.128 niallo 185: };
186:
1.57 xsa 187: #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0]))
1.51 jfb 188:
1.1 jfb 189: static struct rcs_key {
1.57 xsa 190: char rk_str[16];
191: int rk_id;
192: int rk_val;
193: int rk_flags;
1.1 jfb 194: } rcs_keys[] = {
195: { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT },
1.41 jfb 196: { "author", RCS_TOK_AUTHOR, RCS_TOK_ID, 0 },
1.1 jfb 197: { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT },
198: { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT },
199: { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT },
200: { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 },
201: { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL },
202: { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT },
203: { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT },
204: { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 },
205: { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL },
206: { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT },
1.41 jfb 207: { "state", RCS_TOK_STATE, RCS_TOK_ID, RCS_VOPT },
1.1 jfb 208: { "strict", RCS_TOK_STRICT, 0, 0, },
209: { "symbols", RCS_TOK_SYMBOLS, 0, 0 },
210: { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL },
211: };
212:
1.57 xsa 213: #define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0]))
1.1 jfb 214:
1.246 tobias 215: static RCSNUM *rcs_get_revision(const char *, RCSFILE *);
1.218 tobias 216: int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *,
217: struct cvs_line **, struct rcs_delta *);
1.167 ray 218: static void rcs_parse_init(RCSFILE *);
1.57 xsa 219: static int rcs_parse_admin(RCSFILE *);
220: static int rcs_parse_delta(RCSFILE *);
1.146 xsa 221: static void rcs_parse_deltas(RCSFILE *, RCSNUM *);
1.57 xsa 222: static int rcs_parse_deltatext(RCSFILE *);
1.146 xsa 223: static void rcs_parse_deltatexts(RCSFILE *, RCSNUM *);
1.148 xsa 224: static void rcs_parse_desc(RCSFILE *, RCSNUM *);
1.57 xsa 225:
226: static int rcs_parse_access(RCSFILE *);
227: static int rcs_parse_symbols(RCSFILE *);
228: static int rcs_parse_locks(RCSFILE *);
229: static int rcs_parse_branches(RCSFILE *, struct rcs_delta *);
230: static void rcs_freedelta(struct rcs_delta *);
231: static void rcs_freepdata(struct rcs_pdata *);
232: static int rcs_gettok(RCSFILE *);
233: static int rcs_pushtok(RCSFILE *, const char *, int);
1.151 xsa 234: static void rcs_growbuf(RCSFILE *);
1.155 ray 235: static void rcs_strprint(const u_char *, size_t, FILE *);
1.57 xsa 236:
1.233 tobias 237: static void rcs_kwexp_line(char *, struct rcs_delta *, struct cvs_lines *,
238: struct cvs_line *, int mode);
1.26 jfb 239:
1.60 xsa 240: RCSFILE *
1.172 joris 241: rcs_open(const char *path, int fd, int flags, ...)
1.1 jfb 242: {
1.172 joris 243: int mode;
1.228 tobias 244: mode_t fmode, mask;
1.1 jfb 245: RCSFILE *rfp;
1.26 jfb 246: va_list vap;
1.109 joris 247: struct rcs_delta *rdp;
248: struct rcs_lock *lkr;
1.26 jfb 249:
1.152 niallo 250: fmode = S_IRUSR|S_IRGRP|S_IROTH;
1.26 jfb 251: flags &= 0xffff; /* ditch any internal flags */
1.1 jfb 252:
1.172 joris 253: if (flags & RCS_CREATE) {
254: va_start(vap, flags);
255: mode = va_arg(vap, int);
256: va_end(vap);
257: fmode = (mode_t)mode;
1.1 jfb 258: }
1.228 tobias 259:
260: mask = umask(0);
261: umask(mask);
262: fmode &= ~mask;
1.1 jfb 263:
1.161 ray 264: rfp = xcalloc(1, sizeof(*rfp));
1.1 jfb 265:
1.110 joris 266: rfp->rf_path = xstrdup(path);
1.142 niallo 267: rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
1.26 jfb 268: rfp->rf_mode = fmode;
1.172 joris 269: rfp->fd = fd;
1.175 joris 270: rfp->rf_dead = 0;
1.1 jfb 271:
272: TAILQ_INIT(&(rfp->rf_delta));
1.29 jfb 273: TAILQ_INIT(&(rfp->rf_access));
1.1 jfb 274: TAILQ_INIT(&(rfp->rf_symbols));
275: TAILQ_INIT(&(rfp->rf_locks));
276:
1.167 ray 277: if (!(rfp->rf_flags & RCS_CREATE))
278: rcs_parse_init(rfp);
1.1 jfb 279:
1.109 joris 280: /* fill in rd_locker */
281: TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
282: if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) {
283: rcs_close(rfp);
284: return (NULL);
285: }
286:
1.110 joris 287: rdp->rd_locker = xstrdup(lkr->rl_name);
1.109 joris 288: }
289:
1.1 jfb 290: return (rfp);
291: }
292:
293: /*
294: * rcs_close()
295: *
296: * Close an RCS file handle.
297: */
298: void
299: rcs_close(RCSFILE *rfp)
300: {
301: struct rcs_delta *rdp;
1.71 moritz 302: struct rcs_access *rap;
1.13 jfb 303: struct rcs_lock *rlp;
304: struct rcs_sym *rsp;
1.1 jfb 305:
1.26 jfb 306: if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
307: rcs_write(rfp);
308:
1.1 jfb 309: while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
310: rdp = TAILQ_FIRST(&(rfp->rf_delta));
311: TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
312: rcs_freedelta(rdp);
1.71 moritz 313: }
314:
315: while (!TAILQ_EMPTY(&(rfp->rf_access))) {
316: rap = TAILQ_FIRST(&(rfp->rf_access));
317: TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list);
1.110 joris 318: xfree(rap->ra_name);
319: xfree(rap);
1.1 jfb 320: }
321:
1.13 jfb 322: while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
323: rsp = TAILQ_FIRST(&(rfp->rf_symbols));
324: TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
325: rcsnum_free(rsp->rs_num);
1.110 joris 326: xfree(rsp->rs_name);
327: xfree(rsp);
1.13 jfb 328: }
329:
330: while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
331: rlp = TAILQ_FIRST(&(rfp->rf_locks));
332: TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
333: rcsnum_free(rlp->rl_num);
1.110 joris 334: xfree(rlp->rl_name);
335: xfree(rlp);
1.13 jfb 336: }
337:
1.1 jfb 338: if (rfp->rf_head != NULL)
339: rcsnum_free(rfp->rf_head);
1.11 joris 340: if (rfp->rf_branch != NULL)
341: rcsnum_free(rfp->rf_branch);
1.1 jfb 342:
343: if (rfp->rf_path != NULL)
1.110 joris 344: xfree(rfp->rf_path);
1.1 jfb 345: if (rfp->rf_comment != NULL)
1.110 joris 346: xfree(rfp->rf_comment);
1.1 jfb 347: if (rfp->rf_expand != NULL)
1.110 joris 348: xfree(rfp->rf_expand);
1.1 jfb 349: if (rfp->rf_desc != NULL)
1.110 joris 350: xfree(rfp->rf_desc);
1.118 joris 351: if (rfp->rf_pdata != NULL)
352: rcs_freepdata(rfp->rf_pdata);
1.110 joris 353: xfree(rfp);
1.1 jfb 354: }
355:
356: /*
357: * rcs_write()
358: *
359: * Write the contents of the RCS file handle <rfp> to disk in the file whose
360: * path is in <rf_path>.
361: */
1.172 joris 362: void
1.1 jfb 363: rcs_write(RCSFILE *rfp)
364: {
365: FILE *fp;
1.214 xsa 366: char buf[1024], numbuf[CVS_REV_BUFSZ], *fn, tmpdir[MAXPATHLEN];
1.29 jfb 367: struct rcs_access *ap;
1.1 jfb 368: struct rcs_sym *symp;
1.46 jfb 369: struct rcs_branch *brp;
1.1 jfb 370: struct rcs_delta *rdp;
1.75 joris 371: struct rcs_lock *lkp;
1.100 xsa 372: size_t len;
1.207 otto 373: int fd, saved_errno;
1.75 joris 374:
1.207 otto 375: fd = -1;
1.1 jfb 376:
1.137 joris 377: if (rfp->rf_flags & RCS_SYNCED)
1.181 joris 378: return;
379:
380: if (cvs_noexec == 1)
1.172 joris 381: return;
1.137 joris 382:
1.117 niallo 383: /* Write operations need the whole file parsed */
384: rcs_parse_deltatexts(rfp, NULL);
1.1 jfb 385:
1.207 otto 386: if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
387: fatal("rcs_write: truncation");
388: (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
1.172 joris 389:
1.113 xsa 390: if ((fd = mkstemp(fn)) == -1)
1.172 joris 391: fatal("%s", fn);
1.113 xsa 392:
1.207 otto 393: if ((fp = fdopen(fd, "w")) == NULL) {
1.172 joris 394: saved_errno = errno;
395: (void)unlink(fn);
1.207 otto 396: fatal("fdopen %s: %s", fn, strerror(saved_errno));
1.1 jfb 397: }
398:
1.28 jfb 399: if (rfp->rf_head != NULL)
400: rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
401: else
402: numbuf[0] = '\0';
403:
1.1 jfb 404: fprintf(fp, "head\t%s;\n", numbuf);
1.35 jfb 405:
406: if (rfp->rf_branch != NULL) {
407: rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
408: fprintf(fp, "branch\t%s;\n", numbuf);
409: }
410:
1.29 jfb 411: fputs("access", fp);
412: TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
413: fprintf(fp, "\n\t%s", ap->ra_name);
414: }
415: fputs(";\n", fp);
1.1 jfb 416:
1.85 joris 417: fprintf(fp, "symbols");
1.1 jfb 418: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
1.243 tobias 419: if (RCSNUM_ISBRANCH(symp->rs_num))
420: rcsnum_addmagic(symp->rs_num);
1.1 jfb 421: rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
1.172 joris 422: if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) ||
423: strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) ||
424: strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf))
425: fatal("rcs_write: string overflow");
1.85 joris 426: fprintf(fp, "\n\t%s", buf);
1.1 jfb 427: }
428: fprintf(fp, ";\n");
429:
1.75 joris 430: fprintf(fp, "locks");
431: TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) {
432: rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
433: fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
434: }
435:
436: fprintf(fp, ";");
1.1 jfb 437:
1.26 jfb 438: if (rfp->rf_flags & RCS_SLOCK)
1.1 jfb 439: fprintf(fp, " strict;");
440: fputc('\n', fp);
441:
1.129 xsa 442: fputs("comment\t@", fp);
1.42 jfb 443: if (rfp->rf_comment != NULL) {
1.58 xsa 444: rcs_strprint((const u_char *)rfp->rf_comment,
445: strlen(rfp->rf_comment), fp);
1.42 jfb 446: fputs("@;\n", fp);
1.129 xsa 447: } else
448: fputs("# @;\n", fp);
1.1 jfb 449:
1.42 jfb 450: if (rfp->rf_expand != NULL) {
451: fputs("expand @", fp);
1.58 xsa 452: rcs_strprint((const u_char *)rfp->rf_expand,
453: strlen(rfp->rf_expand), fp);
1.42 jfb 454: fputs("@;\n", fp);
455: }
1.1 jfb 456:
1.46 jfb 457: fputs("\n\n", fp);
1.1 jfb 458:
459: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
460: fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
461: sizeof(numbuf)));
462: fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
1.44 jfb 463: rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
1.1 jfb 464: rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
465: rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
466: fprintf(fp, "\tauthor %s;\tstate %s;\n",
467: rdp->rd_author, rdp->rd_state);
1.46 jfb 468: fputs("branches", fp);
469: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.232 tobias 470: fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
1.46 jfb 471: sizeof(numbuf)));
472: }
473: fputs(";\n", fp);
1.1 jfb 474: fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
475: numbuf, sizeof(numbuf)));
476: }
477:
1.42 jfb 478: fputs("\ndesc\n@", fp);
1.141 ray 479: if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) {
1.100 xsa 480: rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
481: if (rfp->rf_desc[len-1] != '\n')
482: fputc('\n', fp);
483: }
484: fputs("@\n", fp);
1.1 jfb 485:
486: /* deltatexts */
487: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1.100 xsa 488: fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
1.1 jfb 489: sizeof(numbuf)));
1.42 jfb 490: fputs("log\n@", fp);
1.100 xsa 491: if (rdp->rd_log != NULL) {
492: len = strlen(rdp->rd_log);
493: rcs_strprint((const u_char *)rdp->rd_log, len, fp);
494: if (rdp->rd_log[len-1] != '\n')
495: fputc('\n', fp);
496: }
1.42 jfb 497: fputs("@\ntext\n@", fp);
1.100 xsa 498: if (rdp->rd_text != NULL) {
499: rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
500: }
501: fputs("@\n", fp);
1.1 jfb 502: }
1.207 otto 503: if (fchmod(fd, rfp->rf_mode) == -1) {
504: saved_errno = errno;
505: (void)unlink(fn);
506: fatal("fchmod %s: %s", fn, strerror(saved_errno));
507: }
508:
1.172 joris 509: (void)fclose(fp);
1.73 joris 510:
1.207 otto 511: if (rename(fn, rfp->rf_path) == -1) {
512: saved_errno = errno;
1.172 joris 513: (void)unlink(fn);
1.207 otto 514: fatal("rename(%s, %s): %s", fn, rfp->rf_path,
515: strerror(saved_errno));
1.172 joris 516: }
1.73 joris 517:
1.172 joris 518: rfp->rf_flags |= RCS_SYNCED;
1.73 joris 519:
1.172 joris 520: if (fn != NULL)
521: xfree(fn);
1.1 jfb 522: }
523:
524: /*
1.43 jfb 525: * rcs_head_get()
526: *
527: * Retrieve the revision number of the head revision for the RCS file <file>.
528: */
1.180 joris 529: RCSNUM *
1.43 jfb 530: rcs_head_get(RCSFILE *file)
531: {
1.213 niallo 532: struct rcs_branch *brp;
533: struct rcs_delta *rdp;
534: RCSNUM *rev, *rootrev;
1.180 joris 535:
1.234 tobias 536: if (file->rf_head == NULL)
537: return NULL;
538:
1.213 niallo 539: rev = rcsnum_alloc();
1.180 joris 540: if (file->rf_branch != NULL) {
1.213 niallo 541: /* we have a default branch, use that to calculate the
542: * real HEAD*/
543: rootrev = rcsnum_alloc();
544: rcsnum_cpy(file->rf_branch, rootrev, 2);
545: if ((rdp = rcs_findrev(file, rootrev)) == NULL)
546: fatal("rcs_head_get: could not find root revision");
547:
548: /* HEAD should be the last revision on the default branch */
549: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
550: if (TAILQ_NEXT(brp, rb_list) == NULL)
551: break;
552: }
553: rcsnum_free(rootrev);
1.226 tobias 554:
555: if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL)
556: fatal("rcs_head_get: could not find branch revision");
557: while (rdp->rd_next->rn_len != 0)
558: if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL)
559: fatal("rcs_head_get: could not find "
560: "next branch revision");
561:
562: rcsnum_cpy(rdp->rd_num, rev, 0);
1.195 joris 563: } else {
564: rcsnum_cpy(file->rf_head, rev, 0);
1.180 joris 565: }
566:
1.195 joris 567: return (rev);
1.43 jfb 568: }
569:
570: /*
571: * rcs_head_set()
572: *
573: * Set the revision number of the head revision for the RCS file <file> to
574: * <rev>, which must reference a valid revision within the file.
575: */
576: int
1.117 niallo 577: rcs_head_set(RCSFILE *file, RCSNUM *rev)
1.43 jfb 578: {
1.167 ray 579: if (rcs_findrev(file, rev) == NULL)
1.43 jfb 580: return (-1);
581:
1.111 joris 582: if (file->rf_head == NULL)
583: file->rf_head = rcsnum_alloc();
1.43 jfb 584:
1.111 joris 585: rcsnum_cpy(rev, file->rf_head, 0);
1.70 moritz 586: file->rf_flags &= ~RCS_SYNCED;
1.43 jfb 587: return (0);
588: }
589:
590:
591: /*
1.35 jfb 592: * rcs_branch_get()
593: *
594: * Retrieve the default branch number for the RCS file <file>.
595: * Returns the number on success. If NULL is returned, then there is no
596: * default branch for this file.
597: */
1.60 xsa 598: const RCSNUM *
1.35 jfb 599: rcs_branch_get(RCSFILE *file)
600: {
601: return (file->rf_branch);
602: }
603:
604: /*
605: * rcs_branch_set()
606: *
607: * Set the default branch for the RCS file <file> to <bnum>.
608: * Returns 0 on success, -1 on failure.
609: */
610: int
611: rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
612: {
1.111 joris 613: if (file->rf_branch == NULL)
614: file->rf_branch = rcsnum_alloc();
1.35 jfb 615:
1.111 joris 616: rcsnum_cpy(bnum, file->rf_branch, 0);
1.70 moritz 617: file->rf_flags &= ~RCS_SYNCED;
1.35 jfb 618: return (0);
619: }
620:
621: /*
1.29 jfb 622: * rcs_access_add()
623: *
624: * Add the login name <login> to the access list for the RCS file <file>.
625: * Returns 0 on success, or -1 on failure.
626: */
627: int
628: rcs_access_add(RCSFILE *file, const char *login)
629: {
630: struct rcs_access *ap;
631:
632: /* first look for duplication */
633: TAILQ_FOREACH(ap, &(file->rf_access), ra_list) {
1.254 joris 634: if (strcmp(ap->ra_name, login) == 0)
1.29 jfb 635: return (-1);
636: }
637:
1.161 ray 638: ap = xmalloc(sizeof(*ap));
1.110 joris 639: ap->ra_name = xstrdup(login);
1.29 jfb 640: TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list);
641:
642: /* not synced anymore */
643: file->rf_flags &= ~RCS_SYNCED;
644: return (0);
645: }
646:
647: /*
648: * rcs_access_remove()
649: *
650: * Remove an entry with login name <login> from the access list of the RCS
651: * file <file>.
652: * Returns 0 on success, or -1 on failure.
653: */
654: int
655: rcs_access_remove(RCSFILE *file, const char *login)
656: {
657: struct rcs_access *ap;
658:
659: TAILQ_FOREACH(ap, &(file->rf_access), ra_list)
660: if (strcmp(ap->ra_name, login) == 0)
661: break;
662:
1.254 joris 663: if (ap == NULL)
1.29 jfb 664: return (-1);
665:
666: TAILQ_REMOVE(&(file->rf_access), ap, ra_list);
1.110 joris 667: xfree(ap->ra_name);
668: xfree(ap);
1.29 jfb 669:
670: /* not synced anymore */
671: file->rf_flags &= ~RCS_SYNCED;
672: return (0);
673: }
674:
675: /*
1.26 jfb 676: * rcs_sym_add()
1.1 jfb 677: *
678: * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
679: * is named <sym> and is bound to the RCS revision <snum>.
680: */
681: int
1.26 jfb 682: rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
1.1 jfb 683: {
684: struct rcs_sym *symp;
685:
1.254 joris 686: if (!rcs_sym_check(sym))
1.69 moritz 687: return (-1);
1.47 jfb 688:
1.1 jfb 689: /* first look for duplication */
690: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
1.254 joris 691: if (strcmp(symp->rs_name, sym) == 0)
692: return (1);
1.1 jfb 693: }
694:
1.161 ray 695: symp = xmalloc(sizeof(*symp));
1.110 joris 696: symp->rs_name = xstrdup(sym);
1.111 joris 697: symp->rs_num = rcsnum_alloc();
1.1 jfb 698: rcsnum_cpy(snum, symp->rs_num, 0);
699:
700: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
701:
702: /* not synced anymore */
1.26 jfb 703: rfp->rf_flags &= ~RCS_SYNCED;
1.1 jfb 704: return (0);
705: }
706:
707: /*
1.27 jfb 708: * rcs_sym_remove()
709: *
710: * Remove the symbol with name <sym> from the symbol list for the RCS file
711: * <file>. If no such symbol is found, the call fails and returns with an
712: * error.
713: * Returns 0 on success, or -1 on failure.
714: */
715: int
716: rcs_sym_remove(RCSFILE *file, const char *sym)
717: {
718: struct rcs_sym *symp;
719:
1.254 joris 720: if (!rcs_sym_check(sym))
1.69 moritz 721: return (-1);
1.47 jfb 722:
1.27 jfb 723: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
724: if (strcmp(symp->rs_name, sym) == 0)
725: break;
726:
1.254 joris 727: if (symp == NULL)
1.27 jfb 728: return (-1);
729:
730: TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list);
1.110 joris 731: xfree(symp->rs_name);
1.27 jfb 732: rcsnum_free(symp->rs_num);
1.110 joris 733: xfree(symp);
1.27 jfb 734:
735: /* not synced anymore */
736: file->rf_flags &= ~RCS_SYNCED;
737: return (0);
1.184 xsa 738: }
739:
740: /*
741: * rcs_sym_get()
742: *
743: * Find a specific symbol <sym> entry in the tree of the RCS file <file>.
744: *
745: * Returns a pointer to the symbol on success, or NULL on failure.
746: */
747: struct rcs_sym *
748: rcs_sym_get(RCSFILE *file, const char *sym)
749: {
750: struct rcs_sym *symp;
751:
752: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
753: if (strcmp(symp->rs_name, sym) == 0)
754: return (symp);
755:
756: return (NULL);
1.27 jfb 757: }
758:
759: /*
760: * rcs_sym_getrev()
761: *
762: * Retrieve the RCS revision number associated with the symbol <sym> for the
763: * RCS file <file>. The returned value is a dynamically-allocated copy and
764: * should be freed by the caller once they are done with it.
765: * Returns the RCSNUM on success, or NULL on failure.
766: */
1.60 xsa 767: RCSNUM *
1.27 jfb 768: rcs_sym_getrev(RCSFILE *file, const char *sym)
769: {
770: RCSNUM *num;
771: struct rcs_sym *symp;
772:
1.254 joris 773: if (!rcs_sym_check(sym))
1.47 jfb 774: return (NULL);
775:
1.176 joris 776: if (!strcmp(sym, RCS_HEAD_BRANCH)) {
777: num = rcsnum_alloc();
778: rcsnum_cpy(file->rf_head, num, 0);
779: return (num);
780: }
781:
1.27 jfb 782: num = NULL;
783: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
784: if (strcmp(symp->rs_name, sym) == 0)
785: break;
786:
1.254 joris 787: if (symp != NULL) {
1.111 joris 788: num = rcsnum_alloc();
789: rcsnum_cpy(symp->rs_num, num, 0);
1.27 jfb 790: }
791:
792: return (num);
1.47 jfb 793: }
794:
795: /*
796: * rcs_sym_check()
797: *
798: * Check the RCS symbol name <sym> for any unsupported characters.
799: * Returns 1 if the tag is correct, 0 if it isn't valid.
800: */
801: int
802: rcs_sym_check(const char *sym)
803: {
804: int ret;
805: const char *cp;
806:
807: ret = 1;
808: cp = sym;
809: if (!isalpha(*cp++))
810: return (0);
811:
812: for (; *cp != '\0'; cp++)
813: if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) {
814: ret = 0;
815: break;
816: }
817:
818: return (ret);
1.30 jfb 819: }
820:
821: /*
822: * rcs_lock_getmode()
823: *
824: * Retrieve the locking mode of the RCS file <file>.
825: */
826: int
827: rcs_lock_getmode(RCSFILE *file)
828: {
829: return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE;
830: }
831:
832: /*
833: * rcs_lock_setmode()
834: *
835: * Set the locking mode of the RCS file <file> to <mode>, which must either
836: * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
837: * Returns the previous mode on success, or -1 on failure.
838: */
839: int
840: rcs_lock_setmode(RCSFILE *file, int mode)
841: {
842: int pmode;
843: pmode = rcs_lock_getmode(file);
844:
845: if (mode == RCS_LOCK_STRICT)
846: file->rf_flags |= RCS_SLOCK;
847: else if (mode == RCS_LOCK_LOOSE)
848: file->rf_flags &= ~RCS_SLOCK;
1.145 xsa 849: else
850: fatal("rcs_lock_setmode: invalid mode `%d'", mode);
1.30 jfb 851:
1.70 moritz 852: file->rf_flags &= ~RCS_SYNCED;
1.30 jfb 853: return (pmode);
1.27 jfb 854: }
855:
856: /*
1.40 jfb 857: * rcs_lock_add()
858: *
859: * Add an RCS lock for the user <user> on revision <rev>.
860: * Returns 0 on success, or -1 on failure.
861: */
862: int
863: rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
864: {
865: struct rcs_lock *lkp;
866:
867: /* first look for duplication */
868: TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
1.168 deraadt 869: if (strcmp(lkp->rl_name, user) == 0 &&
1.254 joris 870: rcsnum_cmp(rev, lkp->rl_num, 0) == 0)
1.40 jfb 871: return (-1);
872: }
873:
1.161 ray 874: lkp = xmalloc(sizeof(*lkp));
1.110 joris 875: lkp->rl_name = xstrdup(user);
1.111 joris 876: lkp->rl_num = rcsnum_alloc();
1.68 moritz 877: rcsnum_cpy(rev, lkp->rl_num, 0);
1.40 jfb 878:
879: TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list);
880:
881: /* not synced anymore */
882: file->rf_flags &= ~RCS_SYNCED;
883: return (0);
884: }
885:
886:
887: /*
888: * rcs_lock_remove()
889: *
890: * Remove the RCS lock on revision <rev>.
891: * Returns 0 on success, or -1 on failure.
892: */
893: int
1.109 joris 894: rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
1.40 jfb 895: {
896: struct rcs_lock *lkp;
897:
1.109 joris 898: TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
1.168 deraadt 899: if (strcmp(lkp->rl_name, user) == 0 &&
900: rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
1.40 jfb 901: break;
1.109 joris 902: }
1.40 jfb 903:
1.254 joris 904: if (lkp == NULL)
1.40 jfb 905: return (-1);
906:
907: TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list);
908: rcsnum_free(lkp->rl_num);
1.110 joris 909: xfree(lkp->rl_name);
910: xfree(lkp);
1.40 jfb 911:
912: /* not synced anymore */
913: file->rf_flags &= ~RCS_SYNCED;
914: return (0);
915: }
916:
917: /*
1.27 jfb 918: * rcs_desc_get()
919: *
920: * Retrieve the description for the RCS file <file>.
921: */
1.57 xsa 922: const char *
1.27 jfb 923: rcs_desc_get(RCSFILE *file)
924: {
925: return (file->rf_desc);
926: }
927:
928: /*
929: * rcs_desc_set()
930: *
931: * Set the description for the RCS file <file>.
932: */
1.149 xsa 933: void
1.27 jfb 934: rcs_desc_set(RCSFILE *file, const char *desc)
935: {
936: char *tmp;
937:
1.110 joris 938: tmp = xstrdup(desc);
1.27 jfb 939: if (file->rf_desc != NULL)
1.110 joris 940: xfree(file->rf_desc);
1.27 jfb 941: file->rf_desc = tmp;
942: file->rf_flags &= ~RCS_SYNCED;
1.51 jfb 943: }
944:
945: /*
946: * rcs_comment_lookup()
947: *
948: * Lookup the assumed comment leader based on a file's suffix.
949: * Returns a pointer to the string on success, or NULL on failure.
950: */
1.57 xsa 951: const char *
1.51 jfb 952: rcs_comment_lookup(const char *filename)
953: {
954: int i;
955: const char *sp;
956:
1.254 joris 957: if ((sp = strrchr(filename, '.')) == NULL)
1.51 jfb 958: return (NULL);
959: sp++;
960:
961: for (i = 0; i < (int)NB_COMTYPES; i++)
962: if (strcmp(rcs_comments[i].rc_suffix, sp) == 0)
963: return (rcs_comments[i].rc_cstr);
964: return (NULL);
1.27 jfb 965: }
966:
1.33 jfb 967: /*
968: * rcs_comment_get()
969: *
970: * Retrieve the comment leader for the RCS file <file>.
971: */
1.57 xsa 972: const char *
1.33 jfb 973: rcs_comment_get(RCSFILE *file)
974: {
975: return (file->rf_comment);
976: }
977:
978: /*
979: * rcs_comment_set()
980: *
981: * Set the comment leader for the RCS file <file>.
982: */
1.150 xsa 983: void
1.33 jfb 984: rcs_comment_set(RCSFILE *file, const char *comment)
985: {
986: char *tmp;
987:
1.110 joris 988: tmp = xstrdup(comment);
1.33 jfb 989: if (file->rf_comment != NULL)
1.110 joris 990: xfree(file->rf_comment);
1.33 jfb 991: file->rf_comment = tmp;
992: file->rf_flags &= ~RCS_SYNCED;
1.40 jfb 993: }
994:
1.94 joris 995: int
1.218 tobias 996: rcs_patch_lines(struct cvs_lines *dlines, struct cvs_lines *plines,
997: struct cvs_line **alines, struct rcs_delta *rdp)
1.5 vincent 998: {
1.209 otto 999: u_char op;
1000: char *ep;
1.94 joris 1001: struct cvs_line *lp, *dlp, *ndlp;
1.5 vincent 1002: int i, lineno, nbln;
1.193 niallo 1003: u_char tmp;
1.1 jfb 1004:
1.94 joris 1005: dlp = TAILQ_FIRST(&(dlines->l_lines));
1006: lp = TAILQ_FIRST(&(plines->l_lines));
1.1 jfb 1007:
1008: /* skip first bogus line */
1.94 joris 1009: for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1010: lp = TAILQ_NEXT(lp, l_list)) {
1.193 niallo 1011: if (lp->l_len < 2)
1012: fatal("line too short, RCS patch seems broken");
1.94 joris 1013: op = *(lp->l_line);
1.193 niallo 1014: /* NUL-terminate line buffer for strtol() safety. */
1015: tmp = lp->l_line[lp->l_len - 1];
1016: lp->l_line[lp->l_len - 1] = '\0';
1.209 otto 1017: lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
1.193 niallo 1018: if (lineno - 1 > dlines->l_nblines || lineno < 0) {
1019: fatal("invalid line specification in RCS patch");
1020: }
1.1 jfb 1021: ep++;
1022: nbln = (int)strtol(ep, &ep, 10);
1.193 niallo 1023: /* Restore the last byte of the buffer */
1024: lp->l_line[lp->l_len - 1] = tmp;
1025: if (nbln < 0)
1.132 niallo 1026: fatal("invalid line number specification in RCS patch");
1.1 jfb 1027:
1028: /* find the appropriate line */
1029: for (;;) {
1030: if (dlp == NULL)
1031: break;
1.94 joris 1032: if (dlp->l_lineno == lineno)
1.1 jfb 1033: break;
1.94 joris 1034: if (dlp->l_lineno > lineno) {
1035: dlp = TAILQ_PREV(dlp, cvs_tqh, l_list);
1036: } else if (dlp->l_lineno < lineno) {
1.133 niallo 1037: if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) ||
1.168 deraadt 1038: ndlp->l_lineno > lineno)
1.1 jfb 1039: break;
1040: dlp = ndlp;
1041: }
1042: }
1.132 niallo 1043: if (dlp == NULL)
1044: fatal("can't find referenced line in RCS patch");
1.1 jfb 1045:
1046: if (op == 'd') {
1047: for (i = 0; (i < nbln) && (dlp != NULL); i++) {
1.94 joris 1048: ndlp = TAILQ_NEXT(dlp, l_list);
1049: TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
1.218 tobias 1050: if (alines != NULL && dlp->l_line != NULL) {
1051: dlp->l_delta = rdp;
1052: alines[dlp->l_lineno_orig - 1] =
1053: dlp;
1054: } else
1055: xfree(dlp);
1.1 jfb 1056: dlp = ndlp;
1.133 niallo 1057: /* last line is gone - reset dlp */
1058: if (dlp == NULL) {
1059: ndlp = TAILQ_LAST(&(dlines->l_lines),
1060: cvs_tqh);
1061: dlp = ndlp;
1062: }
1.1 jfb 1063: }
1.14 deraadt 1064: } else if (op == 'a') {
1.1 jfb 1065: for (i = 0; i < nbln; i++) {
1066: ndlp = lp;
1.94 joris 1067: lp = TAILQ_NEXT(lp, l_list);
1.132 niallo 1068: if (lp == NULL)
1069: fatal("truncated RCS patch");
1.94 joris 1070: TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
1.218 tobias 1071: if (alines != NULL) {
1072: if (lp->l_needsfree == 1)
1073: xfree(lp->l_line);
1074: lp->l_line = NULL;
1075: lp->l_needsfree = 0;
1076: }
1.225 tobias 1077: lp->l_delta = rdp;
1.94 joris 1078: TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
1079: lp, l_list);
1.1 jfb 1080: dlp = lp;
1081:
1082: /* we don't want lookup to block on those */
1.94 joris 1083: lp->l_lineno = lineno;
1.1 jfb 1084:
1085: lp = ndlp;
1086: }
1.132 niallo 1087: } else
1088: fatal("unknown RCS patch operation `%c'", op);
1.1 jfb 1089:
1090: /* last line of the patch, done */
1.94 joris 1091: if (lp->l_lineno == plines->l_nblines)
1.1 jfb 1092: break;
1093: }
1094:
1095: /* once we're done patching, rebuild the line numbers */
1.2 vincent 1096: lineno = 0;
1.94 joris 1097: TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
1098: lp->l_lineno = lineno++;
1099: dlines->l_nblines = lineno - 1;
1.1 jfb 1100:
1.5 vincent 1101: return (0);
1.241 joris 1102: }
1103:
1104: void
1105: rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
1106: {
1107: struct cvs_lines *plines;
1108: struct cvs_line *lp;
1109: int added, i, lineno, nbln, removed;
1110: char op, *ep;
1111: u_char tmp;
1112:
1113: added = removed = 0;
1114:
1115: plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
1116: lp = TAILQ_FIRST(&(plines->l_lines));
1117:
1118: /* skip first bogus line */
1119: for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1120: lp = TAILQ_NEXT(lp, l_list)) {
1121: if (lp->l_len < 2)
1122: fatal("line too short, RCS patch seems broken");
1123: op = *(lp->l_line);
1124: /* NUL-terminate line buffer for strtol() safety. */
1125: tmp = lp->l_line[lp->l_len - 1];
1126: lp->l_line[lp->l_len - 1] = '\0';
1127: lineno = (int)strtol((lp->l_line + 1), &ep, 10);
1128: ep++;
1129: nbln = (int)strtol(ep, &ep, 10);
1130: /* Restore the last byte of the buffer */
1131: lp->l_line[lp->l_len - 1] = tmp;
1132: if (nbln < 0)
1133: fatal("invalid line number specification in RCS patch");
1134:
1135: if (op == 'a') {
1136: added += nbln;
1137: for (i = 0; i < nbln; i++) {
1138: lp = TAILQ_NEXT(lp, l_list);
1139: if (lp == NULL)
1140: fatal("truncated RCS patch");
1141: }
1142: }
1143: else if (op == 'd')
1144: removed += nbln;
1145: else
1146: fatal("unknown RCS patch operation '%c'", op);
1147: }
1148:
1149: *ladded = added;
1150: *lremoved = removed;
1.1 jfb 1151: }
1152:
1153: /*
1.52 jfb 1154: * rcs_rev_add()
1155: *
1.53 jfb 1156: * Add a revision to the RCS file <rf>. The new revision's number can be
1157: * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
1158: * new revision will have a number equal to the previous head revision plus
1159: * one). The <msg> argument specifies the log message for that revision, and
1160: * <date> specifies the revision's date (a value of -1 is
1161: * equivalent to using the current time).
1.96 xsa 1162: * If <username> is NULL, set the author for this revision to the current user.
1.90 niallo 1163: * Otherwise, set it to <username>.
1.52 jfb 1164: * Returns 0 on success, or -1 on failure.
1165: */
1166: int
1.90 niallo 1167: rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1168: const char *username)
1.52 jfb 1169: {
1170: time_t now;
1.222 joris 1171: RCSNUM *root;
1.52 jfb 1172: struct passwd *pw;
1.180 joris 1173: struct rcs_branch *brp;
1.83 joris 1174: struct rcs_delta *ordp, *rdp;
1175:
1.52 jfb 1176: if (rev == RCS_HEAD_REV) {
1.101 niallo 1177: if (rf->rf_flags & RCS_CREATE) {
1178: if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
1179: return (-1);
1.111 joris 1180: rf->rf_head = rcsnum_alloc();
1.101 niallo 1181: rcsnum_cpy(rev, rf->rf_head, 0);
1182: } else {
1183: rev = rcsnum_inc(rf->rf_head);
1184: }
1.83 joris 1185: } else {
1.254 joris 1186: if ((rdp = rcs_findrev(rf, rev)) != NULL)
1.83 joris 1187: return (-1);
1.52 jfb 1188: }
1189:
1.112 xsa 1190: if ((pw = getpwuid(getuid())) == NULL)
1191: fatal("getpwuid failed");
1.52 jfb 1192:
1.161 ray 1193: rdp = xcalloc(1, sizeof(*rdp));
1.52 jfb 1194:
1195: TAILQ_INIT(&(rdp->rd_branches));
1196:
1.111 joris 1197: rdp->rd_num = rcsnum_alloc();
1.52 jfb 1198: rcsnum_cpy(rev, rdp->rd_num, 0);
1.83 joris 1199:
1.111 joris 1200: rdp->rd_next = rcsnum_alloc();
1.92 joris 1201:
1.90 niallo 1202: if (username == NULL)
1203: username = pw->pw_name;
1204:
1.110 joris 1205: rdp->rd_author = xstrdup(username);
1206: rdp->rd_state = xstrdup(RCS_STATE_EXP);
1207: rdp->rd_log = xstrdup(msg);
1.52 jfb 1208:
1.53 jfb 1209: if (date != (time_t)(-1))
1210: now = date;
1211: else
1212: time(&now);
1.52 jfb 1213: gmtime_r(&now, &(rdp->rd_date));
1214:
1.180 joris 1215: if (RCSNUM_ISBRANCHREV(rev))
1216: TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
1217: else
1218: TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1.52 jfb 1219: rf->rf_ndelta++;
1.81 niallo 1220:
1.180 joris 1221: if (!(rf->rf_flags & RCS_CREATE)) {
1222: if (RCSNUM_ISBRANCHREV(rev)) {
1.222 joris 1223: root = rcsnum_branch_root(rev);
1.180 joris 1224: brp = xmalloc(sizeof(*brp));
1225: brp->rb_num = rcsnum_alloc();
1226: rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1.222 joris 1227: if ((ordp = rcs_findrev(rf, root)) == NULL)
1228: fatal("root node not found");
1229: if (TAILQ_EMPTY(&(ordp->rd_branches))) {
1230: TAILQ_INSERT_TAIL(&(ordp->rd_branches),
1231: brp, rb_list);
1232: }
1233:
1.208 otto 1234: ordp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
1.222 joris 1235: if (RCSNUM_ISBRANCHREV(ordp->rd_num))
1236: rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1.180 joris 1237: } else {
1238: ordp = TAILQ_NEXT(rdp, rd_list);
1239: rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1240: }
1241: }
1242:
1.64 niallo 1243: /* not synced anymore */
1244: rf->rf_flags &= ~RCS_SYNCED;
1.52 jfb 1245:
1246: return (0);
1247: }
1248:
1249: /*
1250: * rcs_rev_remove()
1251: *
1252: * Remove the revision whose number is <rev> from the RCS file <rf>.
1253: */
1254: int
1255: rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1256: {
1.251 joris 1257: int fd1, fd2;
1.194 joris 1258: char *path_tmp1, *path_tmp2;
1.166 joris 1259: struct rcs_delta *rdp, *prevrdp, *nextrdp;
1.209 otto 1260: BUF *prevbuf, *newdiff, *newdeltatext;
1.166 joris 1261:
1.52 jfb 1262: if (rev == RCS_HEAD_REV)
1263: rev = rf->rf_head;
1264:
1265: /* do we actually have that revision? */
1.254 joris 1266: if ((rdp = rcs_findrev(rf, rev)) == NULL)
1.166 joris 1267: return (-1);
1268:
1269: /*
1270: * This is confusing, the previous delta is next in the TAILQ list.
1271: * the next delta is the previous one in the TAILQ list.
1272: *
1273: * When the HEAD revision got specified, nextrdp will be NULL.
1274: * When the first revision got specified, prevrdp will be NULL.
1275: */
1276: prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list);
1277: nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, cvs_tqh, rd_list);
1278:
1279: newdeltatext = NULL;
1.209 otto 1280: prevbuf = NULL;
1.166 joris 1281:
1.168 deraadt 1282: if (prevrdp != NULL && nextrdp != NULL) {
1.249 tobias 1283: newdiff = cvs_buf_alloc(64);
1.166 joris 1284:
1285: /* calculate new diff */
1.172 joris 1286: (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
1.251 joris 1287: fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1.166 joris 1288:
1.172 joris 1289: (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
1.251 joris 1290: fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1.166 joris 1291:
1292: diff_format = D_RCSDIFF;
1.251 joris 1293: if (cvs_diffreg(path_tmp1, path_tmp2,
1294: fd1, fd2, newdiff) == D_ERROR)
1.172 joris 1295: fatal("rcs_diffreg failed");
1.166 joris 1296:
1.251 joris 1297: close(fd1);
1298: close(fd2);
1299:
1.194 joris 1300: newdeltatext = newdiff;
1.168 deraadt 1301: } else if (nextrdp == NULL && prevrdp != NULL) {
1.194 joris 1302: newdeltatext = prevbuf;
1.166 joris 1303: }
1304:
1305: if (newdeltatext != NULL) {
1306: if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1307: fatal("error setting new deltatext");
1308: }
1309:
1310: TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1311:
1312: /* update pointers */
1.168 deraadt 1313: if (prevrdp != NULL && nextrdp != NULL) {
1.166 joris 1314: rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1315: } else if (prevrdp != NULL) {
1.170 xsa 1316: if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1317: fatal("rcs_head_set failed");
1.166 joris 1318: } else if (nextrdp != NULL) {
1319: rcsnum_free(nextrdp->rd_next);
1320: nextrdp->rd_next = rcsnum_alloc();
1.52 jfb 1321: } else {
1.166 joris 1322: rcsnum_free(rf->rf_head);
1323: rf->rf_head = NULL;
1.52 jfb 1324: }
1325:
1.166 joris 1326: rf->rf_ndelta--;
1327: rf->rf_flags &= ~RCS_SYNCED;
1328:
1329: rcs_freedelta(rdp);
1330:
1331: if (newdeltatext != NULL)
1332: xfree(newdeltatext);
1.52 jfb 1333:
1.172 joris 1334: if (path_tmp1 != NULL)
1335: xfree(path_tmp1);
1336: if (path_tmp2 != NULL)
1337: xfree(path_tmp2);
1338:
1.166 joris 1339: return (0);
1.52 jfb 1340: }
1341:
1342: /*
1.1 jfb 1343: * rcs_findrev()
1344: *
1345: * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1346: * The revision number is given in <rev>.
1.156 joris 1347: *
1.1 jfb 1348: * Returns a pointer to the delta on success, or NULL on failure.
1349: */
1.102 xsa 1350: struct rcs_delta *
1.117 niallo 1351: rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1.1 jfb 1352: {
1.178 joris 1353: int isbrev;
1.156 joris 1354: struct rcs_delta *rdp;
1.178 joris 1355:
1356: isbrev = RCSNUM_ISBRANCHREV(rev);
1.26 jfb 1357:
1.117 niallo 1358: /*
1359: * We need to do more parsing if the last revision in the linked list
1360: * is greater than the requested revision.
1361: */
1.156 joris 1362: rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1.168 deraadt 1363: if (rdp == NULL ||
1.178 joris 1364: (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1365: ((isbrev && rdp->rd_num->rn_len < 4) ||
1366: (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1.117 niallo 1367: rcs_parse_deltas(rfp, rev);
1368: }
1369:
1.156 joris 1370: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1.178 joris 1371: if (rcsnum_differ(rdp->rd_num, rev))
1372: continue;
1373: else
1.156 joris 1374: return (rdp);
1375: }
1.1 jfb 1376:
1377: return (NULL);
1.20 jfb 1378: }
1379:
1380: /*
1.26 jfb 1381: * rcs_kwexp_set()
1382: *
1383: * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1384: */
1.162 xsa 1385: void
1.26 jfb 1386: rcs_kwexp_set(RCSFILE *file, int mode)
1387: {
1388: int i;
1389: char *tmp, buf[8] = "";
1390:
1391: if (RCS_KWEXP_INVAL(mode))
1.162 xsa 1392: return;
1.26 jfb 1393:
1394: i = 0;
1395: if (mode == RCS_KWEXP_NONE)
1396: buf[0] = 'b';
1397: else if (mode == RCS_KWEXP_OLD)
1398: buf[0] = 'o';
1399: else {
1400: if (mode & RCS_KWEXP_NAME)
1401: buf[i++] = 'k';
1402: if (mode & RCS_KWEXP_VAL)
1403: buf[i++] = 'v';
1404: if (mode & RCS_KWEXP_LKR)
1405: buf[i++] = 'l';
1406: }
1407:
1.110 joris 1408: tmp = xstrdup(buf);
1.27 jfb 1409: if (file->rf_expand != NULL)
1.147 ray 1410: xfree(file->rf_expand);
1.26 jfb 1411: file->rf_expand = tmp;
1.64 niallo 1412: /* not synced anymore */
1413: file->rf_flags &= ~RCS_SYNCED;
1.26 jfb 1414: }
1415:
1416: /*
1417: * rcs_kwexp_get()
1418: *
1419: * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1420: */
1421: int
1422: rcs_kwexp_get(RCSFILE *file)
1423: {
1424: return rcs_kflag_get(file->rf_expand);
1425: }
1426:
1427: /*
1.20 jfb 1428: * rcs_kflag_get()
1429: *
1430: * Get the keyword expansion mode from a set of character flags given in
1431: * <flags> and return the appropriate flag mask. In case of an error, the
1432: * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1433: */
1434: int
1435: rcs_kflag_get(const char *flags)
1436: {
1437: int fl;
1438: size_t len;
1439: const char *fp;
1.253 tobias 1440:
1441: if (flags == NULL)
1442: return 0;
1.20 jfb 1443:
1444: fl = 0;
1.235 tobias 1445: if (!(len = strlen(flags)))
1446: return RCS_KWEXP_ERR;
1.20 jfb 1447:
1448: for (fp = flags; *fp != '\0'; fp++) {
1449: if (*fp == 'k')
1450: fl |= RCS_KWEXP_NAME;
1451: else if (*fp == 'v')
1452: fl |= RCS_KWEXP_VAL;
1453: else if (*fp == 'l')
1454: fl |= RCS_KWEXP_LKR;
1455: else if (*fp == 'o') {
1456: if (len != 1)
1457: fl |= RCS_KWEXP_ERR;
1458: fl |= RCS_KWEXP_OLD;
1459: } else if (*fp == 'b') {
1460: if (len != 1)
1461: fl |= RCS_KWEXP_ERR;
1.205 xsa 1462: fl |= RCS_KWEXP_NONE;
1.20 jfb 1463: } else /* unknown letter */
1464: fl |= RCS_KWEXP_ERR;
1465: }
1466:
1467: return (fl);
1.1 jfb 1468: }
1469:
1.117 niallo 1470: /* rcs_parse_deltas()
1471: *
1472: * Parse deltas. If <rev> is not NULL, parse only as far as that
1473: * revision. If <rev> is NULL, parse all deltas.
1474: */
1.146 xsa 1475: static void
1.117 niallo 1476: rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev)
1477: {
1478: int ret;
1479: struct rcs_delta *enddelta;
1.146 xsa 1480:
1481: if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
1482: return;
1483:
1.117 niallo 1484: for (;;) {
1485: ret = rcs_parse_delta(rfp);
1486: if (rev != NULL) {
1487: enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1.155 ray 1488: if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
1.117 niallo 1489: break;
1490: }
1.180 joris 1491:
1.117 niallo 1492: if (ret == 0) {
1493: rfp->rf_flags |= PARSED_DELTAS;
1494: break;
1495: }
1496: else if (ret == -1)
1497: fatal("error parsing deltas");
1498: }
1499: }
1500:
1501: /* rcs_parse_deltatexts()
1502: *
1503: * Parse deltatexts. If <rev> is not NULL, parse only as far as that
1504: * revision. If <rev> is NULL, parse everything.
1505: */
1.146 xsa 1506: static void
1.117 niallo 1507: rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
1508: {
1509: int ret;
1510: struct rcs_delta *rdp;
1.146 xsa 1511:
1.168 deraadt 1512: if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
1513: (rfp->rf_flags & RCS_CREATE))
1.146 xsa 1514: return;
1515:
1.117 niallo 1516: if (!(rfp->rf_flags & PARSED_DESC))
1517: rcs_parse_desc(rfp, rev);
1518: for (;;) {
1519: if (rev != NULL) {
1520: rdp = rcs_findrev(rfp, rev);
1521: if (rdp->rd_text != NULL)
1522: break;
1523: else
1524: ret = rcs_parse_deltatext(rfp);
1525: } else
1526: ret = rcs_parse_deltatext(rfp);
1527: if (ret == 0) {
1528: rfp->rf_flags |= PARSED_DELTATEXTS;
1529: break;
1530: }
1.146 xsa 1531: else if (ret == -1)
1.117 niallo 1532: fatal("problem parsing deltatexts");
1533: }
1534: }
1535:
1536: /* rcs_parse_desc()
1537: *
1538: * Parse RCS description.
1539: */
1.148 xsa 1540: static void
1.117 niallo 1541: rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev)
1542: {
1543: int ret = 0;
1.148 xsa 1544:
1545: if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE))
1546: return;
1.122 reyk 1547: if (!(rfp->rf_flags & PARSED_DELTAS))
1.117 niallo 1548: rcs_parse_deltas(rfp, rev);
1549: /* do parsing */
1550: ret = rcs_gettok(rfp);
1.148 xsa 1551: if (ret != RCS_TOK_DESC)
1552: fatal("token `%s' found where RCS desc expected",
1.117 niallo 1553: RCS_TOKSTR(rfp));
1554:
1555: ret = rcs_gettok(rfp);
1.148 xsa 1556: if (ret != RCS_TOK_STRING)
1557: fatal("token `%s' found where RCS desc expected",
1.117 niallo 1558: RCS_TOKSTR(rfp));
1559:
1560: rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp));
1561: rfp->rf_flags |= PARSED_DESC;
1562: }
1563:
1.1 jfb 1564: /*
1.117 niallo 1565: * rcs_parse_init()
1.1 jfb 1566: *
1.117 niallo 1567: * Initial parsing of file <path>, which are in the RCS format.
1.167 ray 1568: * Just does admin section.
1.1 jfb 1569: */
1.167 ray 1570: static void
1.117 niallo 1571: rcs_parse_init(RCSFILE *rfp)
1.1 jfb 1572: {
1573: struct rcs_pdata *pdp;
1574:
1.26 jfb 1575: if (rfp->rf_flags & RCS_PARSED)
1.167 ray 1576: return;
1.1 jfb 1577:
1.161 ray 1578: pdp = xcalloc(1, sizeof(*pdp));
1.1 jfb 1579:
1.18 jfb 1580: pdp->rp_lines = 0;
1.1 jfb 1581: pdp->rp_pttype = RCS_TOK_ERR;
1582:
1.172 joris 1583: if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL)
1.216 xsa 1584: fatal("fdopen: `%s'", rfp->rf_path);
1.1 jfb 1585:
1.161 ray 1586: pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE);
1.1 jfb 1587: pdp->rp_blen = RCS_BUFSIZE;
1.18 jfb 1588: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.1 jfb 1589:
1590: /* ditch the strict lock */
1.26 jfb 1591: rfp->rf_flags &= ~RCS_SLOCK;
1.1 jfb 1592: rfp->rf_pdata = pdp;
1593:
1.167 ray 1594: if (rcs_parse_admin(rfp) < 0) {
1.1 jfb 1595: rcs_freepdata(pdp);
1.117 niallo 1596: fatal("could not parse admin data");
1.1 jfb 1597: }
1598:
1.117 niallo 1599: if (rfp->rf_flags & RCS_PARSE_FULLY)
1600: rcs_parse_deltatexts(rfp, NULL);
1.1 jfb 1601:
1.117 niallo 1602: rfp->rf_flags |= RCS_SYNCED;
1.1 jfb 1603: }
1604:
1605: /*
1606: * rcs_parse_admin()
1607: *
1608: * Parse the administrative portion of an RCS file.
1.31 jfb 1609: * Returns the type of the first token found after the admin section on
1610: * success, or -1 on failure.
1.1 jfb 1611: */
1612: static int
1613: rcs_parse_admin(RCSFILE *rfp)
1614: {
1615: u_int i;
1616: int tok, ntok, hmask;
1617: struct rcs_key *rk;
1618:
1.180 joris 1619: rfp->rf_head = NULL;
1620: rfp->rf_branch = NULL;
1621:
1.1 jfb 1622: /* hmask is a mask of the headers already encountered */
1623: hmask = 0;
1624: for (;;) {
1625: tok = rcs_gettok(rfp);
1626: if (tok == RCS_TOK_ERR) {
1627: cvs_log(LP_ERR, "parse error in RCS admin section");
1.147 ray 1628: goto fail;
1.168 deraadt 1629: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.31 jfb 1630: /*
1631: * Assume this is the start of the first delta or
1632: * that we are dealing with an empty RCS file and
1633: * we just found the description.
1634: */
1.1 jfb 1635: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.31 jfb 1636: return (tok);
1.1 jfb 1637: }
1638:
1639: rk = NULL;
1.18 jfb 1640: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1641: if (rcs_keys[i].rk_id == tok)
1642: rk = &(rcs_keys[i]);
1643:
1644: if (hmask & (1 << tok)) {
1645: cvs_log(LP_ERR, "duplicate RCS key");
1.147 ray 1646: goto fail;
1.1 jfb 1647: }
1648: hmask |= (1 << tok);
1649:
1650: switch (tok) {
1651: case RCS_TOK_HEAD:
1652: case RCS_TOK_BRANCH:
1653: case RCS_TOK_COMMENT:
1654: case RCS_TOK_EXPAND:
1655: ntok = rcs_gettok(rfp);
1656: if (ntok == RCS_TOK_SCOLON)
1657: break;
1658: if (ntok != rk->rk_val) {
1659: cvs_log(LP_ERR,
1660: "invalid value type for RCS key `%s'",
1661: rk->rk_str);
1662: }
1663:
1664: if (tok == RCS_TOK_HEAD) {
1.111 joris 1665: if (rfp->rf_head == NULL)
1.28 jfb 1666: rfp->rf_head = rcsnum_alloc();
1.1 jfb 1667: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1668: rfp->rf_head);
1.14 deraadt 1669: } else if (tok == RCS_TOK_BRANCH) {
1.111 joris 1670: if (rfp->rf_branch == NULL)
1.35 jfb 1671: rfp->rf_branch = rcsnum_alloc();
1672: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1673: rfp->rf_branch) < 0)
1.147 ray 1674: goto fail;
1.14 deraadt 1675: } else if (tok == RCS_TOK_COMMENT) {
1.110 joris 1676: rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp));
1.14 deraadt 1677: } else if (tok == RCS_TOK_EXPAND) {
1.110 joris 1678: rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1679: }
1680:
1681: /* now get the expected semi-colon */
1682: ntok = rcs_gettok(rfp);
1683: if (ntok != RCS_TOK_SCOLON) {
1684: cvs_log(LP_ERR,
1685: "missing semi-colon after RCS `%s' key",
1.26 jfb 1686: rk->rk_str);
1.147 ray 1687: goto fail;
1.1 jfb 1688: }
1689: break;
1690: case RCS_TOK_ACCESS:
1.29 jfb 1691: if (rcs_parse_access(rfp) < 0)
1.147 ray 1692: goto fail;
1.1 jfb 1693: break;
1694: case RCS_TOK_SYMBOLS:
1.29 jfb 1695: if (rcs_parse_symbols(rfp) < 0)
1.147 ray 1696: goto fail;
1.1 jfb 1697: break;
1698: case RCS_TOK_LOCKS:
1.29 jfb 1699: if (rcs_parse_locks(rfp) < 0)
1.147 ray 1700: goto fail;
1.1 jfb 1701: break;
1702: default:
1703: cvs_log(LP_ERR,
1704: "unexpected token `%s' in RCS admin section",
1705: RCS_TOKSTR(rfp));
1.147 ray 1706: goto fail;
1.1 jfb 1707: }
1708: }
1709:
1.147 ray 1710: fail:
1711: return (-1);
1.1 jfb 1712: }
1713:
1714: /*
1715: * rcs_parse_delta()
1716: *
1717: * Parse an RCS delta section and allocate the structure to store that delta's
1718: * information in the <rfp> delta list.
1719: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1720: * -1 on error.
1721: */
1722: static int
1723: rcs_parse_delta(RCSFILE *rfp)
1724: {
1725: int ret, tok, ntok, hmask;
1726: u_int i;
1727: char *tokstr;
1.3 vincent 1728: RCSNUM *datenum;
1.1 jfb 1729: struct rcs_delta *rdp;
1730: struct rcs_key *rk;
1731:
1732: tok = rcs_gettok(rfp);
1.154 ray 1733: if (tok == RCS_TOK_DESC) {
1734: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1735: return (0);
1736: } else if (tok != RCS_TOK_NUM) {
1.1 jfb 1737: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1738: RCS_TOKSTR(rfp));
1739: return (-1);
1740: }
1.192 niallo 1741:
1742: rdp = xcalloc(1, sizeof(*rdp));
1743:
1744: rdp->rd_num = rcsnum_alloc();
1745: rdp->rd_next = rcsnum_alloc();
1746:
1747: TAILQ_INIT(&(rdp->rd_branches));
1748:
1.1 jfb 1749: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1750:
1751: hmask = 0;
1752: ret = 0;
1753: tokstr = NULL;
1754:
1755: for (;;) {
1756: tok = rcs_gettok(rfp);
1757: if (tok == RCS_TOK_ERR) {
1758: cvs_log(LP_ERR, "parse error in RCS delta section");
1759: rcs_freedelta(rdp);
1760: return (-1);
1.14 deraadt 1761: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.15 tedu 1762: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.1 jfb 1763: ret = (tok == RCS_TOK_NUM ? 1 : 0);
1764: break;
1765: }
1766:
1767: rk = NULL;
1.18 jfb 1768: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1769: if (rcs_keys[i].rk_id == tok)
1770: rk = &(rcs_keys[i]);
1771:
1772: if (hmask & (1 << tok)) {
1773: cvs_log(LP_ERR, "duplicate RCS key");
1774: rcs_freedelta(rdp);
1775: return (-1);
1776: }
1777: hmask |= (1 << tok);
1778:
1779: switch (tok) {
1780: case RCS_TOK_DATE:
1781: case RCS_TOK_AUTHOR:
1782: case RCS_TOK_STATE:
1783: case RCS_TOK_NEXT:
1784: ntok = rcs_gettok(rfp);
1785: if (ntok == RCS_TOK_SCOLON) {
1786: if (rk->rk_flags & RCS_VOPT)
1787: break;
1788: else {
1789: cvs_log(LP_ERR, "missing mandatory "
1790: "value to RCS key `%s'",
1791: rk->rk_str);
1792: rcs_freedelta(rdp);
1793: return (-1);
1794: }
1795: }
1796:
1797: if (ntok != rk->rk_val) {
1798: cvs_log(LP_ERR,
1799: "invalid value type for RCS key `%s'",
1800: rk->rk_str);
1801: rcs_freedelta(rdp);
1802: return (-1);
1803: }
1804:
1805: if (tokstr != NULL)
1.110 joris 1806: xfree(tokstr);
1807: tokstr = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1808: /* now get the expected semi-colon */
1809: ntok = rcs_gettok(rfp);
1810: if (ntok != RCS_TOK_SCOLON) {
1811: cvs_log(LP_ERR,
1812: "missing semi-colon after RCS `%s' key",
1.26 jfb 1813: rk->rk_str);
1.110 joris 1814: xfree(tokstr);
1.1 jfb 1815: rcs_freedelta(rdp);
1816: return (-1);
1817: }
1818:
1819: if (tok == RCS_TOK_DATE) {
1.25 jfb 1820: if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1.110 joris 1821: xfree(tokstr);
1.11 joris 1822: rcs_freedelta(rdp);
1823: return (-1);
1824: }
1.3 vincent 1825: if (datenum->rn_len != 6) {
1.1 jfb 1826: cvs_log(LP_ERR,
1827: "RCS date specification has %s "
1828: "fields",
1.3 vincent 1829: (datenum->rn_len > 6) ? "too many" :
1.1 jfb 1830: "missing");
1.110 joris 1831: xfree(tokstr);
1.1 jfb 1832: rcs_freedelta(rdp);
1.37 tedu 1833: rcsnum_free(datenum);
1834: return (-1);
1.1 jfb 1835: }
1.3 vincent 1836: rdp->rd_date.tm_year = datenum->rn_id[0];
1.19 jfb 1837: if (rdp->rd_date.tm_year >= 1900)
1838: rdp->rd_date.tm_year -= 1900;
1.3 vincent 1839: rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1840: rdp->rd_date.tm_mday = datenum->rn_id[2];
1841: rdp->rd_date.tm_hour = datenum->rn_id[3];
1842: rdp->rd_date.tm_min = datenum->rn_id[4];
1843: rdp->rd_date.tm_sec = datenum->rn_id[5];
1844: rcsnum_free(datenum);
1.14 deraadt 1845: } else if (tok == RCS_TOK_AUTHOR) {
1.1 jfb 1846: rdp->rd_author = tokstr;
1847: tokstr = NULL;
1.14 deraadt 1848: } else if (tok == RCS_TOK_STATE) {
1.1 jfb 1849: rdp->rd_state = tokstr;
1850: tokstr = NULL;
1.14 deraadt 1851: } else if (tok == RCS_TOK_NEXT) {
1.1 jfb 1852: rcsnum_aton(tokstr, NULL, rdp->rd_next);
1853: }
1854: break;
1855: case RCS_TOK_BRANCHES:
1.46 jfb 1856: if (rcs_parse_branches(rfp, rdp) < 0) {
1857: rcs_freedelta(rdp);
1858: return (-1);
1859: }
1.1 jfb 1860: break;
1861: default:
1.172 joris 1862: cvs_log(LP_ERR, "unexpected token `%s' in RCS delta",
1.1 jfb 1863: RCS_TOKSTR(rfp));
1864: rcs_freedelta(rdp);
1865: return (-1);
1866: }
1867: }
1868:
1.13 jfb 1869: if (tokstr != NULL)
1.110 joris 1870: xfree(tokstr);
1.13 jfb 1871:
1.1 jfb 1872: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1.26 jfb 1873: rfp->rf_ndelta++;
1.1 jfb 1874:
1875: return (ret);
1876: }
1877:
1878: /*
1879: * rcs_parse_deltatext()
1880: *
1881: * Parse an RCS delta text section and fill in the log and text field of the
1882: * appropriate delta section.
1883: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1884: * -1 on error.
1885: */
1886: static int
1887: rcs_parse_deltatext(RCSFILE *rfp)
1888: {
1889: int tok;
1890: RCSNUM *tnum;
1891: struct rcs_delta *rdp;
1892:
1893: tok = rcs_gettok(rfp);
1894: if (tok == RCS_TOK_EOF)
1895: return (0);
1896:
1897: if (tok != RCS_TOK_NUM) {
1898: cvs_log(LP_ERR,
1899: "unexpected token `%s' at start of RCS delta text",
1900: RCS_TOKSTR(rfp));
1901: return (-1);
1902: }
1.13 jfb 1903:
1904: tnum = rcsnum_alloc();
1.1 jfb 1905: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1906:
1907: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1908: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1909: break;
1910: }
1.13 jfb 1911: rcsnum_free(tnum);
1912:
1.1 jfb 1913: if (rdp == NULL) {
1914: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1915: RCS_TOKSTR(rfp));
1916: return (-1);
1917: }
1918:
1919: tok = rcs_gettok(rfp);
1920: if (tok != RCS_TOK_LOG) {
1921: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1922: RCS_TOKSTR(rfp));
1923: return (-1);
1924: }
1925:
1926: tok = rcs_gettok(rfp);
1927: if (tok != RCS_TOK_STRING) {
1928: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1929: RCS_TOKSTR(rfp));
1930: return (-1);
1931: }
1.110 joris 1932: rdp->rd_log = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1933: tok = rcs_gettok(rfp);
1934: if (tok != RCS_TOK_TEXT) {
1935: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1936: RCS_TOKSTR(rfp));
1937: return (-1);
1938: }
1939:
1940: tok = rcs_gettok(rfp);
1941: if (tok != RCS_TOK_STRING) {
1942: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1943: RCS_TOKSTR(rfp));
1944: return (-1);
1945: }
1946:
1.193 niallo 1947: if (RCS_TOKLEN(rfp) == 0) {
1948: rdp->rd_text = xmalloc(1);
1949: rdp->rd_text[0] = '\0';
1950: rdp->rd_tlen = 0;
1951: } else {
1952: rdp->rd_text = xmalloc(RCS_TOKLEN(rfp));
1953: memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp));
1954: rdp->rd_tlen = RCS_TOKLEN(rfp);
1955: }
1.1 jfb 1956:
1957: return (1);
1958: }
1959:
1960: /*
1961: * rcs_parse_access()
1962: *
1963: * Parse the access list given as value to the `access' keyword.
1964: * Returns 0 on success, or -1 on failure.
1965: */
1966: static int
1967: rcs_parse_access(RCSFILE *rfp)
1968: {
1969: int type;
1970:
1971: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
1972: if (type != RCS_TOK_ID) {
1973: cvs_log(LP_ERR, "unexpected token `%s' in access list",
1974: RCS_TOKSTR(rfp));
1975: return (-1);
1976: }
1.29 jfb 1977:
1978: if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
1979: return (-1);
1.1 jfb 1980: }
1981:
1982: return (0);
1983: }
1984:
1985: /*
1986: * rcs_parse_symbols()
1987: *
1988: * Parse the symbol list given as value to the `symbols' keyword.
1989: * Returns 0 on success, or -1 on failure.
1990: */
1991: static int
1992: rcs_parse_symbols(RCSFILE *rfp)
1993: {
1994: int type;
1995: struct rcs_sym *symp;
1996:
1997: for (;;) {
1998: type = rcs_gettok(rfp);
1999: if (type == RCS_TOK_SCOLON)
2000: break;
2001:
1.41 jfb 2002: if (type != RCS_TOK_ID) {
1.1 jfb 2003: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2004: RCS_TOKSTR(rfp));
2005: return (-1);
2006: }
2007:
1.161 ray 2008: symp = xmalloc(sizeof(*symp));
1.110 joris 2009: symp->rs_name = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 2010: symp->rs_num = rcsnum_alloc();
2011:
2012: type = rcs_gettok(rfp);
2013: if (type != RCS_TOK_COLON) {
2014: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2015: RCS_TOKSTR(rfp));
1.11 joris 2016: rcsnum_free(symp->rs_num);
1.110 joris 2017: xfree(symp->rs_name);
2018: xfree(symp);
1.1 jfb 2019: return (-1);
2020: }
2021:
2022: type = rcs_gettok(rfp);
2023: if (type != RCS_TOK_NUM) {
2024: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2025: RCS_TOKSTR(rfp));
1.11 joris 2026: rcsnum_free(symp->rs_num);
1.110 joris 2027: xfree(symp->rs_name);
2028: xfree(symp);
1.1 jfb 2029: return (-1);
2030: }
2031:
2032: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
2033: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2034: RCS_TOKSTR(rfp));
1.11 joris 2035: rcsnum_free(symp->rs_num);
1.110 joris 2036: xfree(symp->rs_name);
2037: xfree(symp);
1.1 jfb 2038: return (-1);
2039: }
2040:
1.43 jfb 2041: TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
1.1 jfb 2042: }
2043:
2044: return (0);
2045: }
2046:
2047: /*
2048: * rcs_parse_locks()
2049: *
2050: * Parse the lock list given as value to the `locks' keyword.
2051: * Returns 0 on success, or -1 on failure.
2052: */
2053: static int
2054: rcs_parse_locks(RCSFILE *rfp)
2055: {
2056: int type;
2057: struct rcs_lock *lkp;
2058:
2059: for (;;) {
2060: type = rcs_gettok(rfp);
2061: if (type == RCS_TOK_SCOLON)
2062: break;
2063:
2064: if (type != RCS_TOK_ID) {
2065: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
2066: RCS_TOKSTR(rfp));
2067: return (-1);
2068: }
2069:
1.161 ray 2070: lkp = xmalloc(sizeof(*lkp));
1.110 joris 2071: lkp->rl_name = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 2072: lkp->rl_num = rcsnum_alloc();
2073:
2074: type = rcs_gettok(rfp);
2075: if (type != RCS_TOK_COLON) {
2076: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2077: RCS_TOKSTR(rfp));
1.37 tedu 2078: rcsnum_free(lkp->rl_num);
1.110 joris 2079: xfree(lkp->rl_name);
2080: xfree(lkp);
1.1 jfb 2081: return (-1);
2082: }
2083:
2084: type = rcs_gettok(rfp);
2085: if (type != RCS_TOK_NUM) {
2086: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2087: RCS_TOKSTR(rfp));
1.37 tedu 2088: rcsnum_free(lkp->rl_num);
1.110 joris 2089: xfree(lkp->rl_name);
2090: xfree(lkp);
1.1 jfb 2091: return (-1);
2092: }
2093:
2094: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
2095: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2096: RCS_TOKSTR(rfp));
1.37 tedu 2097: rcsnum_free(lkp->rl_num);
1.110 joris 2098: xfree(lkp->rl_name);
2099: xfree(lkp);
1.1 jfb 2100: return (-1);
2101: }
2102:
2103: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
2104: }
2105:
2106: /* check if we have a `strict' */
2107: type = rcs_gettok(rfp);
2108: if (type != RCS_TOK_STRICT) {
2109: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1.14 deraadt 2110: } else {
1.26 jfb 2111: rfp->rf_flags |= RCS_SLOCK;
1.1 jfb 2112:
2113: type = rcs_gettok(rfp);
2114: if (type != RCS_TOK_SCOLON) {
2115: cvs_log(LP_ERR,
2116: "missing semi-colon after `strict' keyword");
2117: return (-1);
2118: }
2119: }
2120:
2121: return (0);
2122: }
2123:
2124: /*
2125: * rcs_parse_branches()
2126: *
2127: * Parse the list of branches following a `branches' keyword in a delta.
2128: * Returns 0 on success, or -1 on failure.
2129: */
2130: static int
2131: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
2132: {
2133: int type;
2134: struct rcs_branch *brp;
2135:
2136: for (;;) {
2137: type = rcs_gettok(rfp);
2138: if (type == RCS_TOK_SCOLON)
2139: break;
2140:
2141: if (type != RCS_TOK_NUM) {
2142: cvs_log(LP_ERR,
2143: "unexpected token `%s' in list of branches",
2144: RCS_TOKSTR(rfp));
2145: return (-1);
2146: }
2147:
1.161 ray 2148: brp = xmalloc(sizeof(*brp));
1.46 jfb 2149: brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp));
1.11 joris 2150: if (brp->rb_num == NULL) {
1.110 joris 2151: xfree(brp);
1.11 joris 2152: return (-1);
2153: }
1.1 jfb 2154:
2155: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
2156: }
2157:
2158: return (0);
2159: }
2160:
2161: /*
2162: * rcs_freedelta()
2163: *
2164: * Free the contents of a delta structure.
2165: */
1.18 jfb 2166: static void
1.1 jfb 2167: rcs_freedelta(struct rcs_delta *rdp)
2168: {
1.12 jfb 2169: struct rcs_branch *rb;
1.1 jfb 2170:
1.12 jfb 2171: if (rdp->rd_num != NULL)
2172: rcsnum_free(rdp->rd_num);
2173: if (rdp->rd_next != NULL)
2174: rcsnum_free(rdp->rd_next);
2175:
1.1 jfb 2176: if (rdp->rd_author != NULL)
1.110 joris 2177: xfree(rdp->rd_author);
1.109 joris 2178: if (rdp->rd_locker != NULL)
1.110 joris 2179: xfree(rdp->rd_locker);
1.1 jfb 2180: if (rdp->rd_state != NULL)
1.110 joris 2181: xfree(rdp->rd_state);
1.1 jfb 2182: if (rdp->rd_log != NULL)
1.110 joris 2183: xfree(rdp->rd_log);
1.1 jfb 2184: if (rdp->rd_text != NULL)
1.110 joris 2185: xfree(rdp->rd_text);
1.12 jfb 2186:
2187: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
2188: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
2189: rcsnum_free(rb->rb_num);
1.110 joris 2190: xfree(rb);
1.1 jfb 2191: }
2192:
1.110 joris 2193: xfree(rdp);
1.1 jfb 2194: }
2195:
2196: /*
2197: * rcs_freepdata()
2198: *
2199: * Free the contents of the parser data structure.
2200: */
2201: static void
2202: rcs_freepdata(struct rcs_pdata *pd)
2203: {
2204: if (pd->rp_file != NULL)
2205: (void)fclose(pd->rp_file);
2206: if (pd->rp_buf != NULL)
1.110 joris 2207: xfree(pd->rp_buf);
2208: xfree(pd);
1.1 jfb 2209: }
2210:
2211: /*
2212: * rcs_gettok()
2213: *
2214: * Get the next RCS token from the string <str>.
2215: */
2216: static int
2217: rcs_gettok(RCSFILE *rfp)
2218: {
2219: u_int i;
2220: int ch, last, type;
1.18 jfb 2221: size_t len;
2222: char *bp;
1.1 jfb 2223: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2224:
2225: type = RCS_TOK_ERR;
2226: bp = pdp->rp_buf;
1.42 jfb 2227: pdp->rp_tlen = 0;
1.1 jfb 2228: *bp = '\0';
2229:
2230: if (pdp->rp_pttype != RCS_TOK_ERR) {
2231: type = pdp->rp_pttype;
1.172 joris 2232: if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >=
2233: pdp->rp_blen)
2234: fatal("rcs_gettok: strlcpy");
1.1 jfb 2235: pdp->rp_pttype = RCS_TOK_ERR;
2236: return (type);
2237: }
2238:
2239: /* skip leading whitespace */
2240: /* XXX we must skip backspace too for compatibility, should we? */
2241: do {
2242: ch = getc(pdp->rp_file);
2243: if (ch == '\n')
1.18 jfb 2244: pdp->rp_lines++;
1.1 jfb 2245: } while (isspace(ch));
2246:
2247: if (ch == EOF) {
2248: type = RCS_TOK_EOF;
1.14 deraadt 2249: } else if (ch == ';') {
1.1 jfb 2250: type = RCS_TOK_SCOLON;
1.14 deraadt 2251: } else if (ch == ':') {
1.1 jfb 2252: type = RCS_TOK_COLON;
1.14 deraadt 2253: } else if (isalpha(ch)) {
1.31 jfb 2254: type = RCS_TOK_ID;
1.1 jfb 2255: *(bp++) = ch;
1.18 jfb 2256: for (;;) {
1.1 jfb 2257: ch = getc(pdp->rp_file);
1.188 joris 2258: if (ch == EOF) {
2259: type = RCS_TOK_EOF;
2260: break;
2261: } else if (!isalnum(ch) && ch != '_' && ch != '-' &&
1.201 joris 2262: ch != '/' && ch != '+') {
1.1 jfb 2263: ungetc(ch, pdp->rp_file);
2264: break;
2265: }
2266: *(bp++) = ch;
1.42 jfb 2267: pdp->rp_tlen++;
1.18 jfb 2268: if (bp == pdp->rp_bufend - 1) {
2269: len = bp - pdp->rp_buf;
1.151 xsa 2270: rcs_growbuf(rfp);
1.18 jfb 2271: bp = pdp->rp_buf + len;
2272: }
1.1 jfb 2273: }
2274: *bp = '\0';
2275:
1.18 jfb 2276: if (type != RCS_TOK_ERR) {
2277: for (i = 0; i < RCS_NKEYS; i++) {
2278: if (strcmp(rcs_keys[i].rk_str,
2279: pdp->rp_buf) == 0) {
2280: type = rcs_keys[i].rk_id;
2281: break;
2282: }
1.1 jfb 2283: }
2284: }
1.14 deraadt 2285: } else if (ch == '@') {
1.1 jfb 2286: /* we have a string */
1.18 jfb 2287: type = RCS_TOK_STRING;
1.1 jfb 2288: for (;;) {
2289: ch = getc(pdp->rp_file);
1.188 joris 2290: if (ch == EOF) {
2291: type = RCS_TOK_EOF;
2292: break;
2293: } else if (ch == '@') {
1.1 jfb 2294: ch = getc(pdp->rp_file);
2295: if (ch != '@') {
2296: ungetc(ch, pdp->rp_file);
2297: break;
2298: }
1.14 deraadt 2299: } else if (ch == '\n')
1.18 jfb 2300: pdp->rp_lines++;
1.1 jfb 2301:
2302: *(bp++) = ch;
1.42 jfb 2303: pdp->rp_tlen++;
1.18 jfb 2304: if (bp == pdp->rp_bufend - 1) {
2305: len = bp - pdp->rp_buf;
1.151 xsa 2306: rcs_growbuf(rfp);
1.18 jfb 2307: bp = pdp->rp_buf + len;
2308: }
1.1 jfb 2309: }
2310:
2311: *bp = '\0';
1.14 deraadt 2312: } else if (isdigit(ch)) {
1.1 jfb 2313: *(bp++) = ch;
2314: last = ch;
2315: type = RCS_TOK_NUM;
2316:
2317: for (;;) {
2318: ch = getc(pdp->rp_file);
1.188 joris 2319: if (ch == EOF) {
2320: type = RCS_TOK_EOF;
2321: break;
2322: }
1.18 jfb 2323: if (bp == pdp->rp_bufend)
1.1 jfb 2324: break;
2325: if (!isdigit(ch) && ch != '.') {
2326: ungetc(ch, pdp->rp_file);
2327: break;
2328: }
2329:
2330: if (last == '.' && ch == '.') {
2331: type = RCS_TOK_ERR;
2332: break;
2333: }
2334: last = ch;
2335: *(bp++) = ch;
1.42 jfb 2336: pdp->rp_tlen++;
1.1 jfb 2337: }
1.18 jfb 2338: *bp = '\0';
1.1 jfb 2339: }
2340:
2341: return (type);
2342: }
2343:
2344: /*
2345: * rcs_pushtok()
2346: *
2347: * Push a token back in the parser's token buffer.
2348: */
2349: static int
2350: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
2351: {
2352: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2353:
2354: if (pdp->rp_pttype != RCS_TOK_ERR)
2355: return (-1);
2356:
2357: pdp->rp_pttype = type;
1.172 joris 2358: if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >=
2359: sizeof(pdp->rp_ptok))
2360: fatal("rcs_pushtok: strlcpy");
1.1 jfb 2361: return (0);
2362: }
2363:
1.18 jfb 2364:
2365: /*
2366: * rcs_growbuf()
2367: *
2368: * Attempt to grow the internal parse buffer for the RCS file <rf> by
2369: * RCS_BUFEXTSIZE.
2370: * In case of failure, the original buffer is left unmodified.
2371: */
1.151 xsa 2372: static void
1.18 jfb 2373: rcs_growbuf(RCSFILE *rf)
2374: {
2375: struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
2376:
1.212 ray 2377: pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
2378: pdp->rp_blen + RCS_BUFEXTSIZE);
1.18 jfb 2379: pdp->rp_blen += RCS_BUFEXTSIZE;
2380: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.42 jfb 2381: }
2382:
2383: /*
2384: * rcs_strprint()
2385: *
2386: * Output an RCS string <str> of size <slen> to the stream <stream>. Any
2387: * '@' characters are escaped. Otherwise, the string can contain arbitrary
2388: * binary data.
2389: */
1.155 ray 2390: static void
1.42 jfb 2391: rcs_strprint(const u_char *str, size_t slen, FILE *stream)
2392: {
2393: const u_char *ap, *ep, *sp;
1.52 jfb 2394:
2395: if (slen == 0)
1.155 ray 2396: return;
1.42 jfb 2397:
2398: ep = str + slen - 1;
2399:
2400: for (sp = str; sp <= ep;) {
2401: ap = memchr(sp, '@', ep - sp);
2402: if (ap == NULL)
2403: ap = ep;
1.155 ray 2404: (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1.42 jfb 2405:
2406: if (*ap == '@')
2407: putc('@', stream);
2408: sp = ap + 1;
1.63 joris 2409: }
2410: }
2411:
2412: /*
1.81 niallo 2413: * rcs_deltatext_set()
2414: *
2415: * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1.96 xsa 2416: * Returns -1 on error, 0 on success.
1.81 niallo 2417: */
2418: int
1.194 joris 2419: rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1.81 niallo 2420: {
2421: size_t len;
1.194 joris 2422: u_char *dtext;
1.81 niallo 2423: struct rcs_delta *rdp;
1.117 niallo 2424:
2425: /* Write operations require full parsing */
2426: rcs_parse_deltatexts(rfp, NULL);
1.81 niallo 2427:
2428: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2429: return (-1);
2430:
2431: if (rdp->rd_text != NULL)
1.110 joris 2432: xfree(rdp->rd_text);
1.81 niallo 2433:
1.194 joris 2434: len = cvs_buf_len(bp);
2435: dtext = cvs_buf_release(bp);
2436: bp = NULL;
2437:
1.103 joris 2438: if (len != 0) {
1.194 joris 2439: rdp->rd_text = xmalloc(len);
1.155 ray 2440: rdp->rd_tlen = len;
1.194 joris 2441: (void)memcpy(rdp->rd_text, dtext, len);
1.103 joris 2442: } else {
2443: rdp->rd_text = NULL;
2444: rdp->rd_tlen = 0;
2445: }
1.194 joris 2446:
2447: if (dtext != NULL)
2448: xfree(dtext);
1.18 jfb 2449:
1.86 joris 2450: return (0);
2451: }
2452:
2453: /*
2454: * rcs_rev_setlog()
2455: *
2456: * Sets the log message of revision <rev> to <logtext>
2457: */
2458: int
2459: rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
2460: {
2461: struct rcs_delta *rdp;
1.214 xsa 2462: char buf[CVS_REV_BUFSZ];
1.86 joris 2463:
2464: rcsnum_tostr(rev, buf, sizeof(buf));
2465:
2466: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2467: return (-1);
2468:
2469: if (rdp->rd_log != NULL)
1.110 joris 2470: xfree(rdp->rd_log);
1.86 joris 2471:
1.110 joris 2472: rdp->rd_log = xstrdup(logtext);
1.86 joris 2473: rfp->rf_flags &= ~RCS_SYNCED;
1.95 niallo 2474: return (0);
2475: }
1.97 niallo 2476: /*
2477: * rcs_rev_getdate()
2478: *
2479: * Get the date corresponding to a given revision.
2480: * Returns the date on success, -1 on failure.
2481: */
2482: time_t
2483: rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
2484: {
2485: struct rcs_delta *rdp;
2486:
2487: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2488: return (-1);
2489:
1.204 joris 2490: return (timegm(&rdp->rd_date));
1.97 niallo 2491: }
1.95 niallo 2492:
2493: /*
2494: * rcs_state_set()
2495: *
2496: * Sets the state of revision <rev> to <state>
2497: * NOTE: default state is 'Exp'. States may not contain spaces.
2498: *
2499: * Returns -1 on failure, 0 on success.
2500: */
2501: int
2502: rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
2503: {
2504: struct rcs_delta *rdp;
2505:
2506: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2507: return (-1);
2508:
2509: if (rdp->rd_state != NULL)
1.110 joris 2510: xfree(rdp->rd_state);
1.95 niallo 2511:
1.110 joris 2512: rdp->rd_state = xstrdup(state);
1.95 niallo 2513:
2514: rfp->rf_flags &= ~RCS_SYNCED;
2515:
2516: return (0);
2517: }
2518:
2519: /*
2520: * rcs_state_check()
2521: *
2522: * Check if string <state> is valid.
2523: *
1.96 xsa 2524: * Returns 0 if the string is valid, -1 otherwise.
1.95 niallo 2525: */
2526: int
2527: rcs_state_check(const char *state)
2528: {
2529: if (strchr(state, ' ') != NULL)
2530: return (-1);
2531:
1.18 jfb 2532: return (0);
1.1 jfb 2533: }
1.97 niallo 2534:
2535: /*
2536: * rcs_state_get()
2537: *
2538: * Get the state for a given revision of a specified RCSFILE.
2539: *
2540: * Returns NULL on failure.
2541: */
2542: const char *
2543: rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
2544: {
2545: struct rcs_delta *rdp;
2546:
2547: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2548: return (NULL);
2549:
2550: return (rdp->rd_state);
2551: }
2552:
1.246 tobias 2553: /* rcs_get_revision() */
2554: static RCSNUM *
2555: rcs_get_revision(const char *revstr, RCSFILE *rfp)
1.176 joris 2556: {
1.213 niallo 2557: RCSNUM *rev, *brev, *frev;
1.178 joris 2558: struct rcs_branch *brp;
1.183 joris 2559: struct rcs_delta *rdp;
1.213 niallo 2560: size_t i;
1.183 joris 2561:
2562: rdp = NULL;
1.215 joris 2563:
2564: if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
2565: frev = rcsnum_alloc();
2566: rcsnum_cpy(rfp->rf_head, frev, 0);
2567: return (frev);
2568: }
1.176 joris 2569:
1.213 niallo 2570: /* Possibly we could be passed a version number */
1.223 tobias 2571: if ((rev = rcsnum_parse(revstr)) != NULL) {
2572: /* Do not return if it is not in RCS file */
1.242 joris 2573: if ((rdp = rcs_findrev(rfp, rev)) != NULL) {
2574: frev = rcsnum_alloc();
2575: rcsnum_cpy(rev, frev, 0);
2576: return (frev);
2577: }
1.223 tobias 2578: } else {
2579: /* More likely we will be passed a symbol */
2580: rev = rcs_sym_getrev(rfp, revstr);
2581: }
1.222 joris 2582:
1.213 niallo 2583: if (rev == NULL)
1.245 tobias 2584: return (NULL);
1.183 joris 2585:
1.220 joris 2586: /*
2587: * If it was not a branch, thats ok the symbolic
2588: * name refered to a revision, so return the resolved
1.224 tobias 2589: * revision for the given name if it is not newer than HEAD.
1.220 joris 2590: */
1.224 tobias 2591: if (!RCSNUM_ISBRANCH(rev)) {
2592: if (rcsnum_cmp(rev, rfp->rf_head, 0) < 0) {
2593: rcsnum_free(rev);
2594: return NULL;
2595: }
1.220 joris 2596: return (rev);
1.224 tobias 2597: }
1.183 joris 2598:
1.213 niallo 2599: brev = rcsnum_alloc();
2600: rcsnum_cpy(rev, brev, rev->rn_len - 1);
1.178 joris 2601:
1.180 joris 2602: if ((rdp = rcs_findrev(rfp, brev)) == NULL)
1.246 tobias 2603: fatal("rcs_get_revision: tag `%s' does not exist", revstr);
1.213 niallo 2604: rcsnum_free(brev);
1.178 joris 2605:
1.213 niallo 2606: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2607: for (i = 0; i < rev->rn_len; i++)
2608: if (brp->rb_num->rn_id[i] != rev->rn_id[i])
2609: break;
2610: if (i != rev->rn_len)
2611: continue;
2612: break;
2613: }
1.178 joris 2614:
1.240 tobias 2615: rcsnum_free(rev);
1.213 niallo 2616: frev = rcsnum_alloc();
2617: if (brp == NULL) {
2618: rcsnum_cpy(rdp->rd_num, frev, 0);
2619: return (frev);
2620: } else {
2621: /* Fetch the delta with the correct branch num */
1.180 joris 2622: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.246 tobias 2623: fatal("rcs_get_revision: could not fetch branch "
2624: "delta");
1.213 niallo 2625: /* Find the latest delta on that branch */
2626: for (;;) {
2627: if (rdp->rd_next->rn_len == 0)
2628: break;
1.180 joris 2629: if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
1.246 tobias 2630: fatal("rcs_get_revision: could not fetch "
2631: "branch delta");
1.178 joris 2632: }
1.213 niallo 2633: rcsnum_cpy(rdp->rd_num, frev, 0);
2634: return (frev);
1.176 joris 2635: }
1.196 niallo 2636: }
2637:
2638: /*
2639: * rcs_rev_getlines()
2640: *
1.238 tobias 2641: * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
1.196 niallo 2642: * return it as a pointer to a struct cvs_lines.
2643: */
2644: struct cvs_lines *
1.218 tobias 2645: rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
1.196 niallo 2646: {
1.209 otto 2647: size_t plen;
1.218 tobias 2648: int annotate, done, i, nextroot;
1.196 niallo 2649: RCSNUM *tnum, *bnum;
2650: struct rcs_branch *brp;
1.218 tobias 2651: struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
1.196 niallo 2652: u_char *patch;
1.218 tobias 2653: struct cvs_line *line, *nline;
1.196 niallo 2654: struct cvs_lines *dlines, *plines;
2655:
2656: if ((hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
1.197 joris 2657: fatal("rcs_rev_getlines: no HEAD revision");
1.196 niallo 2658:
2659: tnum = frev;
2660: rcs_parse_deltatexts(rfp, hrdp->rd_num);
2661:
2662: /* revision on branch, get the branch root */
2663: nextroot = 2;
2664: if (RCSNUM_ISBRANCHREV(tnum)) {
2665: bnum = rcsnum_alloc();
2666: rcsnum_cpy(tnum, bnum, nextroot);
2667: } else {
2668: bnum = tnum;
2669: }
2670:
1.218 tobias 2671: if (alines != NULL) {
2672: /* start with annotate first at requested revision */
2673: annotate = ANNOTATE_LATER;
2674: *alines = NULL;
2675: } else
2676: annotate = ANNOTATE_NEVER;
2677:
1.196 niallo 2678: dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
2679:
2680: done = 0;
2681:
2682: rdp = hrdp;
1.218 tobias 2683: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2684: if (annotate == ANNOTATE_LATER) {
2685: /* found requested revision for annotate */
2686: i = 0;
2687: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2688: line->l_lineno_orig = line->l_lineno;
2689: i++;
2690: }
2691:
2692: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2693: (*alines)[i] = NULL;
2694: annotate = ANNOTATE_NOW;
2695:
2696: /* annotate down to 1.1 from where we are */
2697: if (bnum == tnum)
2698: bnum = rcsnum_alloc();
2699: bnum = rcsnum_parse("1.1");
2700: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2701: goto next;
2702: }
2703: } else
2704: goto next;
2705: }
1.196 niallo 2706:
1.218 tobias 2707: prdp = hrdp;
1.196 niallo 2708: if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
2709: goto done;
2710:
2711: again:
2712: for (;;) {
2713: if (rdp->rd_next->rn_len != 0) {
2714: trdp = rcs_findrev(rfp, rdp->rd_next);
2715: if (trdp == NULL)
2716: fatal("failed to grab next revision");
2717: }
2718:
2719: if (rdp->rd_tlen == 0) {
2720: rcs_parse_deltatexts(rfp, rdp->rd_num);
2721: if (rdp->rd_tlen == 0) {
2722: if (!rcsnum_differ(rdp->rd_num, bnum))
2723: break;
2724: rdp = trdp;
2725: continue;
2726: }
2727: }
2728:
2729: plen = rdp->rd_tlen;
2730: patch = rdp->rd_text;
2731: plines = cvs_splitlines(patch, plen);
1.218 tobias 2732: if (annotate == ANNOTATE_NOW)
2733: rcs_patch_lines(dlines, plines, *alines, prdp);
2734: else
2735: rcs_patch_lines(dlines, plines, NULL, NULL);
1.196 niallo 2736: cvs_freelines(plines);
2737:
1.218 tobias 2738: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2739: if (annotate != ANNOTATE_LATER)
2740: break;
2741:
2742: /* found requested revision for annotate */
2743: i = 0;
2744: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2745: line->l_lineno_orig = line->l_lineno;
2746: i++;
2747: }
2748:
2749: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2750: (*alines)[i] = NULL;
2751: annotate = ANNOTATE_NOW;
2752:
2753: /* annotate down to 1.1 from where we are */
2754: if (bnum == tnum)
2755: bnum = rcsnum_alloc();
2756: bnum = rcsnum_parse("1.1");
2757:
2758: if (!rcsnum_differ(rdp->rd_num, bnum))
2759: break;
2760: }
1.196 niallo 2761:
1.218 tobias 2762: prdp = rdp;
1.196 niallo 2763: rdp = trdp;
2764: }
2765:
2766: next:
2767: if (!rcsnum_differ(rdp->rd_num, frev))
2768: done = 1;
2769:
2770: if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
2771: nextroot += 2;
2772: rcsnum_cpy(frev, bnum, nextroot);
2773:
2774: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.213 niallo 2775: for (i = 0; i < nextroot - 1; i++)
2776: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1.196 niallo 2777: break;
1.213 niallo 2778: if (i == nextroot - 1)
2779: break;
1.196 niallo 2780: }
2781:
1.218 tobias 2782: if (brp == NULL) {
2783: if (annotate != ANNOTATE_NEVER) {
2784: if (*alines != NULL)
2785: xfree(*alines);
2786: *alines = NULL;
2787: cvs_freelines(dlines);
2788: if (bnum != tnum)
2789: rcsnum_free(bnum);
2790: return (NULL);
2791: }
1.196 niallo 2792: fatal("expected branch not found on branch list");
1.218 tobias 2793: }
1.196 niallo 2794:
2795: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.197 joris 2796: fatal("rcs_rev_getlines: failed to get delta for target rev");
1.196 niallo 2797:
2798: goto again;
2799: }
2800: done:
1.225 tobias 2801: /* put remaining lines into annotate buffer */
1.218 tobias 2802: if (annotate == ANNOTATE_NOW) {
2803: for (line = TAILQ_FIRST(&(dlines->l_lines));
2804: line != NULL; line = nline) {
2805: nline = TAILQ_NEXT(line, l_list);
2806: TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
2807: if (line->l_line == NULL) {
2808: xfree(line);
2809: continue;
2810: }
2811:
2812: line->l_delta = rdp;
2813: (*alines)[line->l_lineno_orig - 1] = line;
2814: }
2815:
2816: cvs_freelines(dlines);
2817: dlines = NULL;
2818: }
2819:
1.196 niallo 2820: if (bnum != tnum)
2821: rcsnum_free(bnum);
2822:
2823: return (dlines);
1.225 tobias 2824: }
2825:
2826: void
2827: rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
2828: {
2829: size_t plen;
2830: int i, nextroot;
2831: RCSNUM *bnum;
2832: struct rcs_branch *brp;
2833: struct rcs_delta *rdp, *trdp;
2834: u_char *patch;
2835: struct cvs_line *line;
2836: struct cvs_lines *dlines, *plines;
2837:
2838: if (!RCSNUM_ISBRANCHREV(frev))
2839: fatal("rcs_annotate_getlines: branch revision expected");
2840:
2841: /* revision on branch, get the branch root */
2842: nextroot = 2;
2843: bnum = rcsnum_alloc();
2844: rcsnum_cpy(frev, bnum, nextroot);
2845:
2846: /*
2847: * Going from HEAD to 1.1 enables the use of an array, which is
2848: * much faster. Unfortunately this is not possible with branch
2849: * revisions, so copy over our alines (array) into dlines (tailq).
2850: */
2851: dlines = xcalloc(1, sizeof(*dlines));
2852: TAILQ_INIT(&(dlines->l_lines));
2853: line = xcalloc(1, sizeof(*line));
2854: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2855:
2856: for (i = 0; (*alines)[i] != NULL; i++) {
2857: line = (*alines)[i];
2858: line->l_lineno = i + 1;
2859: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2860: }
2861:
2862: rdp = rcs_findrev(rfp, bnum);
2863: if (rdp == NULL)
2864: fatal("failed to grab branch root revision");
2865:
2866: do {
2867: nextroot += 2;
2868: rcsnum_cpy(frev, bnum, nextroot);
2869:
2870: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2871: for (i = 0; i < nextroot - 1; i++)
2872: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
2873: break;
2874: if (i == nextroot - 1)
2875: break;
2876: }
2877:
2878: if (brp == NULL)
2879: fatal("expected branch not found on branch list");
2880:
2881: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2882: fatal("failed to get delta for target rev");
2883:
2884: for (;;) {
2885: if (rdp->rd_next->rn_len != 0) {
2886: trdp = rcs_findrev(rfp, rdp->rd_next);
2887: if (trdp == NULL)
2888: fatal("failed to grab next revision");
2889: }
2890:
2891: if (rdp->rd_tlen == 0) {
2892: rcs_parse_deltatexts(rfp, rdp->rd_num);
2893: if (rdp->rd_tlen == 0) {
2894: if (!rcsnum_differ(rdp->rd_num, bnum))
2895: break;
2896: rdp = trdp;
2897: continue;
2898: }
2899: }
2900:
2901: plen = rdp->rd_tlen;
2902: patch = rdp->rd_text;
2903: plines = cvs_splitlines(patch, plen);
2904: rcs_patch_lines(dlines, plines, NULL, rdp);
2905: cvs_freelines(plines);
2906:
2907: if (!rcsnum_differ(rdp->rd_num, bnum))
2908: break;
2909:
2910: rdp = trdp;
2911: }
2912: } while (rcsnum_differ(rdp->rd_num, frev));
2913:
2914: if (bnum != frev)
2915: rcsnum_free(bnum);
2916:
2917: /*
2918: * All lines have been parsed, now they must be copied over
2919: * into alines (array) again.
2920: */
2921: xfree(*alines);
2922:
2923: i = 0;
2924: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2925: if (line->l_line != NULL)
2926: i++;
2927: }
1.239 tobias 2928: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
1.225 tobias 2929: (*alines)[i] = NULL;
2930:
2931: i = 0;
2932: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2933: if (line->l_line != NULL)
2934: (*alines)[i++] = line;
2935: }
1.196 niallo 2936: }
2937:
2938: /*
2939: * rcs_rev_getbuf()
2940: *
2941: * XXX: This is really really slow and should be avoided if at all possible!
2942: *
2943: * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
2944: * return it as a BUF pointer.
2945: */
2946: BUF *
1.200 joris 2947: rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
1.196 niallo 2948: {
1.200 joris 2949: int expmode, expand;
2950: struct rcs_delta *rdp;
1.196 niallo 2951: struct cvs_lines *lines;
1.233 tobias 2952: struct cvs_line *lp, *nlp;
1.196 niallo 2953: BUF *bp;
2954:
1.200 joris 2955: expand = 0;
1.218 tobias 2956: lines = rcs_rev_getlines(rfp, rev, NULL);
1.249 tobias 2957: bp = cvs_buf_alloc(1024);
1.200 joris 2958:
2959: if (!(mode & RCS_KWEXP_NONE)) {
2960: if (rfp->rf_expand != NULL)
2961: expmode = rcs_kwexp_get(rfp);
2962: else
2963: expmode = RCS_KWEXP_DEFAULT;
2964:
2965: if (!(expmode & RCS_KWEXP_NONE)) {
2966: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2967: fatal("could not fetch revision");
2968: expand = 1;
2969: }
2970: }
2971:
1.255 deraadt 2972: for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
1.233 tobias 2973: nlp = TAILQ_NEXT(lp, l_list);
2974:
2975: if (lp->l_line == NULL) {
2976: lp = nlp;
1.196 niallo 2977: continue;
1.233 tobias 2978: }
1.200 joris 2979:
2980: if (expand)
1.233 tobias 2981: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.200 joris 2982:
1.233 tobias 2983: do {
2984: cvs_buf_append(bp, lp->l_line, lp->l_len);
2985: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 2986: }
2987:
2988: cvs_freelines(lines);
2989:
2990: return (bp);
2991: }
2992:
2993: /*
2994: * rcs_rev_write_fd()
2995: *
2996: * Write the entire contents of revision <frev> from the rcsfile <rfp> to
2997: * file descriptor <fd>.
2998: */
2999: void
3000: rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int fd, int mode)
3001: {
1.200 joris 3002: int expmode, expand;
1.196 niallo 3003: struct rcs_delta *rdp;
3004: struct cvs_lines *lines;
1.233 tobias 3005: struct cvs_line *lp, *nlp;
1.217 joris 3006: extern int print_stdout;
1.196 niallo 3007:
1.200 joris 3008: expand = 0;
1.218 tobias 3009: lines = rcs_rev_getlines(rfp, rev, NULL);
1.200 joris 3010:
1.196 niallo 3011: if (!(mode & RCS_KWEXP_NONE)) {
3012: if (rfp->rf_expand != NULL)
3013: expmode = rcs_kwexp_get(rfp);
3014: else
3015: expmode = RCS_KWEXP_DEFAULT;
3016:
3017: if (!(expmode & RCS_KWEXP_NONE)) {
3018: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3019: fatal("could not fetch revision");
1.200 joris 3020: expand = 1;
1.196 niallo 3021: }
3022: }
1.200 joris 3023:
1.255 deraadt 3024: for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
1.233 tobias 3025: nlp = TAILQ_NEXT(lp, l_list);
3026:
3027: if (lp->l_line == NULL) {
3028: lp = nlp;
1.196 niallo 3029: continue;
1.233 tobias 3030: }
1.200 joris 3031:
3032: if (expand)
1.233 tobias 3033: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.217 joris 3034:
1.233 tobias 3035: do {
3036: /*
3037: * Solely for the checkout and update -p options.
3038: */
3039: if (cvs_server_active == 1 &&
3040: (cvs_cmdop == CVS_OP_CHECKOUT ||
3041: cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
3042: if (atomicio(vwrite, fd, "M ", 2) != 2)
3043: fatal("rcs_rev_write_fd: %s",
3044: strerror(errno));
3045: }
3046:
3047: if (atomicio(vwrite, fd, lp->l_line, lp->l_len) !=
3048: lp->l_len)
1.217 joris 3049: fatal("rcs_rev_write_fd: %s", strerror(errno));
1.233 tobias 3050: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 3051: }
3052:
3053: cvs_freelines(lines);
3054:
3055: }
3056:
3057: /*
3058: * rcs_rev_write_stmp()
3059: *
3060: * Write the contents of the rev <rev> to a temporary file whose path is
3061: * specified using <template> (see mkstemp(3)). NB. This function will modify
3062: * <template>, as per mkstemp.
3063: */
1.251 joris 3064: int
1.196 niallo 3065: rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode)
3066: {
3067: int fd;
3068:
3069: if ((fd = mkstemp(template)) == -1)
3070: fatal("mkstemp: `%s': %s", template, strerror(errno));
3071:
3072: cvs_worklist_add(template, &temp_files);
3073: rcs_rev_write_fd(rfp, rev, fd, mode);
3074:
1.251 joris 3075: if (lseek(fd, SEEK_SET, 0) < 0)
3076: fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
3077:
3078: return (fd);
1.196 niallo 3079: }
3080:
3081: static void
1.233 tobias 3082: rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_lines *lines,
3083: struct cvs_line *line, int mode)
1.196 niallo 3084: {
1.231 tobias 3085: BUF *tmpbuf;
1.196 niallo 3086: int kwtype;
3087: u_int j, found;
1.209 otto 3088: const u_char *c, *start, *fin, *end;
3089: char *kwstr;
1.196 niallo 3090: char expbuf[256], buf[256];
3091: char *fmt;
1.231 tobias 3092: size_t clen, kwlen, len, tlen;
1.196 niallo 3093:
3094: kwtype = 0;
3095: kwstr = NULL;
1.230 tobias 3096:
3097: if (mode & RCS_KWEXP_OLD)
3098: return;
1.196 niallo 3099:
3100: len = line->l_len;
3101: if (len == 0)
3102: return;
3103:
3104: c = line->l_line;
3105: found = 0;
3106: /* Final character in buffer. */
3107: fin = c + len - 1;
3108:
3109: /*
3110: * Keyword formats:
3111: * $Keyword$
3112: * $Keyword: value$
3113: */
3114: for (; c < fin; c++) {
1.231 tobias 3115: if (*c != '$')
3116: continue;
1.196 niallo 3117:
1.231 tobias 3118: /* remember start of this possible keyword */
3119: start = c;
1.196 niallo 3120:
1.231 tobias 3121: /* first following character has to be alphanumeric */
3122: c++;
3123: if (!isalpha(*c)) {
3124: c = start;
3125: continue;
3126: }
1.196 niallo 3127:
1.231 tobias 3128: /* Number of characters between c and fin, inclusive. */
3129: clen = fin - c + 1;
1.196 niallo 3130:
1.231 tobias 3131: /* look for any matching keywords */
3132: found = 0;
3133: for (j = 0; j < RCS_NKWORDS; j++) {
3134: kwlen = strlen(rcs_expkw[j].kw_str);
1.196 niallo 3135: /*
1.231 tobias 3136: * kwlen must be less than clen since clen
3137: * includes either a terminating `$' or a `:'.
1.196 niallo 3138: */
1.231 tobias 3139: if (kwlen < clen &&
3140: memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
3141: (c[kwlen] == '$' || c[kwlen] == ':')) {
3142: found = 1;
3143: kwstr = rcs_expkw[j].kw_str;
3144: kwtype = rcs_expkw[j].kw_type;
3145: c += kwlen;
3146: break;
3147: }
3148: }
1.196 niallo 3149:
1.231 tobias 3150: if (found == 0 && cvs_tagname != NULL) {
3151: kwlen = strlen(cvs_tagname);
3152: if (kwlen < clen &&
3153: memcmp(c, cvs_tagname, kwlen) == 0 &&
3154: (c[kwlen] == '$' || c[kwlen] == ':')) {
3155: found = 1;
3156: kwstr = cvs_tagname;
3157: kwtype = RCS_KW_ID;
3158: c += kwlen;
1.196 niallo 3159: }
1.231 tobias 3160: }
1.196 niallo 3161:
1.231 tobias 3162: /* unknown keyword, continue looking */
3163: if (found == 0) {
3164: c = start;
3165: continue;
3166: }
1.196 niallo 3167:
1.231 tobias 3168: /*
3169: * if the next character was ':' we need to look for
3170: * an '$' before the end of the line to be sure it is
3171: * in fact a keyword.
3172: */
3173: if (*c == ':') {
3174: for (; c <= fin; ++c) {
3175: if (*c == '$' || *c == '\n')
3176: break;
1.196 niallo 3177: }
3178:
1.231 tobias 3179: if (*c != '$') {
3180: c = start;
3181: continue;
3182: }
3183: }
3184: end = c + 1;
1.196 niallo 3185:
1.231 tobias 3186: /* start constructing the expansion */
3187: expbuf[0] = '\0';
1.196 niallo 3188:
1.231 tobias 3189: if (mode & RCS_KWEXP_NAME) {
3190: if (strlcat(expbuf, "$", sizeof(expbuf)) >=
3191: sizeof(expbuf) || strlcat(expbuf, kwstr,
3192: sizeof(expbuf)) >= sizeof(expbuf))
3193: fatal("rcs_kwexp_line: truncated");
3194: if ((mode & RCS_KWEXP_VAL) &&
3195: strlcat(expbuf, ": ", sizeof(expbuf)) >=
3196: sizeof(expbuf))
3197: fatal("rcs_kwexp_line: truncated");
3198: }
1.196 niallo 3199:
1.231 tobias 3200: /*
3201: * order matters because of RCS_KW_ID and
3202: * RCS_KW_HEADER here
3203: */
3204: if (mode & RCS_KWEXP_VAL) {
3205: if (kwtype & RCS_KW_RCSFILE) {
3206: if (!(kwtype & RCS_KW_FULLPATH))
3207: (void)strlcat(expbuf, basename(rcsfile),
3208: sizeof(expbuf));
3209: else
3210: (void)strlcat(expbuf, rcsfile,
3211: sizeof(expbuf));
3212: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3213: sizeof(expbuf))
3214: fatal("rcs_kwexp_line: truncated");
3215: }
1.211 niallo 3216:
1.231 tobias 3217: if (kwtype & RCS_KW_REVISION) {
3218: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3219: if (strlcat(buf, " ", sizeof(buf)) >=
3220: sizeof(buf) || strlcat(expbuf, buf,
3221: sizeof(expbuf)) >= sizeof(buf))
3222: fatal("rcs_kwexp_line: truncated");
3223: }
1.196 niallo 3224:
1.231 tobias 3225: if (kwtype & RCS_KW_DATE) {
3226: fmt = "%Y/%m/%d %H:%M:%S ";
1.196 niallo 3227:
1.231 tobias 3228: if (strftime(buf, sizeof(buf), fmt,
3229: &rdp->rd_date) == 0)
3230: fatal("rcs_kwexp_line: strftime "
3231: "failure");
3232: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3233: sizeof(expbuf))
3234: fatal("rcs_kwexp_line: string "
3235: "truncated");
3236: }
1.196 niallo 3237:
1.231 tobias 3238: if (kwtype & RCS_KW_MDOCDATE) {
3239: /*
3240: * Do not prepend ' ' for a single
3241: * digit, %e would do so and there is
3242: * no better format for strftime().
3243: */
3244: if (rdp->rd_date.tm_mday < 10)
3245: fmt = "%B%e %Y ";
3246: else
3247: fmt = "%B %e %Y ";
3248:
3249: if (strftime(buf, sizeof(buf), fmt,
3250: &rdp->rd_date) == 0)
3251: fatal("rcs_kwexp_line: strftime "
3252: "failure");
3253: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3254: sizeof(expbuf))
3255: fatal("rcs_kwexp_line: string "
3256: "truncated");
3257: }
3258:
3259: if (kwtype & RCS_KW_AUTHOR) {
3260: if (strlcat(expbuf, rdp->rd_author,
3261: sizeof(expbuf)) >= sizeof(expbuf) ||
3262: strlcat(expbuf, " ", sizeof(expbuf)) >=
3263: sizeof(expbuf))
3264: fatal("rcs_kwexp_line: string "
3265: "truncated");
3266: }
3267:
3268: if (kwtype & RCS_KW_STATE) {
3269: if (strlcat(expbuf, rdp->rd_state,
3270: sizeof(expbuf)) >= sizeof(expbuf) ||
3271: strlcat(expbuf, " ", sizeof(expbuf)) >=
3272: sizeof(expbuf))
3273: fatal("rcs_kwexp_line: string "
3274: "truncated");
3275: }
3276:
3277: /* order does not matter anymore below */
1.233 tobias 3278: if (kwtype & RCS_KW_LOG) {
3279: char linebuf[256];
3280: struct cvs_line *cur, *lp;
3281: char *logp, *l_line, *prefix, *q, *sprefix;
3282: size_t i;
3283:
1.236 tobias 3284: /* Log line */
1.233 tobias 3285: if (!(kwtype & RCS_KW_FULLPATH))
3286: (void)strlcat(expbuf,
3287: basename(rcsfile), sizeof(expbuf));
3288: else
3289: (void)strlcat(expbuf, rcsfile,
3290: sizeof(expbuf));
3291:
1.231 tobias 3292: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3293: sizeof(expbuf))
3294: fatal("rcs_kwexp_line: string "
3295: "truncated");
1.233 tobias 3296:
3297: cur = line;
3298:
3299: /* copy rdp->rd_log for strsep */
3300: logp = xstrdup(rdp->rd_log);
3301:
3302: /* copy our prefix for later processing */
3303: prefix = xmalloc(start - line->l_line + 1);
3304: memcpy(prefix, line->l_line,
3305: start - line->l_line);
3306: prefix[start - line->l_line] = '\0';
3307:
3308: /* copy also prefix without trailing blanks. */
3309: sprefix = xstrdup(prefix);
3310: for (i = strlen(sprefix); i > 0 &&
3311: sprefix[i - 1] == ' '; i--)
3312: sprefix[i - 1] = '\0';
3313:
3314: /* new line: revision + date + author */
3315: linebuf[0] = '\0';
3316: if (strlcat(linebuf, "Revision ",
3317: sizeof(linebuf)) >= sizeof(linebuf))
3318: fatal("rcs_kwexp_line: truncated");
3319: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3320: if (strlcat(linebuf, buf, sizeof(linebuf))
3321: >= sizeof(buf))
3322: fatal("rcs_kwexp_line: truncated");
3323: fmt = " %Y/%m/%d %H:%M:%S ";
3324: if (strftime(buf, sizeof(buf), fmt,
3325: &rdp->rd_date) == 0)
3326: fatal("rcs_kwexp_line: strftime "
3327: "failure");
3328: if (strlcat(linebuf, buf, sizeof(linebuf))
3329: >= sizeof(linebuf))
3330: fatal("rcs_kwexp_line: string "
3331: "truncated");
3332: if (strlcat(linebuf, rdp->rd_author,
3333: sizeof(linebuf)) >= sizeof(linebuf))
3334: fatal("rcs_kwexp_line: string "
3335: "truncated");
3336:
3337: lp = xcalloc(1, sizeof(*lp));
3338: xasprintf(&(lp->l_line), "%s%s\n",
3339: prefix, linebuf);
3340: lp->l_len = strlen(lp->l_line);
3341: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3342: l_list);
3343: cur = lp;
3344:
3345: /* Log message */
3346: q = logp;
3347: while ((l_line = strsep(&q, "\n")) != NULL &&
3348: q != NULL) {
3349: lp = xcalloc(1, sizeof(*lp));
3350:
3351: if (l_line[0] == '\0') {
3352: xasprintf(&(lp->l_line), "%s\n",
3353: sprefix);
3354: } else {
3355: xasprintf(&(lp->l_line),
3356: "%s%s\n", prefix, l_line);
3357: }
3358:
3359: lp->l_len = strlen(lp->l_line);
3360: TAILQ_INSERT_AFTER(&(lines->l_lines),
3361: cur, lp, l_list);
3362: cur = lp;
3363: }
3364: xfree(logp);
3365:
3366: /*
3367: * This is just another hairy mess, but it must
1.237 tobias 3368: * be done: All characters behind Log will be
1.233 tobias 3369: * written in a new line next to log messages.
3370: * But that's not enough, we have to strip all
3371: * trailing whitespaces of our prefix.
3372: */
3373: lp = xcalloc(1, sizeof(*lp));
3374: lp->l_line = xcalloc(strlen(sprefix) +
3375: line->l_line + line->l_len - end + 1, 1);
3376: strlcpy(lp->l_line, sprefix,
3377: strlen(sprefix) + 1);
3378: memcpy(lp->l_line + strlen(sprefix),
3379: end, line->l_line + line->l_len - end);
3380: lp->l_len = strlen(lp->l_line);
3381: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3382: l_list);
3383: cur = lp;
3384:
3385: end = line->l_line + line->l_len - 1;
3386:
3387: xfree(prefix);
3388: xfree(sprefix);
3389:
3390: }
1.231 tobias 3391:
3392: if (kwtype & RCS_KW_SOURCE) {
3393: if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
3394: sizeof(expbuf) || strlcat(expbuf, " ",
3395: sizeof(expbuf)) >= sizeof(expbuf))
3396: fatal("rcs_kwexp_line: string "
3397: "truncated");
1.196 niallo 3398: }
3399:
1.231 tobias 3400: if (kwtype & RCS_KW_NAME)
3401: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3402: sizeof(expbuf))
3403: fatal("rcs_kwexp_line: string "
3404: "truncated");
1.196 niallo 3405:
1.231 tobias 3406: if (kwtype & RCS_KW_LOCKER)
3407: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3408: sizeof(expbuf))
3409: fatal("rcs_kwexp_line: string "
3410: "truncated");
1.196 niallo 3411: }
1.231 tobias 3412:
3413: /* end the expansion */
3414: if (mode & RCS_KWEXP_NAME)
3415: if (strlcat(expbuf, "$",
3416: sizeof(expbuf)) >= sizeof(expbuf))
3417: fatal("rcs_kwexp_line: truncated");
3418:
3419: /* Concatenate everything together. */
1.249 tobias 3420: tmpbuf = cvs_buf_alloc(len + strlen(expbuf));
1.231 tobias 3421: /* Append everything before keyword. */
3422: cvs_buf_append(tmpbuf, line->l_line,
3423: start - line->l_line);
3424: /* Append keyword. */
3425: cvs_buf_append(tmpbuf, expbuf, strlen(expbuf));
3426: /* Point c to end of keyword. */
3427: tlen = cvs_buf_len(tmpbuf) - 1;
3428: /* Append everything after keyword. */
3429: cvs_buf_append(tmpbuf, end,
3430: line->l_line + line->l_len - end);
3431: c = cvs_buf_get(tmpbuf) + tlen;
3432: /* Point fin to end of data. */
3433: fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1;
3434: /* Recalculate new length. */
3435: len = cvs_buf_len(tmpbuf);
3436:
3437: /* tmpbuf is now ready, convert to string */
3438: if (line->l_needsfree)
3439: xfree(line->l_line);
3440: line->l_len = len;
3441: line->l_line = cvs_buf_release(tmpbuf);
3442: line->l_needsfree = 1;
1.196 niallo 3443: }
1.131 niallo 3444: }
1.246 tobias 3445:
3446: /* rcs_translate_tag() */
3447: RCSNUM *
3448: rcs_translate_tag(const char *revstr, RCSFILE *rfp)
3449: {
3450: int follow;
1.250 joris 3451: char branch[CVS_REV_BUFSZ];
1.246 tobias 3452: RCSNUM *brev, *frev, *rev;
3453: struct rcs_delta *rdp, *trdp;
3454:
3455: brev = frev = NULL;
3456:
1.250 joris 3457: if (revstr == NULL) {
3458: if (rfp->rf_branch != NULL) {
3459: rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
3460: revstr = branch;
3461: } else {
3462: revstr = RCS_HEAD_BRANCH;
3463: }
3464: }
1.246 tobias 3465:
3466: if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
3467: return NULL;
3468:
3469: /* let's see if we must follow a branch */
3470: if (!strcmp(revstr, RCS_HEAD_BRANCH))
3471: follow = 1;
3472: else {
3473: frev = rcs_sym_getrev(rfp, revstr);
3474:
3475: if (frev != NULL && RCSNUM_ISBRANCH(frev))
3476: follow = 1;
3477: else
3478: follow = 0;
3479: }
3480:
3481: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1.247 joris 3482: fatal("rcs_translate_tag: cannot find revision");
1.246 tobias 3483:
1.248 joris 3484: if (cvs_specified_date == -1)
1.246 tobias 3485: return rev;
3486:
3487: if (frev != NULL) {
3488: brev = rcsnum_revtobr(frev);
3489: brev->rn_len = rev->rn_len;
3490: }
3491:
3492: rcsnum_free(rev);
3493:
3494: do {
3495: if (timelocal(&(rdp->rd_date)) < cvs_specified_date) {
3496: rev = rcsnum_alloc();
3497: rcsnum_cpy(rdp->rd_num, rev, 0);
3498: return rev;
3499: }
3500:
3501: if (follow && rdp->rd_next->rn_len != 0) {
3502: if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
3503: break;
3504:
3505: trdp = rcs_findrev(rfp, rdp->rd_next);
3506: if (trdp == NULL)
3507: fatal("failed to grab next revision");
3508: rdp = trdp;
3509: } else
3510: follow = 0;
3511: } while (follow);
3512:
3513: return NULL;
3514: }
3515: